diff --git a/libs/iojobs/iojob.cpp b/libs/iojobs/iojob.cpp index 9ca2ba7f31..ea6cba33ed 100644 --- a/libs/iojobs/iojob.cpp +++ b/libs/iojobs/iojob.cpp @@ -1,458 +1,477 @@ /* ============================================================ * * 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" #include "dfileoperations.h" namespace Digikam { IOJob::IOJob() { } // -------------------------------------------- CopyJob::CopyJob(IOJobData* const data) { m_data = data; } void CopyJob::run() { while (m_data) { if (m_cancel) { return; } QUrl srcUrl = m_data->getNextUrl(); if (srcUrl.isEmpty()) { break; } QFileInfo srcInfo(srcUrl.toLocalFile()); QDir dstDir(m_data->destUrl().toLocalFile()); if (!srcInfo.exists()) { emit error(i18n("File/Folder %1 does not exist anymore", srcInfo.baseName())); emit signalOneProccessed(m_data->operation()); continue; } if (!dstDir.exists()) { emit error(i18n("Album %1 does not exist anymore", dstDir.dirName())); emit signalOneProccessed(m_data->operation()); continue; } // 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 signalOneProccessed(m_data->operation()); continue; } if (m_data->operation() == IOJobData::MoveAlbum || m_data->operation() == IOJobData::MoveImage || m_data->operation() == IOJobData::MoveFiles) { if (srcInfo.isDir()) { QDir srcDir(srcInfo.filePath()); if (!srcDir.rename(srcDir.path(), destenation)) { // If QDir::rename fails, try copy and remove. if (!DFileOperations::copyFolderRecursively(srcDir.path(), dstDir.path())) { emit error(i18n("Could not move folder %1 to album %2", QDir::toNativeSeparators(srcDir.path()), QDir::toNativeSeparators(dstDir.path()))); emit signalOneProccessed(m_data->operation()); continue; } 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()))); emit signalOneProccessed(m_data->operation()); continue; } } } else { if (srcInfo.isDir()) { QDir srcDir(srcInfo.filePath()); if (!DFileOperations::copyFolderRecursively(srcDir.path(), dstDir.path())) { emit error(i18n("Could not copy folder %1 to album %2", QDir::toNativeSeparators(srcDir.path()), QDir::toNativeSeparators(dstDir.path()))); emit signalOneProccessed(m_data->operation()); continue; } } 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 signalOneProccessed(m_data->operation()); continue; } } } emit signalOneProccessed(m_data->operation()); m_data->addProcessedUrl(srcUrl); } emit signalDone(); } // -------------------------------------------- -DeleteJob::DeleteJob(const QUrl& srcToDelete, bool useTrash, bool markAsObsolete) +DeleteJob::DeleteJob(IOJobData* const data) { - m_srcToDelete = srcToDelete; - m_useTrash = useTrash; - m_markAsObsolete = markAsObsolete; + m_data = data; } void DeleteJob::run() { - if (m_cancel) + while (m_data) { - return; - } + if (m_cancel) + { + return; + } - QFileInfo fileInfo(m_srcToDelete.toLocalFile()); - qCDebug(DIGIKAM_IOJOB_LOG) << "Deleting: " << fileInfo.filePath(); - qCDebug(DIGIKAM_IOJOB_LOG) << "File exists?" << fileInfo.exists(); - qCDebug(DIGIKAM_IOJOB_LOG) << "Is to trash?" << m_useTrash; + QUrl deleteUrl = m_data->getNextUrl(); - if (!fileInfo.exists()) - { - emit error(i18n("File/Folder %1 does not exist", - QDir::toNativeSeparators(fileInfo.filePath()))); - emit signalDone(); - return; - } + if (deleteUrl.isEmpty()) + { + break; + } - if (m_useTrash) - { - if (fileInfo.isDir()) + bool useTrash = (m_data->operation() == IOJobData::Trash); + + QFileInfo fileInfo(deleteUrl.toLocalFile()); + qCDebug(DIGIKAM_IOJOB_LOG) << "Deleting: " << fileInfo.filePath(); + qCDebug(DIGIKAM_IOJOB_LOG) << "File exists?" << fileInfo.exists(); + qCDebug(DIGIKAM_IOJOB_LOG) << "Is to trash?" << useTrash; + + if (!fileInfo.exists()) { - if (!DTrash::deleteDirRecursivley(m_srcToDelete.toLocalFile())) - { - emit error(i18n("Couldn't move folder %1 to collection trash", - QDir::toNativeSeparators(fileInfo.path()))); - } + emit error(i18n("File/Folder %1 does not exist", + QDir::toNativeSeparators(fileInfo.filePath()))); + emit signalOneProccessed(m_data->operation()); + continue; } - else + + if (useTrash) { - if (!DTrash::deleteImage(m_srcToDelete.toLocalFile())) + if (fileInfo.isDir()) { - emit error(i18n("Couldn't move image %1 to collection trash", + if (!DTrash::deleteDirRecursivley(deleteUrl.toLocalFile())) + { + emit error(i18n("Couldn't move folder %1 to collection trash", + QDir::toNativeSeparators(fileInfo.path()))); + emit signalOneProccessed(m_data->operation()); + continue; + } + } + else + { + if (!DTrash::deleteImage(deleteUrl.toLocalFile())) + { + emit error(i18n("Couldn't move image %1 to collection trash", QDir::toNativeSeparators(fileInfo.filePath()))); + emit signalOneProccessed(m_data->operation()); + continue; + } } } - } - else - { - if (fileInfo.isDir()) + else { - QDir dir(fileInfo.filePath()); - - if (!dir.removeRecursively()) - { - emit error(i18n("Album %1 could not be removed", - QDir::toNativeSeparators(fileInfo.path()))); - } - else if (m_markAsObsolete) + if (fileInfo.isDir()) { + QDir dir(fileInfo.filePath()); + + if (!dir.removeRecursively()) + { + emit error(i18n("Album %1 could not be removed", + QDir::toNativeSeparators(fileInfo.path()))); + emit signalOneProccessed(m_data->operation()); + continue; + } + 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) + else { + QFile file(fileInfo.filePath()); + + if (!file.remove()) + { + emit error(i18n("Image %1 could not be removed", + QDir::toNativeSeparators(fileInfo.filePath()))); + emit signalOneProccessed(m_data->operation()); + continue; + } + // 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 signalOneProccessed(m_data->operation()); + m_data->addProcessedUrl(deleteUrl); } emit signalDone(); } qlonglong DeleteJob::getItemFromUrl(const QUrl& url) { 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; // Get the album and with this the image id of the image to trash. 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 signalRenameFailed(m_srcToRename); emit signalDone(); return; } qCDebug(DIGIKAM_IOJOB_LOG) << "Destination Url:" << m_newUrl; 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 signalRenameFailed(m_srcToRename); emit signalDone(); return; } QFile file(m_srcToRename.toLocalFile()); qCDebug(DIGIKAM_IOJOB_LOG) << "Trying to rename" << m_srcToRename.toLocalFile() << "to" << 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 signalRenameFailed(m_srcToRename); emit signalDone(); return; } emit signalRenamed(m_srcToRename); 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) << "Collection trash files path:" << 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(); } // ---------------------------------------------- RestoreDTrashItemsJob::RestoreDTrashItemsJob(const DTrashItemInfoList& infos) { m_dtrashItemInfoList = infos; } void RestoreDTrashItemsJob::run() { foreach (const DTrashItemInfo& item, m_dtrashItemInfoList) { QUrl srcToRename = QUrl::fromLocalFile(item.collectionPath); QUrl newName = DFileOperations::getUniqueFileUrl(srcToRename); QFileInfo fi(item.collectionPath); if (!fi.dir().exists()) { fi.dir().mkpath(fi.dir().path()); } if (!QFile::rename(item.trashPath, newName.toLocalFile())) { qCDebug(DIGIKAM_IOJOB_LOG) << "Trash file couldn't be renamed!"; return; } - - QFile::remove(item.jsonFilePath); + else + { + QFile::remove(item.jsonFilePath); + } } emit signalDone(); } // ---------------------------------------------- DeleteDTrashItemsJob::DeleteDTrashItemsJob(const DTrashItemInfoList& infos) { m_dtrashItemInfoList = infos; } void DeleteDTrashItemsJob::run() { CoreDbAccess access; foreach (const DTrashItemInfo& item, m_dtrashItemInfoList) { QFile::remove(item.trashPath); QFile::remove(item.jsonFilePath); // Set the status of the image id to obsolete, i.e. to remove. access.db()->setItemStatus(item.imageId, DatabaseItem::Status::Obsolete); } emit signalDone(); } } // namespace Digikam diff --git a/libs/iojobs/iojob.h b/libs/iojobs/iojob.h index cf1d1aa467..9fb397cded 100644 --- a/libs/iojobs/iojob.h +++ b/libs/iojobs/iojob.h @@ -1,189 +1,187 @@ /* ============================================================ * * 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 "actionthreadbase.h" #include "dtrashiteminfo.h" #include "digikam_export.h" #include "iojobdata.h" namespace Digikam { class ImageInfo; class DIGIKAM_EXPORT IOJob : public ActionJob { Q_OBJECT protected: IOJob(); Q_SIGNALS: void signalOneProccessed(int operation); void error(const QString& errMsg); }; // --------------------------------------- class DIGIKAM_EXPORT CopyJob : public IOJob { Q_OBJECT public: CopyJob(IOJobData* const data); protected: void run(); private: IOJobData* m_data; }; // --------------------------------------- class DIGIKAM_EXPORT DeleteJob : public IOJob { Q_OBJECT public: - DeleteJob(const QUrl& srcToDelete, bool useTrash, bool markAsObsolete=false); + DeleteJob(IOJobData* const data); protected: void run(); private: qlonglong getItemFromUrl(const QUrl& url); private: - QUrl m_srcToDelete; - bool m_useTrash; - bool m_markAsObsolete; + IOJobData* m_data; }; // --------------------------------------- class DIGIKAM_EXPORT RenameFileJob : public IOJob { Q_OBJECT public: RenameFileJob(const QUrl& srcToRename, const QUrl& newName); Q_SIGNALS: void signalRenamed(const QUrl& oldUrl); void signalRenameFailed(const QUrl& oldUrl); 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); protected: void run(); private: QString m_collectionPath; }; // ---------------------------------------------- class DIGIKAM_EXPORT RestoreDTrashItemsJob : public IOJob { Q_OBJECT public: RestoreDTrashItemsJob(const DTrashItemInfoList& infos); protected: void run(); private: DTrashItemInfoList m_dtrashItemInfoList; }; // ---------------------------------------------- class DIGIKAM_EXPORT DeleteDTrashItemsJob : public IOJob { Q_OBJECT public: DeleteDTrashItemsJob(const DTrashItemInfoList& infos); protected: void run(); private: DTrashItemInfoList m_dtrashItemInfoList; }; } // namespace Digikam #endif // IOJOB_H diff --git a/libs/iojobs/iojobsthread.cpp b/libs/iojobs/iojobsthread.cpp index a493532a1a..1d4014967c 100644 --- a/libs/iojobs/iojobsthread.cpp +++ b/libs/iojobs/iojobsthread.cpp @@ -1,256 +1,254 @@ /* ============================================================ * * 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. * * ============================================================ */ #include "iojobsthread.h" // Qt includes #include #include // Local includes #include "iojob.h" #include "iojobdata.h" #include "digikam_debug.h" #include "coredb.h" #include "coredbaccess.h" namespace Digikam { class IOJobsThread::Private { public: Private() : jobsCount(0), isCanceled(false), jobData(0) { } int jobsCount; bool isCanceled; IOJobData* jobData; QList errorsList; }; IOJobsThread::IOJobsThread(QObject* const parent) : ActionThreadBase(parent), d(new Private) { } IOJobsThread::~IOJobsThread() { delete d->jobData; delete d; } void IOJobsThread::copy(IOJobData* const data) { d->jobData = data; ActionJobCollection collection; for (int i = 0; i < maximumNumberOfThreads(); i++) { CopyJob* const j = new CopyJob(data); connectOneJob(j); collection.insert(j, 0); d->jobsCount++; } appendJobs(collection); } void IOJobsThread::move(IOJobData* const data) { d->jobData = data; ActionJobCollection collection; for (int i = 0; i < maximumNumberOfThreads(); i++) { CopyJob* const j = new CopyJob(data); connectOneJob(j); collection.insert(j, 0); d->jobsCount++; } appendJobs(collection); } void IOJobsThread::deleteFiles(IOJobData* const data) { d->jobData = data; ActionJobCollection collection; - bool useTrash = (data->operation() == IOJobData::Trash); - - foreach (const QUrl& url, data->sourceUrls()) + for (int i = 0; i < maximumNumberOfThreads(); i++) { - DeleteJob* const j = new DeleteJob(url, useTrash, true); + DeleteJob* const j = new DeleteJob(data); connectOneJob(j); collection.insert(j, 0); d->jobsCount++; } appendJobs(collection); } void IOJobsThread::renameFile(IOJobData* const data) { d->jobData = data; ActionJobCollection collection; RenameFileJob* const j = new RenameFileJob(data->srcUrl(), data->destUrl()); connectOneJob(j); connect(j, SIGNAL(signalRenamed(QUrl)), this, SIGNAL(signalRenamed(QUrl))); connect(j, SIGNAL(signalRenameFailed(QUrl)), this, SIGNAL(signalRenameFailed(QUrl))); 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) { ActionJobCollection collection; RestoreDTrashItemsJob* const j = new RestoreDTrashItemsJob(items); connect(j, SIGNAL(signalDone()), this, SIGNAL(finished())); collection.insert(j, 0); d->jobsCount++; appendJobs(collection); } void IOJobsThread::deleteDTrashItems(const DTrashItemInfoList& items) { ActionJobCollection collection; DeleteDTrashItemsJob* const j = new DeleteDTrashItemsJob(items); connect(j, SIGNAL(signalDone()), this, SIGNAL(finished())); collection.insert(j, 0); d->jobsCount++; appendJobs(collection); } bool IOJobsThread::isCanceled() { return d->isCanceled; } bool IOJobsThread::hasErrors() { return !d->errorsList.isEmpty(); } QList& IOJobsThread::errorsList() { return d->errorsList; } void IOJobsThread::connectOneJob(IOJob* const j) { connect(j, SIGNAL(error(QString)), this, SLOT(slotError(QString))); connect(j, SIGNAL(signalDone()), this, SLOT(slotOneJobFinished())); connect(j, SIGNAL(signalOneProccessed(int)), this, SIGNAL(signalOneProccessed(int))); } void IOJobsThread::slotOneJobFinished() { d->jobsCount--; if (d->jobsCount == 0) { emit finished(); qCDebug(DIGIKAM_IOJOB_LOG) << "Thread Finished"; } } void IOJobsThread::slotError(const QString& errString) { d->errorsList.append(errString); } void IOJobsThread::slotCancel() { d->isCanceled = true; ActionThreadBase::cancel(); } IOJobData* IOJobsThread::jobData() { return d->jobData; } } // namespace Digikam