diff --git a/libs/database/utils/dio.cpp b/libs/database/utils/dio.cpp index d49adf4696..245bf3a491 100644 --- a/libs/database/utils/dio.cpp +++ b/libs/database/utils/dio.cpp @@ -1,557 +1,547 @@ /* ============================================================ * * 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 "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" #include "iojobdata.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 (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(); } } // ------------------------------------------------------------------------------------------------ // 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(PAlbum* const src, PAlbum* const dest) { if (!src || !dest) { return; } instance()->processJob(new IOJobData(IOJobData::CopyAlbum, src, dest)); } void DIO::move(PAlbum* const src, PAlbum* const dest) { if (!src || !dest) { return; } #ifdef Q_OS_WIN AlbumManager::instance()->removeWatchedPAlbums(src); #endif instance()->processJob(new IOJobData(IOJobData::MoveAlbum, src, dest)); } // Images -> Album ---------------------------------------------------- void DIO::copy(const QList& infos, PAlbum* const dest) { if (!dest) { return; } instance()->processJob(new IOJobData(IOJobData::CopyImage, infos, dest)); } void DIO::move(const QList& infos, PAlbum* const dest) { if (!dest) { return; } instance()->processJob(new IOJobData(IOJobData::MoveImage, infos, dest)); } // External files -> album -------------------------------------------- void DIO::copy(const QUrl& src, PAlbum* const dest) { copy(QList() << src, dest); } void DIO::copy(const QList& srcList, PAlbum* const dest) { if (!dest) { return; } instance()->processJob(new IOJobData(IOJobData::CopyFiles, srcList, dest)); } void DIO::move(const QUrl& src, PAlbum* const dest) { move(QList() << src, dest); } void DIO::move(const QList& srcList, PAlbum* const dest) { if (!dest) { return; } instance()->processJob(new IOJobData(IOJobData::MoveFiles, srcList, dest)); } // Rename -------------------------------------------------------------- void DIO::rename(const ImageInfo& info, const QString& newName) { - instance()->renameFile(new IOJobData(IOJobData::Rename, info, newName)); + instance()->processJob(new IOJobData(IOJobData::Rename, info, newName)); } // Delete -------------------------------------------------------------- void DIO::del(const QList& infos, bool useTrash) { instance()->processJob(new IOJobData(useTrash ? IOJobData::Trash : IOJobData::Delete, infos)); } void DIO::del(const ImageInfo& info, bool useTrash) { del(QList() << info, useTrash); } void DIO::del(PAlbum* const album, bool useTrash) { if (!album) { return; } #ifdef Q_OS_WIN AlbumManager::instance()->removeWatchedPAlbums(album); #endif instance()->createJob(new IOJobData(useTrash ? IOJobData::Trash : IOJobData::Delete, QList() << album->fileUrl())); } // ------------------------------------------------------------------------------------------------ -void DIO::renameFile(IOJobData* const data) -{ - QUrl newUrl = data->srcUrl(); - newUrl = newUrl.adjusted(QUrl::RemoveFilename); - newUrl.setPath(newUrl.path() + data->newName()); - data->setDestUrl(newUrl); - - PAlbum* const album = AlbumManager::instance()->findPAlbum(data->imageInfo().albumId()); - - if (album) - { - ScanController::instance()->hintAtMoveOrCopyOfItem(data->imageInfo().id(), - album, data->newName()); - } - - // If we rename a file, the name changes. This is equivalent to a move. - // Do this in database, too. - CoreDbAccess().db()->moveItem(data->imageInfo().albumId(), - data->srcUrl().fileName(), - data->imageInfo().albumId(), - data->newName()); - - processRename(data); -} - -void DIO::processRename(IOJobData* const data) -{ - SidecarFinder finder(data->srcUrl()); - - for (int i = 0 ; i < finder.localFiles.length() ; ++i) - { - createJob(new IOJobData(IOJobData::Rename, QList() << finder.localFiles.at(i), - QUrl::fromLocalFile(data->destUrl().toLocalFile() + - finder.localFileSuffixes.at(i)))); - } - - delete data; -} - void DIO::processJob(IOJobData* const data) { int operation = data->operation(); if (operation == IOJobData::CopyImage || operation == IOJobData::MoveImage) { // this is a fast db operation, do here GroupedImagesFinder finder(data->imageInfos()); data->setImageInfos(finder.infos); QStringList filenames; QList ids; foreach(const ImageInfo& info, data->imageInfos()) { filenames << info.name(); ids << info.id(); } ScanController::instance()->hintAtMoveOrCopyOfItems(ids, data->destAlbum(), filenames); } else if (operation == IOJobData::CopyAlbum || operation == IOJobData::MoveAlbum) { ScanController::instance()->hintAtMoveOrCopyOfAlbum(data->srcAlbum(), data->destAlbum()); createJob(data); return; } else if (operation == IOJobData::Delete || operation == IOJobData::Trash) { qCDebug(DIGIKAM_DATABASE_LOG) << "Deleting files:" << data->sourceUrls(); } + else if (operation == IOJobData::Rename) + { + PAlbum* const album = AlbumManager::instance()->findPAlbum(data->imageInfo().albumId()); + + if (album) + { + ScanController::instance()->hintAtMoveOrCopyOfItem(data->imageInfo().id(), + album, data->destUrl().fileName()); + } + + SidecarFinder finder(data->srcUrl()); + + for (int i = 0 ; i < finder.localFiles.length() ; ++i) + { + IOJobData* const newdata = new IOJobData(IOJobData::Rename, data->imageInfos()); + newdata->setSourceUrls(QList() << finder.localFiles.at(i)); + newdata->setDestUrl(QUrl::fromLocalFile(data->destUrl().toLocalFile() + + finder.localFileSuffixes.at(i))); + createJob(newdata); + } + + delete data; + return; + } SidecarFinder finder(data->sourceUrls()); - data->addSourceUrls(finder.localFiles); + data->setSourceUrls(finder.localFiles); createJob(data); } void DIO::createJob(IOJobData* const data) { if (data->sourceUrls().isEmpty()) { delete data; return; } ProgressItem* item = 0; IOJobsThread* jobThread = 0; int operation = data->operation(); if (operation == IOJobData::CopyAlbum || operation == IOJobData::CopyImage || operation == IOJobData::CopyFiles) { item = getProgressItem(operation); if (!item || item->totalCompleted()) { item = ProgressManager::instance()->createProgressItem(QLatin1String("DIOCopy"), i18n("Copy"), QString(), true, false); } item->setTotalItems(item->totalItems() + data->sourceUrls().count()); jobThread = IOJobsManager::instance()->startCopy(data); } else if (operation == IOJobData::MoveAlbum || operation == IOJobData::MoveImage || operation == IOJobData::MoveFiles) { item = getProgressItem(operation); if (!item || item->totalCompleted()) { item = ProgressManager::instance()->createProgressItem(QLatin1String("DIOMove"), i18n("Move"), QString(), true, false); } item->setTotalItems(item->totalItems() + data->sourceUrls().count()); jobThread = IOJobsManager::instance()->startMove(data); } else if (operation == IOJobData::Rename) { if (data->sourceUrls().count() != 1) { qCDebug(DIGIKAM_DATABASE_LOG) << "Invalid operation: renaming is not 1:1"; return; } jobThread = IOJobsManager::instance()->startRenameFile(data); - connect(jobThread, SIGNAL(signalRenamed(QUrl,QUrl)), - this, SLOT(slotRenamed(QUrl,QUrl))); + connect(jobThread, SIGNAL(signalRenamed(QUrl)), + this, SIGNAL(signalRenameSucceeded(QUrl))); connect(jobThread, SIGNAL(signalRenameFailed(QUrl)), this, SIGNAL(signalRenameFailed(QUrl))); } else if (operation == IOJobData::Trash) { item = getProgressItem(operation); if (!item || item->totalCompleted()) { item = ProgressManager::instance()->createProgressItem(QLatin1String("DIOTrash"), i18n("Trash"), QString(), true, false); } item->setTotalItems(item->totalItems() + data->sourceUrls().count()); jobThread = IOJobsManager::instance()->startDelete(data); } else if (operation == IOJobData::Delete) { item = getProgressItem(operation); if (!item || item->totalCompleted()) { item = ProgressManager::instance()->createProgressItem(QLatin1String("DIODelete"), i18n("Delete"), QString(), true, false); } - qCDebug(DIGIKAM_DATABASE_LOG) << "SRCS " << data->sourceUrls(); item->setTotalItems(item->totalItems() + data->sourceUrls().count()); jobThread = IOJobsManager::instance()->startDelete(data); } 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 || !jobThread->jobData()) { return; } IOJobData* const data = jobThread->jobData(); int operation = data->operation(); if (operation == IOJobData::MoveImage) { // update the image infos CoreDbAccess access; foreach(const ImageInfo& info, data->imageInfos()) { if (QFileInfo::exists(data->destUrl().toLocalFile() + info.name())) { access.db()->moveItem(info.albumId(), info.name(), data->destAlbum()->id(), info.name()); } } } + else if (operation == IOJobData::Rename) + { + // If we rename a file, the name changes. This is equivalent to a move. + // Do this in database, too. + if (QFileInfo::exists(data->destUrl().toLocalFile())) + { + CoreDbAccess().db()->moveItem(data->imageInfo().albumId(), + data->srcUrl().fileName(), + data->imageInfo().albumId(), + data->destUrl().fileName()); + + // delete thumbnail + ThumbnailLoadThread::deleteThumbnail(data->srcUrl().toLocalFile()); + LoadingCacheInterface::fileChanged(data->destUrl().toLocalFile()); + } + } if (jobThread->hasErrors()) { // 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(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 signalRenameSucceeded(oldUrl); -} - ProgressItem* DIO::getProgressItem(int operation) { ProgressItem* item = 0; switch (operation) { case IOJobData::CopyAlbum: case IOJobData::CopyImage: case IOJobData::CopyFiles: item = ProgressManager::instance()->findItembyId(QLatin1String("DIOCopy")); break; case IOJobData::MoveAlbum: case IOJobData::MoveImage: case IOJobData::MoveFiles: item = ProgressManager::instance()->findItembyId(QLatin1String("DIOMove")); break; case IOJobData::Trash: item = ProgressManager::instance()->findItembyId(QLatin1String("DIOTrash")); break; case IOJobData::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 004e2d04c8..4c0f978d0a 100644 --- a/libs/database/utils/dio.h +++ b/libs/database/utils/dio.h @@ -1,156 +1,152 @@ /* ============================================================ * * 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 IOJobData; 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(PAlbum* const src, PAlbum* const dest); /// Copy items to another album static void copy(const QList& infos, PAlbum* const dest); /// Copy an external file to another album static void copy(const QUrl& src, PAlbum* const dest); /// Copy external files to another album static void copy(const QList& srcList, PAlbum* const dest); /// Move an album into another album static void move(PAlbum* const src, PAlbum* const dest); /// Move items to another album static void move(const QList& infos, PAlbum* const dest); /// Move external files another album static void move(const QUrl& src, PAlbum* const dest); /// Move external files into another album static void move(const QList& srcList, PAlbum* const dest); static void del(const QList& infos, bool useTrash); static void del(const ImageInfo& info, bool useTrash); static void del(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 signalRenameSucceeded(const QUrl&); void signalRenameFailed(const QUrl&); private: DIO(); ~DIO(); - void renameFile(IOJobData* const data); - - void processRename(IOJobData* const data); void processJob(IOJobData* const data); void createJob(IOJobData* const data); 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 localFileSuffixes; private: void process(const QList&); }; // ----------------------------------------------------------------------------------------- class GroupedImagesFinder { public: explicit GroupedImagesFinder(const QList& source); QList infos; private: void process(const QList& source); }; } // namespace Digikam #endif // _DIGIKAM_IO_H_ diff --git a/libs/iojobs/iojob.cpp b/libs/iojobs/iojob.cpp index 7ff0e85d3f..db1b95b036 100644 --- a/libs/iojobs/iojob.cpp +++ b/libs/iojobs/iojob.cpp @@ -1,437 +1,437 @@ /* ============================================================ * * 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); + 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 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); } 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 d9a880f7e9..af4362421d 100644 --- a/libs/iojobs/iojob.h +++ b/libs/iojobs/iojob.h @@ -1,189 +1,189 @@ /* ============================================================ * * 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" namespace Digikam { class ImageInfo; class DIGIKAM_EXPORT IOJob : public ActionJob { Q_OBJECT protected: IOJob(); Q_SIGNALS: void error(const QString& errMsg); }; // --------------------------------------- class DIGIKAM_EXPORT CopyJob : public IOJob { Q_OBJECT public: CopyJob(const QUrl& src, const QUrl& dest, bool isMove); protected: void run(); private: QUrl m_src; QUrl m_dest; bool m_isMove; }; // --------------------------------------- class DIGIKAM_EXPORT DeleteJob : public IOJob { Q_OBJECT public: DeleteJob(const QUrl& srcToDelete, bool useTrash, bool markAsObsolete=false); protected: void run(); private: qlonglong getItemFromUrl(const QUrl& url); private: QUrl m_srcToDelete; bool m_useTrash; bool m_markAsObsolete; }; // --------------------------------------- class DIGIKAM_EXPORT RenameFileJob : public IOJob { Q_OBJECT public: RenameFileJob(const QUrl& srcToRename, const QUrl& newName); Q_SIGNALS: - void signalRenamed(const QUrl& oldUrl, const QUrl& newUrl); + 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); private: void run(); private: QString m_collectionPath; }; // ---------------------------------------------- class DIGIKAM_EXPORT RestoreDTrashItemsJob : public IOJob { Q_OBJECT public: RestoreDTrashItemsJob(const DTrashItemInfoList& infos); private: void run(); private: DTrashItemInfoList m_dtrashItemInfoList; }; // ---------------------------------------------- class DIGIKAM_EXPORT DeleteDTrashItemsJob : public IOJob { Q_OBJECT public: DeleteDTrashItemsJob(const DTrashItemInfoList& infos); private: void run(); private: DTrashItemInfoList m_dtrashItemInfoList; }; } // namespace Digikam #endif // IOJOB_H diff --git a/libs/iojobs/iojobdata.cpp b/libs/iojobs/iojobdata.cpp index 1796f04ee7..f41c012108 100644 --- a/libs/iojobs/iojobdata.cpp +++ b/libs/iojobs/iojobdata.cpp @@ -1,219 +1,208 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2018-02-24 * Description : Container for IOJob data. * * Copyright (C) 2018 by Maik Qualmann * * 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 "iojobdata.h" // Local includes #include "album.h" #include "imageinfo.h" #include "digikam_debug.h" namespace Digikam { class IOJobData::Private { public: Private() : operation(0), srcAlbum(0), destAlbum(0) { } int operation; - QString newName; - PAlbum* srcAlbum; PAlbum* destAlbum; QList imageInfoList; QList sourceUrlList; QUrl destUrl; }; IOJobData::IOJobData(int operation, const QList& infos, PAlbum* const dest) : d(new Private) { d->operation = operation; d->destAlbum = dest; setImageInfos(infos); if (d->destAlbum) { d->destUrl = d->destAlbum->fileUrl(); } } IOJobData::IOJobData(int operation, const QList& urls, PAlbum* const dest) : d(new Private) { d->operation = operation; d->sourceUrlList = urls; d->destAlbum = dest; if (d->destAlbum) { d->destUrl = d->destAlbum->fileUrl(); } } IOJobData::IOJobData(int operation, PAlbum* const src, PAlbum* const dest) : d(new Private) { d->operation = operation; d->srcAlbum = src; d->destAlbum = dest; d->sourceUrlList.clear(); if (d->srcAlbum) { d->sourceUrlList << d->srcAlbum->fileUrl(); } if (d->destAlbum) { d->destUrl = d->destAlbum->fileUrl(); } } IOJobData::IOJobData(int operation, const QList& urls, const QUrl& dest) : d(new Private) { d->operation = operation; d->sourceUrlList = urls; d->destUrl = dest; } IOJobData::IOJobData(int operation, const ImageInfo& info, const QString& name) : d(new Private) { d->operation = operation; - d->newName = name; setImageInfos(QList() << info); + + d->destUrl = srcUrl().adjusted(QUrl::RemoveFilename); + d->destUrl.setPath(d->destUrl.path() + name); } IOJobData::~IOJobData() { delete d; } void IOJobData::setImageInfos(const QList& infos) { d->imageInfoList = infos; d->sourceUrlList.clear(); foreach(const ImageInfo& info, d->imageInfoList) { d->sourceUrlList << info.fileUrl(); } } -void IOJobData::addSourceUrls(const QList& urls) +void IOJobData::setSourceUrls(const QList& urls) { - foreach(const QUrl& url, urls) - { - if (!d->sourceUrlList.contains(url)) - { - d->sourceUrlList << url; - } - } + d->sourceUrlList = urls; } void IOJobData::setDestUrl(const QUrl& url) { d->destUrl = url; } int IOJobData::operation() const { return d->operation; } PAlbum* IOJobData::srcAlbum() const { return d->srcAlbum; } PAlbum* IOJobData::destAlbum() const { return d->destAlbum; } QUrl IOJobData::srcUrl() const { if (d->sourceUrlList.isEmpty()) { return QUrl(); } return d->sourceUrlList.first(); } QUrl IOJobData::destUrl() const { return d->destUrl; } ImageInfo IOJobData::imageInfo() const { if (d->imageInfoList.isEmpty()) { return ImageInfo(); } return d->imageInfoList.first(); } -QString IOJobData::newName() const -{ - return d->newName; -} - QList IOJobData::sourceUrls() const { return d->sourceUrlList; } QList IOJobData::imageInfos() const { return d->imageInfoList; } } // namespace Digikam diff --git a/libs/iojobs/iojobdata.h b/libs/iojobs/iojobdata.h index 4ea56b787f..c42401ecb9 100644 --- a/libs/iojobs/iojobdata.h +++ b/libs/iojobs/iojobdata.h @@ -1,105 +1,103 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2018-02-24 * Description : Container for IOJob data. * * Copyright (C) 2018 by Maik Qualmann * * 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_DATA_H #define IOJOB_DATA_H // Qt includes #include #include namespace Digikam { class PAlbum; class ImageInfo; class IOJobData { public: enum Operation { CopyAlbum = 1 << 0, CopyImage = 1 << 1, CopyFiles = 1 << 2, MoveAlbum = 1 << 3, MoveImage = 1 << 4, MoveFiles = 1 << 5, Rename = 1 << 6, Delete = 1 << 7, Trash = 1 << 8, }; explicit IOJobData(int operation, const QList& infos, PAlbum* const dest = 0); explicit IOJobData(int operation, const QList& urls, PAlbum* const dest = 0); explicit IOJobData(int operation, PAlbum* const src, PAlbum* const dest); explicit IOJobData(int operation, const QList& urls, const QUrl& dest); explicit IOJobData(int operation, const ImageInfo& info, const QString& name); ~IOJobData(); void setImageInfos(const QList& infos); - void addSourceUrls(const QList& urls); + void setSourceUrls(const QList& urls); void setDestUrl(const QUrl& url); int operation() const; PAlbum* srcAlbum() const; PAlbum* destAlbum() const; QUrl srcUrl() const; QUrl destUrl() const; ImageInfo imageInfo() const; - QString newName() const; - QList sourceUrls() const; QList imageInfos() const; private: class Private; Private* const d; }; } // namespace Digikam #endif // IOJOB_DATA_H diff --git a/libs/iojobs/iojobsthread.cpp b/libs/iojobs/iojobsthread.cpp index 21aeb18a54..4a362f681b 100644 --- a/libs/iojobs/iojobsthread.cpp +++ b/libs/iojobs/iojobsthread.cpp @@ -1,259 +1,259 @@ /* ============================================================ * * 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; foreach (const QUrl& url, data->sourceUrls()) { CopyJob* const j = new CopyJob(url, data->destUrl(), false); connectOneJob(j); collection.insert(j, 0); d->jobsCount++; } appendJobs(collection); } void IOJobsThread::move(IOJobData* const data) { d->jobData = data; ActionJobCollection collection; foreach (const QUrl& url, data->sourceUrls()) { CopyJob* const j = new CopyJob(url, data->destUrl(), true); 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()) { DeleteJob* const j = new DeleteJob(url, useTrash, true); 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,QUrl)), - this, SIGNAL(signalRenamed(QUrl,QUrl))); + 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())); } void IOJobsThread::slotOneJobFinished() { d->jobsCount--; if (d->jobData) { emit signalOneProccessed(d->jobData->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(); } IOJobData* IOJobsThread::jobData() { return d->jobData; } } // namespace Digikam diff --git a/libs/iojobs/iojobsthread.h b/libs/iojobs/iojobsthread.h index 35aed1fa60..2b22d2b037 100644 --- a/libs/iojobs/iojobsthread.h +++ b/libs/iojobs/iojobsthread.h @@ -1,161 +1,161 @@ /* ============================================================ * * 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 IOJobData; 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 data: IOJobsData container */ void copy(IOJobData* const data); /** * @brief Starts a number of jobs to move source files to destination * @param data: IOJobsData container */ void move(IOJobData* const data); /** * @brief Starts a number of jobs to delete multiple files * @param data: IOJobsData container */ void deleteFiles(IOJobData* const data); /** * @brief Starts one job to rename a file to a new name * @param data: IOJobsData container */ void renameFile(IOJobData* const data); /** * @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 isCanceled * @return true if the thread was inturrupted */ bool isCanceled(); /** * @brief hasErrors * @return true if string list was not empty */ bool hasErrors(); /** * @brief errorsList * @return */ QList& errorsList(); /** * @brief jobData * @return */ IOJobData* jobData(); public Q_SLOTS: /** * @brief cancels thread execution */ void slotCancel(); Q_SIGNALS: void finished(); - void signalRenamed(const QUrl& oldUrl, const QUrl& newURl); + void signalRenamed(const QUrl& oldUrl); void signalRenameFailed(const QUrl& oldUrl); void signalOneProccessed(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); private Q_SLOTS: /** * @brief connected to all active jobs and checks if the job * list has finished to report that thread is finished */ void slotOneJobFinished(); /** * @brief A slot to receive the error from the job * @param errString: string to be appended */ void slotError(const QString& errString); private: class Private; Private* const d; }; } // namespace Digikam #endif // IOJOBSTHREAD_H