diff --git a/NEWS.6.0.0 b/NEWS.6.0.0 index a4cbd03fc8..8c5a006f18 100644 --- a/NEWS.6.0.0 +++ b/NEWS.6.0.0 @@ -1,67 +1,68 @@ ***************************************************************************************************** digiKam 6.0.0 - Release date: 2018-xx-xx NEW FEATURES: Image Editor : New clone tool to replace old CImg in-painting tool. ImageEditor : Add Web services Import and Export tools. Showfoto : Add Web services Import and Export tools. LightTable : Add Web services Import and Export tools. BUGFIXES FROM BUGZILLA: 001 ==> 172650 - No export tools available. 002 ==> 149591 - The export menu is blank. 003 ==> 300424 - Export tools not detected. 004 ==> 327743 - MediaWiki export not displayed. 005 ==> 348146 - Export tools configuration module. 006 ==> 243275 - Crashing when calling configuration. 007 ==> 257134 - Crashes when entering its settings. 008 ==> 230666 - Crash during start. 009 ==> 306698 - Crashes after update to KDE. 010 ==> 207640 - Crashes immediately at startup, sometimes at closing. 011 ==> 245628 - Crash when enabling/disabling Facebook import/export. 012 ==> 097317 - sigsegv [New Thread 1100241184 (LWP 5665)]. 013 ==> 245776 - Crashes when selecting Settings. 014 ==> 245775 - Crashes even without export tools installed. 015 ==> 254283 - Crash as soon as i click settings. 016 ==> 255733 - crash when reopening configuration dialog. 017 ==> 262050 - Crash after new install and scan to mysql database. 018 ==> 263871 - Crashed after searching for duplicates. 019 ==> 268242 - Crashes after I clicked on 'Settings'. 020 ==> 276882 - Add export tool buttons to toolbar. 021 ==> 284801 - Export to picasaweb crashes. 022 ==> 277669 - Crash after files moved. 023 ==> 281250 - Crash after disabling Export tools. 024 ==> 202637 - Crash trying to start export tool. 025 ==> 306693 - after update to KDE host application crashes on startup. 026 ==> 282781 - Crashes when reopening configuration dialog after disabling export tools. 027 ==> 285499 - Crash when settings window opened. 028 ==> 297629 - Crashed when importing from Nikon P7000. 029 ==> 303338 - Crashes when clicking "send to" button. 030 ==> 307619 - Refuses to load Export tools. 031 ==> 311812 - Export tools not loading, SO version not defined. 032 ==> 313186 - Crashes on attempt to use the "Send to" menu. 033 ==> 313356 - Crashed when clicking the "send to" button. 034 ==> 313577 - Crashes when pressing the "send to" button. 035 ==> 313890 - Crash when clicking "Send to...". 036 ==> 315033 - Crashes on pressing Send To... button. 037 ==> 315914 - The facebook tool crashes everytime on initialization. 038 ==> 326556 - Export tools are not loaded when starting host application for second time. 039 ==> 095175 - crash on loading, signal 11 SIGSEGV. 040 ==> 175844 - Crashes at startup loading Export tools. 041 ==> 306511 - Crash during start. 042 ==> 234021 - Crash on loading. 043 ==> 219772 - Opening the application causes crash. 044 ==> 294173 - Crash after Image resize start. 045 ==> 306881 - Crashed when attempting to open Export tools. 046 ==> 306495 - Crash changing settings linux ubuntu lucid. 047 ==> 306497 - Crash after changing settings segmentation fault possible 2nd report? 048 ==> 185470 - "Import from facebook" is listed twice in import menu. 049 ==> 334045 - MediaWiki option not available in Export menu when plugin activated. 050 ==> 142112 - Can't save on my webspace with ShowFoto. 051 ==> 167417 - Showfoto cannot save files of CIFS mount. 052 ==> 125164 - Flickr export tool should respect host application selection. 053 ==> 238927 - Host application quits when uploading to Flickr. 054 ==> 326740 - Selection of tools is set to default after each update. -055 ==> +055 ==> 233063 - Add progress indicator when moving or copy files [patch]. +056 ==> diff --git a/libs/database/utils/dio.cpp b/libs/database/utils/dio.cpp index 8daf1af93a..37dba7b3ae 100644 --- a/libs/database/utils/dio.cpp +++ b/libs/database/utils/dio.cpp @@ -1,504 +1,600 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2005-05-17 * Description : low level files management interface. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2012-2013 by Marcel Wiesweg * 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 "dio.h" // Qt includes #include // Local includes #include "digikam_debug.h" #include "imageinfo.h" #include "albummanager.h" #include "coredb.h" #include "coredbaccess.h" #include "album.h" #include "dmetadata.h" #include "imagelister.h" #include "loadingcacheinterface.h" #include "metadatasettings.h" #include "scancontroller.h" #include "thumbnailloadthread.h" #include "iojobsmanager.h" #include "collectionmanager.h" #include "dnotificationwrapper.h" +#include "progressmanager.h" #include "digikamapp.h" namespace Digikam { SidecarFinder::SidecarFinder(const QList& files) { process(files); } SidecarFinder::SidecarFinder(const QUrl& file) { process(QList() << file); } void SidecarFinder::process(const QList& files) { foreach(const QUrl& url, files) { if (url.isLocalFile()) { if (DMetadata::hasSidecar(url.toLocalFile())) { localFiles << DMetadata::sidecarUrl(url); localFileSuffixes << QLatin1String(".xmp"); qCDebug(DIGIKAM_DATABASE_LOG) << "Detected a sidecar" << localFiles.last(); } foreach(QString suffix, MetadataSettings::instance()->settings().sidecarExtensions) { suffix = QLatin1String(".") + suffix; QString sidecarName = url.toLocalFile() + suffix; if (QFileInfo::exists(sidecarName) && !localFiles.contains(QUrl::fromLocalFile(sidecarName))) { localFiles << QUrl::fromLocalFile(sidecarName); localFileSuffixes << suffix; qCDebug(DIGIKAM_DATABASE_LOG) << "Detected a sidecar" << localFiles.last(); } } localFiles << url; localFileSuffixes << QString(); } else { possibleRemoteSidecars << DMetadata::sidecarUrl(url); possibleRemoteSidecarSuffixes << QLatin1String(".xmp"); foreach(QString suffix, MetadataSettings::instance()->settings().sidecarExtensions) { suffix = QLatin1String(".") + suffix; QString sidecarName = url.toLocalFile() + suffix; if (!possibleRemoteSidecars.contains(QUrl::fromLocalFile(sidecarName))) { possibleRemoteSidecars << QUrl::fromLocalFile(sidecarName); possibleRemoteSidecarSuffixes << suffix; } } remoteFiles << url; remoteFileSuffixes << QString(); } } } // ------------------------------------------------------------------------------------------------ // TODO // Groups should not be resolved in dio, it should be handled in views. // This is already done for most things except for drag&drop, which is hard :) GroupedImagesFinder::GroupedImagesFinder(const QList& source) { process(source); } void GroupedImagesFinder::process(const QList& source) { QSet ids; foreach(const ImageInfo& info, source) { ids << info.id(); } infos.reserve(source.size()); foreach(const ImageInfo& info, source) { infos << info; if (info.hasGroupedImages()) { foreach(const ImageInfo& groupedImage, info.groupedImages()) { if (ids.contains(groupedImage.id())) { continue; } infos << groupedImage; ids << groupedImage.id(); } } } } // ------------------------------------------------------------------------------------------------ class DIOCreator { public: DIO object; }; Q_GLOBAL_STATIC(DIOCreator, creator) // ------------------------------------------------------------------------------------------------ DIO* DIO::instance() { return &creator->object; } DIO::DIO() { } DIO::~DIO() { } void DIO::cleanUp() { } // Album -> Album ----------------------------------------------------- void DIO::copy(const PAlbum* const src, const PAlbum* const dest) { if (!src || !dest) { return; } instance()->albumToAlbum(Copy, src, dest); } void DIO::move(const PAlbum* src, const PAlbum* const dest) { if (!src || !dest) { return; } #ifdef Q_OS_WIN AlbumManager::instance()->removeWatchedPAlbums(src); #endif instance()->albumToAlbum(Move, src, dest); } // Images -> Album ---------------------------------------------------- void DIO::copy(const QList& infos, const PAlbum* const dest) { if (!dest) { return; } instance()->imagesToAlbum(Copy, infos, dest); } void DIO::move(const QList& infos, const PAlbum* const dest) { if (!dest) { return; } instance()->imagesToAlbum(Move, infos, dest); } // External files -> album -------------------------------------------- void DIO::copy(const QUrl& src, const PAlbum* const dest) { copy(QList() << src, dest); } void DIO::copy(const QList& srcList, const PAlbum* const dest) { if (!dest) { return; } instance()->filesToAlbum(Copy, srcList, dest); } void DIO::move(const QUrl& src, const PAlbum* const dest) { move(QList() << src, dest); } void DIO::move(const QList& srcList, const PAlbum* const dest) { if (!dest) { return; } instance()->filesToAlbum(Move, srcList, dest); } // Rename -------------------------------------------------------------- void DIO::rename(const ImageInfo& info, const QString& newName) { instance()->renameFile(info, newName); } // Delete -------------------------------------------------------------- void DIO::del(const QList& infos, bool useTrash) { instance()->deleteFiles(infos, useTrash); } void DIO::del(const ImageInfo& info, bool useTrash) { del(QList() << info, useTrash); } void DIO::del(const PAlbum* const album, bool useTrash) { if (!album) { return; } #ifdef Q_OS_WIN AlbumManager::instance()->removeWatchedPAlbums(album); #endif instance()->createJob(useTrash ? Trash : Delete, QList() << album->fileUrl(), QUrl()); } // ------------------------------------------------------------------------------------------------ void DIO::imagesToAlbum(int operation, const QList& infos, const PAlbum* const dest) { // this is a fast db operation, do here GroupedImagesFinder finder(infos); QStringList filenames; QList ids; QList urls; if (operation == Move) { // update the image infos CoreDbAccess access; foreach(const ImageInfo& info, finder.infos) { if (!QFileInfo::exists(dest->fileUrl().toLocalFile() + info.name())) { access.db()->moveItem(info.albumId(), info.name(), dest->id(), info.name()); } } } foreach(const ImageInfo& info, finder.infos) { filenames << info.name(); ids << info.id(); urls << info.fileUrl(); } ScanController::instance()->hintAtMoveOrCopyOfItems(ids, dest, filenames); processJob(operation, urls, dest->fileUrl()); } void DIO::albumToAlbum(int operation, const PAlbum* const src, const PAlbum* const dest) { ScanController::instance()->hintAtMoveOrCopyOfAlbum(src, dest); createJob(operation, QList() << src->fileUrl(), dest->fileUrl()); } void DIO::filesToAlbum(int operation, const QList& srcList, const PAlbum* const dest) { processJob(operation, srcList, dest->fileUrl()); } void DIO::renameFile(const ImageInfo& info, const QString& newName) { QUrl oldUrl = info.fileUrl(); QUrl newUrl = oldUrl; newUrl = newUrl.adjusted(QUrl::RemoveFilename); newUrl.setPath(newUrl.path() + newName); PAlbum* const album = AlbumManager::instance()->findPAlbum(info.albumId()); if (album) { ScanController::instance()->hintAtMoveOrCopyOfItem(info.id(), album, newName); } // If we rename a file, the name changes. This is equivalent to a move. // Do this in database, too. CoreDbAccess().db()->moveItem(info.albumId(), oldUrl.fileName(), info.albumId(), newName); processRename(oldUrl, newUrl); } void DIO::deleteFiles(const QList& infos, bool useTrash) { QList urls; foreach(const ImageInfo& info, infos) { urls << info.fileUrl(); } qCDebug(DIGIKAM_DATABASE_LOG) << "Deleting files:" << urls; processJob(useTrash ? Trash : Delete, urls, QUrl()); } void DIO::processJob(int operation, const QList& srcList, const QUrl& dest) { SidecarFinder finder(srcList); createJob(operation, finder.localFiles, dest); if (!finder.remoteFiles.isEmpty()) { createJob(operation, finder.remoteFiles, dest); // stat'ing is unreliable; just try to copy and suppress error message createJob(operation | SourceStatusUnknown, finder.possibleRemoteSidecars, dest); } } void DIO::processRename(const QUrl& src, const QUrl& dest) { SidecarFinder finder(src); if (src.isLocalFile()) { for (int i = 0 ; i < finder.localFiles.length() ; ++i) { createJob(Rename, QList() << finder.localFiles.at(i), QUrl::fromLocalFile(dest.toLocalFile() + finder.localFileSuffixes.at(i))); } return; } for (int i = 0 ; i < finder.remoteFileSuffixes.length() ; ++i) { createJob(Rename | SourceStatusUnknown, QList() << finder.possibleRemoteSidecars.at(i), QUrl::fromLocalFile(dest.toLocalFile() + finder.possibleRemoteSidecarSuffixes.at(i))); } createJob(Rename, QList() << src, dest); } void DIO::createJob(int operation, const QList& src, const QUrl& dest) { if (src.isEmpty()) { return; } + ProgressItem* item = 0; IOJobsThread* jobThread = 0; int flags = operation & FlagMask; operation &= OperationMask; if (operation == Copy) { - jobThread = IOJobsManager::instance()->startCopy(src, dest); + item = getProgressItem(Copy); + + if (!item || item->totalCompleted()) + { + item = ProgressManager::instance()->createProgressItem(QLatin1String("DIOCopy"), + i18n("Copy"), QString(), true, false); + } + + item->setTotalItems(item->totalItems() + src.count()); + jobThread = IOJobsManager::instance()->startCopy(operation, src, dest); } else if (operation == Move) { - jobThread = IOJobsManager::instance()->startMove(src, dest); + item = getProgressItem(Move); + + if (!item || item->totalCompleted()) + { + item = ProgressManager::instance()->createProgressItem(QLatin1String("DIOMove"), + i18n("Move"), QString(), true, false); + } + + item->setTotalItems(item->totalItems() + src.count()); + jobThread = IOJobsManager::instance()->startMove(operation, src, dest); } else if (operation == Rename) { if (src.size() != 1) { qCDebug(DIGIKAM_DATABASE_LOG) << "Invalid operation: renaming is not 1:1"; return; } - jobThread = IOJobsManager::instance()->startRenameFile(src.first(), dest); + jobThread = IOJobsManager::instance()->startRenameFile(operation, src.first(), dest); connect(jobThread, SIGNAL(renamed(QUrl,QUrl)), this, SLOT(slotRenamed(QUrl,QUrl))); connect(jobThread, SIGNAL(renameFailed(QUrl)), this, SIGNAL(imageRenameFailed(QUrl))); } else if (operation == Trash) { - jobThread = IOJobsManager::instance()->startDelete(src); + item = getProgressItem(Trash); + + if (!item || item->totalCompleted()) + { + item = ProgressManager::instance()->createProgressItem(QLatin1String("DIOTrash"), + i18n("Trash"), QString(), true, false); + } + + item->setTotalItems(item->totalItems() + src.count()); + jobThread = IOJobsManager::instance()->startDelete(operation, src); } else // operation == Del { + item = getProgressItem(Delete); + + if (!item || item->totalCompleted()) + { + item = ProgressManager::instance()->createProgressItem(QLatin1String("DIODelete"), + i18n("Delete"), QString(), true, false); + } + qCDebug(DIGIKAM_DATABASE_LOG) << "SRCS " << src; - jobThread = IOJobsManager::instance()->startDelete(src, false); + item->setTotalItems(item->totalItems() + src.count()); + jobThread = IOJobsManager::instance()->startDelete(operation, src, false); } if (flags & SourceStatusUnknown) { jobThread->setKeepErrors(false); } + connect(jobThread, SIGNAL(oneProccessed(int)), + this, SLOT(slotOneProccessed(int))); + connect(jobThread, SIGNAL(finished()), this, SLOT(slotResult())); + + if (item) + { + connect(item, SIGNAL(progressItemCanceled(ProgressItem*)), + jobThread, SLOT(cancel())); + + connect(item, SIGNAL(progressItemCanceled(ProgressItem*)), + this, SLOT(slotCancel(ProgressItem*))); + } } void DIO::slotResult() { IOJobsThread* const jobThread = dynamic_cast(sender()); if (!jobThread) { return; } if (jobThread->hasErrors() && jobThread->isKeepingErrors()) { // Pop-up a message about the error. QString errors = QStringList(jobThread->errorsList()).join(QLatin1String("\n")); DNotificationWrapper(QString(), errors, DigikamApp::instance(), DigikamApp::instance()->windowTitle()); } + + ProgressItem* const item = getProgressItem(jobThread->operation()); + slotCancel(item); +} + +void DIO::slotCancel(ProgressItem* item) +{ + if (item) + { + item->setComplete(); + } +} + +void DIO::slotOneProccessed(int operation) +{ + ProgressItem* const item = getProgressItem(operation); + + if (item) + { + item->advance(1); + } } void DIO::slotRenamed(const QUrl& oldUrl, const QUrl& newUrl) { // delete thumbnail ThumbnailLoadThread::deleteThumbnail(oldUrl.toLocalFile()); // clean LoadingCache as well - be pragmatic, do it here. LoadingCacheInterface::fileChanged(newUrl.toLocalFile()); emit imageRenameSucceeded(oldUrl); } +ProgressItem* DIO::getProgressItem(int operation) +{ + ProgressItem* item = 0; + + switch (operation) + { + case Copy: + item = ProgressManager::instance()->findItembyId(QLatin1String("DIOCopy")); + break; + case Move: + item = ProgressManager::instance()->findItembyId(QLatin1String("DIOMove")); + break; + case Trash: + item = ProgressManager::instance()->findItembyId(QLatin1String("DIOTrash")); + break; + case Delete: + item = ProgressManager::instance()->findItembyId(QLatin1String("DIODelete")); + break; + default: + break; + } + + return item; +} + } // namespace Digikam diff --git a/libs/database/utils/dio.h b/libs/database/utils/dio.h index 5acf61a87d..31b2e0aa72 100644 --- a/libs/database/utils/dio.h +++ b/libs/database/utils/dio.h @@ -1,172 +1,177 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2005-05-17 * Description : low level files management interface. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2012-2013 by Marcel Wiesweg * * 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 _DIGIKAM_IO_H_ #define _DIGIKAM_IO_H_ // Qt includes #include // Local includes #include "digikam_export.h" class QUrl; namespace Digikam { class PAlbum; class ImageInfo; +class ProgressItem; class DIGIKAM_EXPORT DIO : public QObject { Q_OBJECT public: static void cleanUp(); /** * All DIO methods will take care for sidecar files, if they exist */ /// Copy an album to another album static void copy(const PAlbum* const src, const PAlbum* const dest); /// Copy items to another album static void copy(const QList& infos, const PAlbum* const dest); /// Copy an external file to another album static void copy(const QUrl& src, const PAlbum* const dest); /// Copy external files to another album static void copy(const QList& srcList, const PAlbum* const dest); /// Move an album into another album static void move(const PAlbum* src, const PAlbum* const dest); /// Move items to another album static void move(const QList& infos, const PAlbum* const dest); /// Move external files another album static void move(const QUrl& src, const PAlbum* const dest); /// Move external files into another album static void move(const QList& srcList, const PAlbum* const dest); static void del(const QList& infos, bool useTrash); static void del(const ImageInfo& info, bool useTrash); static void del(const PAlbum* const album, bool useTrash); /// Rename item to new name static void rename(const ImageInfo& info, const QString& newName); static DIO* instance(); Q_SIGNALS: void imageRenameSucceeded(const QUrl&); void imageRenameFailed(const QUrl&); private: DIO(); ~DIO(); void imagesToAlbum(int operation, const QList& ids, const PAlbum* const dest); void albumToAlbum(int operation, const PAlbum* const src, const PAlbum* const dest); void filesToAlbum(int operation, const QList& src, const PAlbum* const dest); void renameFile(const ImageInfo& info, const QString& newName); void deleteFiles(const QList& infos, bool useTrash); void processJob(int operation, const QList& src, const QUrl& dest); void processRename(const QUrl& src, const QUrl& dest); void createJob(int operation, const QList& src, const QUrl& dest); + ProgressItem* getProgressItem(int operation); + private Q_SLOTS: void slotResult(); + void slotCancel(ProgressItem* item); + void slotOneProccessed(int operation); void slotRenamed(const QUrl& oldUrl, const QUrl& newUrl); private: friend class DIOCreator; }; // ----------------------------------------------------------------------------------------- class SidecarFinder { public: explicit SidecarFinder(const QList& files); explicit SidecarFinder(const QUrl& file); QList localFiles; QList remoteFiles; QList possibleRemoteSidecars; QList localFileSuffixes; QList remoteFileSuffixes; QList possibleRemoteSidecarSuffixes; private: void process(const QList&); }; // ----------------------------------------------------------------------------------------- class GroupedImagesFinder { public: explicit GroupedImagesFinder(const QList& source); QList infos; private: void process(const QList& source); }; enum Operation { Copy = 1 << 0, Move = 1 << 1, Rename = 1 << 2, Trash = 1 << 3, Delete = 1 << 4, SourceStatusUnknown = 1 << 20, OperationMask = 0xffff, FlagMask = 0xffff0000 }; } // namespace Digikam #endif // _DIGIKAM_IO_H_ diff --git a/libs/iojobs/iojob.cpp b/libs/iojobs/iojob.cpp index f5f1dc2ece..fe8a5dced1 100644 --- a/libs/iojobs/iojob.cpp +++ b/libs/iojobs/iojob.cpp @@ -1,372 +1,382 @@ /* ============================================================ * * 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(const QUrl& src, const QUrl& dest, bool isMove) { m_src = src; m_dest = dest; m_isMove = isMove; } void CopyJob::run() { + if (m_cancel) + { + return; + } + 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 (!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()))); } 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()))); } } } 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()))); } } 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() { + 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; 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(); // 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, 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) << "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(); } } // namespace Digikam diff --git a/libs/iojobs/iojobsmanager.cpp b/libs/iojobs/iojobsmanager.cpp index 4965f2c453..eed7b687b6 100644 --- a/libs/iojobs/iojobsmanager.cpp +++ b/libs/iojobs/iojobsmanager.cpp @@ -1,147 +1,147 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2015-06-15 * Description : Manager for creating and starting IO jobs threads * * 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 "iojobsmanager.h" namespace Digikam { class IOJobsManagerCreator { public: IOJobsManager object; }; Q_GLOBAL_STATIC(IOJobsManagerCreator, creator) // ---------------------------------------------- IOJobsManager::IOJobsManager() { } IOJobsManager* IOJobsManager::instance() { return& creator->object; } -IOJobsThread* IOJobsManager::startCopy(const QList& srcsList, const QUrl& destAlbum) +IOJobsThread* IOJobsManager::startCopy(int operation, const QList& srcsList, const QUrl& destAlbum) { IOJobsThread* const thread = new IOJobsThread(this); - thread->copy(srcsList, destAlbum); + thread->copy(operation, srcsList, destAlbum); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()), Qt::QueuedConnection); thread->start(); return thread; } -IOJobsThread* IOJobsManager::startMove(const QList& srcsList, const QUrl& destAlbum) +IOJobsThread* IOJobsManager::startMove(int operation, const QList& srcsList, const QUrl& destAlbum) { IOJobsThread* const thread = new IOJobsThread(this); - thread->move(srcsList, destAlbum); + thread->move(operation, srcsList, destAlbum); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()), Qt::QueuedConnection); thread->start(); return thread; } -IOJobsThread* IOJobsManager::startDelete(const QList& filesToDelete, bool useTrash) +IOJobsThread* IOJobsManager::startDelete(int operation, const QList& filesToDelete, bool useTrash) { IOJobsThread* const thread = new IOJobsThread(this); - thread->deleteFiles(filesToDelete, useTrash); + thread->deleteFiles(operation, filesToDelete, useTrash); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()), Qt::QueuedConnection); thread->start(); return thread; } -IOJobsThread* IOJobsManager::startRenameFile(const QUrl& srcToRename, const QUrl& newUrl) +IOJobsThread* IOJobsManager::startRenameFile(int operation, const QUrl& srcToRename, const QUrl& newUrl) { IOJobsThread* const thread = new IOJobsThread(this); - thread->renameFile(srcToRename, newUrl); + thread->renameFile(operation, srcToRename, newUrl); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()), Qt::QueuedConnection); thread->start(); return thread; } IOJobsThread *IOJobsManager::startDTrashItemsListingForCollection(const QString& collectionPath) { IOJobsThread* const thread = new IOJobsThread(this); thread->listDTrashItems(collectionPath); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()), Qt::QueuedConnection); thread->start(); return thread; } IOJobsThread* IOJobsManager::startRestoringDTrashItems(const DTrashItemInfoList& trashItemsList) { IOJobsThread* const thread = new IOJobsThread(this); thread->restoreDTrashItems(trashItemsList); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()), Qt::QueuedConnection); thread->start(); return thread; } IOJobsThread* IOJobsManager::startDeletingDTrashItems(const DTrashItemInfoList& trashItemsList) { IOJobsThread* const thread = new IOJobsThread(this); thread->deleteDTrashItems(trashItemsList); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()), Qt::QueuedConnection); thread->start(); return thread; } } // namespace Digikam diff --git a/libs/iojobs/iojobsmanager.h b/libs/iojobs/iojobsmanager.h index 06915510fc..39ffda1150 100644 --- a/libs/iojobs/iojobsmanager.h +++ b/libs/iojobs/iojobsmanager.h @@ -1,113 +1,117 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2015-06-15 * Description : Manager for creating and starting IO jobs threads * * 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 IOJOBSMANAGER_H #define IOJOBSMANAGER_H // Qt includes #include #include // Local includes #include "digikam_export.h" #include "dtrashiteminfo.h" #include "iojobsthread.h" namespace Digikam { class DIGIKAM_EXPORT IOJobsManager : public QObject { public: /** * @brief instance: returns the singleton of IO Jobs Manager * @return IOJobsManager global instance */ static IOJobsManager* instance(); /** * @brief startCopy: Starts a thread to copy items to destination + * @param operation: file operation mode * @param srcsList: Sources urls to copy * @param destAlbum: Destenation url of album folder * @return IOJobsThread pointer for signal/slot connection */ - IOJobsThread* startCopy(const QList& srcsList, const QUrl& destAlbum); + IOJobsThread* startCopy(int operation, const QList& srcsList, const QUrl& destAlbum); /** * @brief startMove: Starts a thread to move items to destination + * @param operation: file operation mode * @param srcsList: Sources urls to move * @param destAlbum: Destenation url of album folder * @return IOJobsThread pointer for signal/slot connection */ - IOJobsThread* startMove(const QList& srcsList, const QUrl& destAlbum); + IOJobsThread* startMove(int operation, const QList& srcsList, const QUrl& destAlbum); /** * @brief startDelete: Starts a thread to delete items + * @param operation: file operation mode * @param filesToDelete: Sources urls to delete * @param useTrash: a flag to specify if the deletion is to trash or permanent * @return IOJobsThread pointer for signal/slot connection */ - IOJobsThread* startDelete(const QList& filesToDelete, bool useTrash = true); + IOJobsThread* startDelete(int operation, const QList& filesToDelete, bool useTrash = true); /** * @brief startRenameFile: Starts a thread to rename a single file + * @param operation: file operation mode * @param srcToRename: The url of the old file name * @param newUrl: The url that represents the new file * @return IOJobsThread pointer for signal/slot connection */ - IOJobsThread* startRenameFile(const QUrl& srcToRename, const QUrl& newUrl); + IOJobsThread* startRenameFile(int operation, const QUrl& srcToRename, const QUrl& newUrl); /** * @brief Starts a thread for listing items inside trash for specific collection * @param collectionPath: the path for collection to list items for it's trash * @return IOJobsThread pointer for signal/slot connection */ IOJobsThread* startDTrashItemsListingForCollection(const QString& collectionPath); /** * @brief Starts a thread to restore mutiple trash items * @param trashItemsList: list of selected trash items to restore * @return IOJobsThread pointer for signal/slot connection */ IOJobsThread* startRestoringDTrashItems(const DTrashItemInfoList& trashItemsList); /** * @brief Starts a thread to delete mutiple trash items * @param trashItemsList: list of selected trash items to delete * @return IOJobsThread pointer for signal/slot connection */ IOJobsThread* startDeletingDTrashItems(const DTrashItemInfoList& trashItemsList); private: friend class IOJobsManagerCreator; IOJobsManager(); }; } // namespace Digikam #endif // FILESYSTEMJOBSMANAGER_H diff --git a/libs/iojobs/iojobsthread.cpp b/libs/iojobs/iojobsthread.cpp index 5014ebc7da..08c628ac27 100644 --- a/libs/iojobs/iojobsthread.cpp +++ b/libs/iojobs/iojobsthread.cpp @@ -1,281 +1,295 @@ /* ============================================================ * * 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 "coredb.h" #include "coredbaccess.h" namespace Digikam { class IOJobsThread::Private { public: Private() : jobsCount(0), + operation(0), isCanceled(false), keepErrors(true) { } int jobsCount; + int operation; bool isCanceled; 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) +void IOJobsThread::copy(int operation, const QList& srcFiles, const QUrl destAlbum) { + d->operation = operation; 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) +void IOJobsThread::move(int operation, const QList& srcFiles, const QUrl destAlbum) { + d->operation = operation; 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::deleteFiles(const QList& srcsToDelete, bool useTrash) +void IOJobsThread::deleteFiles(int operation, const QList& srcsToDelete, bool useTrash) { + d->operation = operation; 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); + renameFile(0, srcToRename, newName); listOfJsonFilesToRemove << QUrl::fromLocalFile(item.jsonFilePath); } - deleteFiles(listOfJsonFilesToRemove, false); + deleteFiles(0, 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); } - deleteFiles(urlsToDelete, false); + deleteFiles(0, urlsToDelete, false); } -void IOJobsThread::renameFile(const QUrl& srcToRename, const QUrl& newName) +void IOJobsThread::renameFile(int operation, const QUrl& srcToRename, const QUrl& newName) { + d->operation = operation; ActionJobCollection collection; + RenameFileJob* const j = new RenameFileJob(srcToRename, newName); connectOneJob(j); connect(j, SIGNAL(signalRenamed(QUrl,QUrl)), this, SIGNAL(renamed(QUrl,QUrl))); connect(j, SIGNAL(signalRenameFailed(QUrl)), this, SIGNAL(renameFailed(QUrl))); collection.insert(j, 0); d->jobsCount++; appendJobs(collection); } 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(); 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--; + emit oneProccessed(d->operation); + if (d->jobsCount == 0) { emit finished(); qCDebug(DIGIKAM_IOJOB_LOG) << "Thread Finished"; } } void IOJobsThread::error(const QString& errString) { d->errorsList.append(errString); } +int IOJobsThread::operation() +{ + return d->operation; +} + } // namespace Digikam diff --git a/libs/iojobs/iojobsthread.h b/libs/iojobs/iojobsthread.h index d7dd979e9b..7338449715 100644 --- a/libs/iojobs/iojobsthread.h +++ b/libs/iojobs/iojobsthread.h @@ -1,177 +1,188 @@ /* ============================================================ * * 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. * * ============================================================ */ #ifndef IOJOBSTHREAD_H #define IOJOBSTHREAD_H // Local includes #include "actionthreadbase.h" #include "digikam_export.h" #include "dtrashiteminfo.h" namespace Digikam { class IOJob; class DIGIKAM_EXPORT IOJobsThread : public ActionThreadBase { Q_OBJECT public: IOJobsThread(QObject* const parent); ~IOJobsThread(); /** * @brief Starts a number of jobs to copy source files to destination + * @param operation: file operation mode * @param srcFiles: files to be copied * @param destAlbum: destination folder */ - void copy(const QList& srcFiles, const QUrl destAlbum); + void copy(int operation, const QList& srcFiles, const QUrl destAlbum); /** * @brief Starts a number of jobs to move source files to destination + * @param operation: file operation mode * @param srcFiles: files to be moved * @param destAlbum: destination folder */ - void move(const QList& srcFiles, const QUrl destAlbum); + void move(int operation, const QList& srcFiles, const QUrl destAlbum); /** * @brief Starts a job for listing trash items in a collection * @param collectionPath */ void listDTrashItems(const QString& collectionPath); /** * @brief creates a job for every item to restore back to album * @param items to restore */ void restoreDTrashItems(const DTrashItemInfoList& items); /** * @brief creates a job for every item to delete from collection trash * @param items to delete */ void deleteDTrashItems(const DTrashItemInfoList& items); /** * @brief Starts a number of jobs to delete multiple files + * @param operation: file operation mode * @param srcsToDelete: files to be deleted * @param useTrash: a flag to use trash or not */ - void deleteFiles(const QList& srcsToDelete, bool useTrash); + void deleteFiles(int operation, const QList& srcsToDelete, bool useTrash); /** * @brief Starts one job to rename a file to a new name + * @param operation: file operation mode * @param srcToRename: the url to be renamed * @param newName: the url of the renamed item */ - void renameFile(const QUrl& srcToRename, const QUrl& newName); - - /** - * @brief cancels thread execution - */ - void cancel(); + void renameFile(int operation, const QUrl& srcToRename, const QUrl& newName); /** * @brief isCanceled * @return true if the thread was inturrupted */ bool isCanceled(); /** * @brief hasErrors * @return true if string list was not empty */ bool hasErrors(); /** * @brief a setter to make the thread keep errors reported * by the job */ void setKeepErrors(bool keepErrors); /** * @brief isKeepingErrors * @return true if the thread keeps error */ bool isKeepingErrors(); /** * @brief errorsList * @return */ QList& errorsList(); + /** + * @brief operation + * @return + */ + int operation(); + public Q_SLOTS: /** * @brief connected to all active jobs and checks if the job * list has finished to report that thread is finished */ void oneJobFinished(); /** * @brief A slot to receive the error from the job * @param errString: string to be appended */ + void error(const QString& errString); + /** + * @brief cancels thread execution + */ + void cancel(); Q_SIGNALS: void finished(); void renamed(const QUrl& oldUrl, const QUrl& newURl); void renameFailed(const QUrl& oldUrl); + void oneProccessed(int operation); void collectionTrashItemInfo(const DTrashItemInfo& trashItemInfo); private: /** * @brief connects the job with signals/slots * @param job to be connected */ void connectOneJob(IOJob* const j); /** * @brief Recursive method to find the suitable to restore items from trash * @param colPath: Path of item in collection before deleting to trash * @param usedUrls: a list of all used urls to rename previous files, * to prevent duplication * @param version: to add to the base name in case the name was taken * @return QUrl to use in the renameFile() method */ QUrl getAvailableQUrlToRestoreInCollection(const QString& fileColPath, QList& usedUrls, int version = 0); private: class Private; Private* const d; }; } // namespace Digikam #endif // IOJOBSTHREAD_H