diff --git a/libs/database/utils/dio.cpp b/libs/database/utils/dio.cpp index a9a8a2d7db..10dc4810c6 100644 --- a/libs/database/utils/dio.cpp +++ b/libs/database/utils/dio.cpp @@ -1,600 +1,599 @@ /* ============================================================ * * 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) + 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) { 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) { 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(operation, src.first(), dest); connect(jobThread, SIGNAL(signalRenamed(QUrl,QUrl)), this, SLOT(slotRenamed(QUrl,QUrl))); connect(jobThread, SIGNAL(signalRenameFailed(QUrl)), this, SIGNAL(imageRenameFailed(QUrl))); } else if (operation == Trash) { 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; item->setTotalItems(item->totalItems() + src.count()); jobThread = IOJobsManager::instance()->startDelete(operation, src, false); } if (flags & SourceStatusUnknown) { jobThread->setKeepErrors(false); } connect(jobThread, SIGNAL(signalOneProccessed(int)), this, SLOT(slotOneProccessed(int))); connect(jobThread, SIGNAL(finished()), this, SLOT(slotResult())); if (item) { connect(item, SIGNAL(progressItemCanceled(ProgressItem*)), jobThread, SLOT(slotCancel())); 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 31b2e0aa72..08c4f9755e 100644 --- a/libs/database/utils/dio.h +++ b/libs/database/utils/dio.h @@ -1,177 +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: +enum Operation +{ + Copy = 1 << 0, + Move = 1 << 1, + Rename = 1 << 2, + Trash = 1 << 3, + Delete = 1 << 4, + SourceStatusUnknown = 1 << 20, + + OperationMask = 0xffff, + FlagMask = 0xffff0000 +}; + 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/iojobsthread.cpp b/libs/iojobs/iojobsthread.cpp index 5710bb2a4d..d40afa4fc8 100644 --- a/libs/iojobs/iojobsthread.cpp +++ b/libs/iojobs/iojobsthread.cpp @@ -1,295 +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(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(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(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); + 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(0, srcToRename, newName); listOfJsonFilesToRemove << QUrl::fromLocalFile(item.jsonFilePath); } 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(0, urlsToDelete, false); } 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(signalRenamed(QUrl,QUrl))); connect(j, SIGNAL(signalRenameFailed(QUrl)), this, SIGNAL(signalRenameFailed(QUrl))); collection.insert(j, 0); d->jobsCount++; appendJobs(collection); } 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(slotError(QString))); connect(j, SIGNAL(signalDone()), this, SLOT(slotOneJobFinished())); } 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::slotOneJobFinished() { d->jobsCount--; emit signalOneProccessed(d->operation); 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(); } int IOJobsThread::operation() { return d->operation; } } // namespace Digikam