diff --git a/libs/database/utils/dio.cpp b/libs/database/utils/dio.cpp index df2569e649..8daf1af93a 100644 --- a/libs/database/utils/dio.cpp +++ b/libs/database/utils/dio.cpp @@ -1,502 +1,504 @@ /* ============================================================ * * 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 "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); - instance()->createJob(operation, QList() << src->fileUrl(), dest->fileUrl()); + 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); - instance()->createJob(operation, finder.localFiles, dest); + createJob(operation, finder.localFiles, dest); if (!finder.remoteFiles.isEmpty()) { - instance()->createJob(operation, finder.remoteFiles, dest); + createJob(operation, finder.remoteFiles, dest); // stat'ing is unreliable; just try to copy and suppress error message - instance()->createJob(operation | SourceStatusUnknown, finder.possibleRemoteSidecars, dest); + 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) { - instance()->createJob(Rename, QList() << finder.localFiles.at(i), - QUrl::fromLocalFile(dest.toLocalFile() + finder.localFileSuffixes.at(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) { - instance()->createJob(Rename | SourceStatusUnknown, - QList() << finder.possibleRemoteSidecars.at(i), - QUrl::fromLocalFile(dest.toLocalFile() + finder.possibleRemoteSidecarSuffixes.at(i))); + createJob(Rename | SourceStatusUnknown, + QList() << finder.possibleRemoteSidecars.at(i), + QUrl::fromLocalFile(dest.toLocalFile() + finder.possibleRemoteSidecarSuffixes.at(i))); } - instance()->createJob(Rename, QList() << src, dest); + createJob(Rename, QList() << src, dest); } void DIO::createJob(int operation, const QList& src, const QUrl& dest) { if (src.isEmpty()) { return; } IOJobsThread* jobThread = 0; int flags = operation & FlagMask; operation &= OperationMask; if (operation == Copy) { jobThread = IOJobsManager::instance()->startCopy(src, dest); } else if (operation == Move) { jobThread = IOJobsManager::instance()->startMove(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); 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); } else // operation == Del { qCDebug(DIGIKAM_DATABASE_LOG) << "SRCS " << src; jobThread = IOJobsManager::instance()->startDelete(src, false); } if (flags & SourceStatusUnknown) { jobThread->setKeepErrors(false); } connect(jobThread, SIGNAL(finished()), this, SLOT(slotResult())); } 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()); } } 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); } } // namespace Digikam