diff --git a/libs/database/utils/dio.cpp b/libs/database/utils/dio.cpp index 7fab493fb0..fec558f144 100644 --- a/libs/database/utils/dio.cpp +++ b/libs/database/utils/dio.cpp @@ -1,529 +1,515 @@ /* ============================================================ * * 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" #include "dio_p.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 { -namespace -{ - const QString renameFileProperty(QLatin1String("DIO Rename source file")); - const QString noErrorMessageProperty(QLatin1String("DIO Ignore Error Message")); -} - -// ------------------------------------------------------------------------------------------------ - 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(); } } } } // ------------------------------------------------------------------------------------------------ DIO::Private::Private(DIO* const qq) : q(qq) { - connectAndSchedule(this, SIGNAL(jobToProcess(int,QList,QUrl)), - this, SLOT(processJob(int,QList,QUrl))); - - connectAndSchedule(this, SIGNAL(renameToProcess(QUrl,QUrl)), - this, SLOT(processRename(QUrl,QUrl))); - connect(this, SIGNAL(jobToCreate(int,QList,QUrl)), q, SLOT(createJob(int,QList,QUrl))); } void DIO::Private::processJob(int operation, const QList& srcList, const QUrl& dest) { SidecarFinder finder(srcList); emit jobToCreate(operation, finder.localFiles, dest); if (!finder.remoteFiles.isEmpty()) { emit jobToCreate(operation, finder.remoteFiles, dest); // stat'ing is unreliable; just try to copy and suppress error message emit jobToCreate(operation | SourceStatusUnknown, finder.possibleRemoteSidecars, dest); } } void DIO::Private::processRename(const QUrl& src, const QUrl& dest) { SidecarFinder finder(src); if (src.isLocalFile()) { for (int i = 0 ; i < finder.localFiles.length() ; ++i) { emit jobToCreate(Rename, QList() << finder.localFiles.at(i), QUrl::fromLocalFile(dest.toLocalFile() + finder.localFileSuffixes.at(i))); } return; } for (int i = 0 ; i < finder.remoteFileSuffixes.length() ; ++i) { emit jobToCreate(Rename | SourceStatusUnknown, QList() << finder.possibleRemoteSidecars.at(i), QUrl::fromLocalFile(dest.toLocalFile() + finder.possibleRemoteSidecarSuffixes.at(i))); } - emit jobToCreate(Rename, QList() << src, dest); -} -void DIO::Private::albumToAlbum(int operation, const PAlbum* const src, const PAlbum* const dest) -{ - ScanController::instance()->hintAtMoveOrCopyOfAlbum(src, dest); - emit jobToCreate(operation, QList() << src->fileUrl(), dest->fileUrl()); + emit jobToCreate(Rename, QList() << src, dest); } void DIO::Private::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); - emit jobToProcess(operation, urls, dest->fileUrl()); + processJob(operation, urls, dest->fileUrl()); +} + +void DIO::Private::albumToAlbum(int operation, const PAlbum* const src, const PAlbum* const dest) +{ + ScanController::instance()->hintAtMoveOrCopyOfAlbum(src, dest); + emit jobToCreate(operation, QList() << src->fileUrl(), dest->fileUrl()); } void DIO::Private::filesToAlbum(int operation, const QList& srcList, const PAlbum* const dest) { - emit jobToProcess(operation, srcList, dest->fileUrl()); + processJob(operation, srcList, dest->fileUrl()); } void DIO::Private::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); - emit renameToProcess(oldUrl, newUrl); + processRename(oldUrl, newUrl); } void DIO::Private::deleteFiles(const QList& infos, bool useTrash) { QList urls; foreach(const ImageInfo& info, infos) { urls << info.fileUrl(); } qCDebug(DIGIKAM_DATABASE_LOG) << "Deleting files:" << urls; - emit jobToProcess(useTrash ? Trash : Delete, urls, QUrl()); + processJob(useTrash ? Trash : Delete, urls, QUrl()); } // ------------------------------------------------------------------------------------------------ class DIOCreator { public: DIO object; }; Q_GLOBAL_STATIC(DIOCreator, creator) // ------------------------------------------------------------------------------------------------ DIO* DIO::instance() { return &creator->object; } DIO::DIO() : d(new Private(this)) { qRegisterMetaType>("QList"); } DIO::~DIO() { delete d; } void DIO::cleanUp() { - instance()->d->deactivate(); - instance()->d->wait(); } 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); } // Album -> Album ----------------------------------------------------- void DIO::copy(const PAlbum* const src, const PAlbum* const dest) { if (!src || !dest) { return; } instance()->d->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()->d->albumToAlbum(Move, src, dest); } // Images -> Album ---------------------------------------------------- void DIO::copy(const QList& infos, const PAlbum* const dest) { if (!dest) { return; } instance()->d->imagesToAlbum(Copy, infos, dest); } void DIO::move(const QList& infos, const PAlbum* const dest) { if (!dest) { return; } instance()->d->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()->d->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()->d->filesToAlbum(Move, srcList, dest); } // Rename -------------------------------------------------------------- void DIO::rename(const ImageInfo& info, const QString& newName) { instance()->d->renameFile(info, newName); } // Delete -------------------------------------------------------------- void DIO::del(const QList& infos, bool useTrash) { instance()->d->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()); } } // namespace Digikam diff --git a/libs/database/utils/dio.h b/libs/database/utils/dio.h index acc88c80c7..236c59a24f 100644 --- a/libs/database/utils/dio.h +++ b/libs/database/utils/dio.h @@ -1,115 +1,115 @@ /* ============================================================ * * 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 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&); -protected Q_SLOTS: - - void slotResult(); - void slotRenamed(const QUrl& oldUrl, const QUrl& newUrl); - void createJob(int operation, const QList& src, const QUrl& dest); - private: DIO(); ~DIO(); +private Q_SLOTS: + + void slotResult(); + void slotRenamed(const QUrl& oldUrl, const QUrl& newUrl); + void createJob(int operation, const QList& src, const QUrl& dest); + private: class Private; Private* const d; friend class DIOCreator; }; } // namespace Digikam #endif // _DIGIKAM_IO_H_ diff --git a/libs/database/utils/dio_p.h b/libs/database/utils/dio_p.h index 4e61874c9c..2b558a8849 100644 --- a/libs/database/utils/dio_p.h +++ b/libs/database/utils/dio_p.h @@ -1,129 +1,116 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-03-13 * Description : low level files management interface. * * 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_P_H_ #define _DIGIKAM_IO_P_H_ -// Local includes +// Qt includes -#include "workerobject.h" +#include namespace Digikam { class Album; class DIO; -class DIO::Private : public WorkerObject +class DIO::Private : public QObject { Q_OBJECT public: explicit Private(DIO* const qq); - void albumToAlbum(int operation, const PAlbum* const src, const PAlbum* const dest); 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); - bool directLocalFileMove(const QString& src, const QString& destPath); - -public Q_SLOTS: - void processJob(int operation, const QList& src, const QUrl& dest); void processRename(const QUrl& src, const QUrl& dest); Q_SIGNALS: - void jobToProcess(int operation, const QList& src, const QUrl& dest); - void renameToProcess(const QUrl& src, const QUrl& dest); void jobToCreate(int operation, const QList& src, const QUrl& dest); - void remoteFilesToStat(int operation, const QList& srcToStat, const QUrl& dest); public: DIO* const q; }; // ----------------------------------------------------------------------------------------- -namespace // anonymous namespace -{ - 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 }; -} // anonymous namespace - } // namespace Digikam #endif // _DIGIKAM_IO_H_