Bug 389273

Summary: Copy/move selected items to anothor Album doesn't work correctly [patch]
Product: [Applications] digikam Reporter: Manfred <away>
Component: Albums-EngineAssignee: Digikam Developers <digikam-bugs-null>
Status: RESOLVED FIXED    
Severity: task CC: caulier.gilles, metzpinguin
Priority: NOR    
Version: 5.8.0   
Target Milestone: ---   
Platform: Appimage   
OS: Linux   
Latest Commit: Version Fixed In: 6.0.0
Sentry Crash Report:
Attachments: DIOProgressManager.patch

Description Manfred 2018-01-21 08:53:23 UTC
By copying/moving selected item to another album always ignoring selected once. I
it used always the whole amount images from actual album. You cann't copy/move selected items. Also when digikam copying the whole album you cann't cancel it in digikam. It is always a extra thread, that you have kill it by a terminal.
Comment 1 Manfred 2018-01-21 09:24:03 UTC
Also by copying/moving some images selected by a group, any images will be copied not the selected once. This behavior is unpredictically and very strange.
Comment 2 caulier.gilles 2018-01-21 09:38:43 UTC
Reproducible with 5.9.0 pre release AppImage ?

https://files.kde.org/digikam/

Did you use Mysql or sqlite database ?

Gilles Caulier
Comment 3 Manfred 2018-01-21 10:05:50 UTC
I am using sqlite. The total amount of images is approx. 16.000. One album is connected as a network drive, mount via smbfs. The other album is connected to local drive.
I didn't use 5.9.0. So I have to check later.
Manfred Doerner
Comment 4 caulier.gilles 2018-01-21 10:57:48 UTC
Another point : run the AppImage from a console and look the debug trace while you perform the operation in DK. This can be instructive.

Gilles Caulier
Comment 5 Manfred 2018-01-21 15:35:35 UTC
The problem is also reproducible with version 5.9.0. 
Debug logs are only "[New Thread...]" and "[Thread....exited]", not more.
I selected 156 items and this was showed but round about 3000 items where copied.
Comment 6 Manfred 2018-01-21 15:48:44 UTC
How I reproduced: 
1. Went in my my Album with 1896 images.
2. Showed all images also which are grouped. I am using grouping.
3. Selected 4+ rating. -> 156 images showed.
3. Selected all 156 images. It signed at status bar that 156 images are selected.
4. Copied to a new album. Digikam noticed that 156 images will be copied. 
5. But all 1896 images where copied. That's the problem.
Comment 7 Manfred 2018-01-21 15:55:31 UTC
One notice more:
After copying images by digikam the RAM buff/cache is always using 5.6GB. It is freezed and isn't decreasing. So I had to reboot my computer.
Comment 8 Maik Qualmann 2018-01-21 19:53:10 UTC
Copying all images of the group is a very old behavior of digiKam. We have recently created the possibility that this behavior can already be configured for many actions. For file operations via drag & drop or copy / move it is not yet implemented. You have to open all groups and select only the images you want to copy. That the memory overflows when copying, I can not reproduce here. I can imagine no reason at the moment which could be triggered. A test here with many more images shows no abnormalities.

Maik
Comment 9 Manfred 2018-01-23 09:52:30 UTC
I investigated one more time and I can explain my mistake.
I am using grouping of images. When the group is opened, the "master" image, which hold the information, looks very similar to others. It differs only by a "number" which indicates the amount of the group. So when only the "master" image is selected digikam shows that one image is selected, ok. Now when I copy/move only the one selected "master" image digikam will be copying the hole group without any information. This was a little bit confusion for me. But now I know this issue and it's ok for me.
In fact the memory overflows isn't a problem of digikam.
Now this topic can be closed because it isn't a bug.
Many thanks for the support.
Finally, digikam is a great software:-) Thanks a lot to the whole digikam team!
Manfred
Comment 10 Maik Qualmann 2018-01-23 11:40:49 UTC
Created attachment 110071 [details]
DIOProgressManager.patch

This is the first version for a patch to provide a progress bar and the ability to cancel file operations (copy, move and delete items).

Maik
Comment 11 Maik Qualmann 2018-01-23 19:47:43 UTC
Comment on attachment 110071 [details]
DIOProgressManager.patch

>diff --git a/libs/database/utils/dio.cpp b/libs/database/utils/dio.cpp
>index 82813bae60..952c9e8336 100644
>--- a/libs/database/utils/dio.cpp
>+++ b/libs/database/utils/dio.cpp
>@@ -48,6 +48,7 @@
> #include "collectionmanager.h"
> #include "dnotificationwrapper.h"
> #include "digikamapp.h"
>+#include "progressmanager.h"
> 
> namespace Digikam
> {
>@@ -188,7 +189,7 @@ void DIO::Private::processJob(int operation, const QList<QUrl>& srcList, const Q
>     {
>         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);
>+        emit jobToCreate(operation | IOJobsThread::SourceStatusUnknown, finder.possibleRemoteSidecars, dest);
>     }
> }
> 
>@@ -200,7 +201,7 @@ void DIO::Private::processRename(const QUrl& src, const QUrl& dest)
>     {
>         for (int i = 0 ; i < finder.localFiles.length() ; ++i)
>         {
>-            emit jobToCreate(Rename, QList<QUrl>() << finder.localFiles.at(i),
>+            emit jobToCreate(IOJobsThread::Rename, QList<QUrl>() << finder.localFiles.at(i),
>                              QUrl::fromLocalFile(dest.toLocalFile() + finder.localFileSuffixes.at(i)));
>         }
> 
>@@ -209,11 +210,11 @@ void DIO::Private::processRename(const QUrl& src, const QUrl& dest)
> 
>     for (int i = 0 ; i < finder.remoteFileSuffixes.length() ; ++i)
>     {
>-        emit jobToCreate(Rename | SourceStatusUnknown,
>+        emit jobToCreate(IOJobsThread::Rename | IOJobsThread::SourceStatusUnknown,
>                          QList<QUrl>() << finder.possibleRemoteSidecars.at(i),
>                          QUrl::fromLocalFile(dest.toLocalFile() + finder.possibleRemoteSidecarSuffixes.at(i)));
>     }
>-    emit jobToCreate(Rename, QList<QUrl>() << src, dest);
>+    emit jobToCreate(IOJobsThread::Rename, QList<QUrl>() << src, dest);
> }
> 
> void DIO::Private::albumToAlbum(int operation, const PAlbum* const src, const PAlbum* const dest)
>@@ -231,7 +232,7 @@ void DIO::Private::imagesToAlbum(int operation, const QList<ImageInfo>& infos, c
>     QList<qlonglong> ids;
>     QList<QUrl>      urls;
> 
>-    if (operation == Move)
>+    if (operation == IOJobsThread::IOJobsThread::Move)
>     {
>         // update the image infos
>         CoreDbAccess access;
>@@ -290,7 +291,7 @@ void DIO::Private::deleteFiles(const QList<ImageInfo>& infos, bool useTrash)
> 
>     qCDebug(DIGIKAM_DATABASE_LOG) << "Deleting files:" << urls;
> 
>-    emit jobToProcess(useTrash ? Trash : Delete, urls, QUrl());
>+    emit jobToProcess(useTrash ? IOJobsThread::Trash : IOJobsThread::Delete, urls, QUrl());
> }
> 
> // ------------------------------------------------------------------------------------------------
>@@ -335,19 +336,38 @@ void DIO::createJob(int operation, const QList<QUrl>& src, const QUrl& dest)
>         return;
>     }
> 
>+    ProgressItem* item      = 0;
>     IOJobsThread* jobThread = 0;
>-    int flags               = operation & FlagMask;
>-    operation              &= OperationMask;
>+    int flags               = operation & IOJobsThread::FlagMask;
>+    operation              &= IOJobsThread::OperationMask;
> 
>-    if (operation == Copy)
>+    if (operation == IOJobsThread::Copy)
>     {
>+        item = getProgressItem(IOJobsThread::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(src, dest);
>     }
>-    else if (operation == Move)
>+    else if (operation == IOJobsThread::Move)
>     {
>+        item = getProgressItem(IOJobsThread::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(src, dest);
>     }
>-    else if (operation == Rename)
>+    else if (operation == IOJobsThread::Rename)
>     {
>         if (src.size() != 1)
>         {
>@@ -363,23 +383,53 @@ void DIO::createJob(int operation, const QList<QUrl>& src, const QUrl& dest)
>         connect(jobThread, SIGNAL(renameFailed(QUrl)),
>                 this, SIGNAL(imageRenameFailed(QUrl)));
>     }
>-    else if (operation == Trash)
>+    else if (operation == IOJobsThread::Trash)
>     {
>+        item = getProgressItem(IOJobsThread::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(src);
>     }
>     else // operation == Del
>     {
>+        item = getProgressItem(IOJobsThread::Delete);
>+
>+        if (!item || item->totalCompleted())
>+        {
>+            item = ProgressManager::instance()->createProgressItem(QLatin1String("DIODelete"),
>+                                                                   i18n("Delete"), QString(), true, false);
>+        }
>+
>+        item->setTotalItems(item->totalItems() + src.count());
>         qCDebug(DIGIKAM_DATABASE_LOG) << "SRCS " << src;
>         jobThread = IOJobsManager::instance()->startDelete(src, false);
>     }
> 
>-    if (flags & SourceStatusUnknown)
>+    if (flags & IOJobsThread::SourceStatusUnknown)
>     {
>         jobThread->setKeepErrors(false);
>     }
> 
>+    connect(jobThread, SIGNAL(oneProccessed(int)),
>+            this, SLOT(slotOneProccessed(int)));
>+
>     connect(jobThread, SIGNAL(finished()),
>             this, SLOT(slotResult()));
>+
>+    if (item)
>+    {
>+        connect(item, SIGNAL(progressItemCanceled(ProgressItem*)),
>+                jobThread, SLOT(cancel()));
>+
>+        connect(item, SIGNAL(progressItemCanceled(ProgressItem*)),
>+                this, SLOT(slotCancel(ProgressItem*)));
>+    }
> }
> 
> void DIO::slotResult()
>@@ -398,6 +448,27 @@ void DIO::slotResult()
>         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)
>@@ -410,6 +481,31 @@ void DIO::slotRenamed(const QUrl& oldUrl, const QUrl& newUrl)
>     emit imageRenameSucceeded(oldUrl);
> }
> 
>+ProgressItem* DIO::getProgressItem(int operation)
>+{
>+    ProgressItem* item = 0;
>+
>+    switch ((IOJobsThread::Operation)operation)
>+    {
>+        case IOJobsThread::Copy:
>+            item = ProgressManager::instance()->findItembyId(QLatin1String("DIOCopy"));
>+            break;
>+        case IOJobsThread::Move:
>+            item = ProgressManager::instance()->findItembyId(QLatin1String("DIOMove"));
>+            break;
>+        case IOJobsThread::Trash:
>+            item = ProgressManager::instance()->findItembyId(QLatin1String("DIOTrash"));
>+            break;
>+        case IOJobsThread::Delete:
>+            item = ProgressManager::instance()->findItembyId(QLatin1String("DIODelete"));
>+            break;
>+        default:
>+            break;
>+    }
>+
>+    return item;
>+}
>+
> // Album -> Album -----------------------------------------------------
> 
> void DIO::copy(const PAlbum* const src, const PAlbum* const dest)
>@@ -419,7 +515,7 @@ void DIO::copy(const PAlbum* const src, const PAlbum* const dest)
>         return;
>     }
> 
>-    instance()->d->albumToAlbum(Copy, src, dest);
>+    instance()->d->albumToAlbum(IOJobsThread::Copy, src, dest);
> }
> 
> void DIO::move(const PAlbum* src, const PAlbum* const dest)
>@@ -433,7 +529,7 @@ void DIO::move(const PAlbum* src, const PAlbum* const dest)
>     AlbumManager::instance()->removeWatchedPAlbums(src);
> #endif
> 
>-    instance()->d->albumToAlbum(Move, src, dest);
>+    instance()->d->albumToAlbum(IOJobsThread::Move, src, dest);
> }
> 
> // Images -> Album ----------------------------------------------------
>@@ -445,7 +541,7 @@ void DIO::copy(const QList<ImageInfo>& infos, const PAlbum* const dest)
>         return;
>     }
> 
>-    instance()->d->imagesToAlbum(Copy, infos, dest);
>+    instance()->d->imagesToAlbum(IOJobsThread::Copy, infos, dest);
> }
> 
> void DIO::move(const QList<ImageInfo>& infos, const PAlbum* const dest)
>@@ -455,7 +551,7 @@ void DIO::move(const QList<ImageInfo>& infos, const PAlbum* const dest)
>         return;
>     }
> 
>-    instance()->d->imagesToAlbum(Move, infos, dest);
>+    instance()->d->imagesToAlbum(IOJobsThread::Move, infos, dest);
> }
> 
> // External files -> album --------------------------------------------
>@@ -472,7 +568,7 @@ void DIO::copy(const QList<QUrl>& srcList, const PAlbum* const dest)
>         return;
>     }
> 
>-    instance()->d->filesToAlbum(Copy, srcList, dest);
>+    instance()->d->filesToAlbum(IOJobsThread::Copy, srcList, dest);
> }
> 
> void DIO::move(const QUrl& src, const PAlbum* const dest)
>@@ -487,7 +583,7 @@ void DIO::move(const QList<QUrl>& srcList, const PAlbum* const dest)
>         return;
>     }
> 
>-    instance()->d->filesToAlbum(Move, srcList, dest);
>+    instance()->d->filesToAlbum(IOJobsThread::Move, srcList, dest);
> }
> 
> // Rename --------------------------------------------------------------
>@@ -520,7 +616,7 @@ void DIO::del(const PAlbum* const album, bool useTrash)
>     AlbumManager::instance()->removeWatchedPAlbums(album);
> #endif
> 
>-    instance()->createJob(useTrash ? Trash : Delete, QList<QUrl>() << album->fileUrl(), QUrl());
>+    instance()->createJob(useTrash ? IOJobsThread::Trash : IOJobsThread::Delete, QList<QUrl>() << album->fileUrl(), QUrl());
> }
> 
> } // namespace Digikam
>diff --git a/libs/database/utils/dio.h b/libs/database/utils/dio.h
>index acc88c80c7..b4d3822ef9 100644
>--- a/libs/database/utils/dio.h
>+++ b/libs/database/utils/dio.h
>@@ -40,6 +40,7 @@ namespace Digikam
> 
> class PAlbum;
> class ImageInfo;
>+class ProgressItem;
> 
> class DIGIKAM_EXPORT DIO : public QObject
> {
>@@ -91,9 +92,15 @@ Q_SIGNALS:
>     void imageRenameSucceeded(const QUrl&);
>     void imageRenameFailed(const QUrl&);
> 
>+protected:
>+
>+    ProgressItem* getProgressItem(int operation);
>+
> protected Q_SLOTS:
> 
>     void slotResult();
>+    void slotCancel(ProgressItem* item);
>+    void slotOneProccessed(int operation);
>     void slotRenamed(const QUrl& oldUrl, const QUrl& newUrl);
>     void createJob(int operation, const QList<QUrl>& src, const QUrl& dest);
> 
>diff --git a/libs/database/utils/dio_p.h b/libs/database/utils/dio_p.h
>index 4e61874c9c..5768de1ced 100644
>--- a/libs/database/utils/dio_p.h
>+++ b/libs/database/utils/dio_p.h
>@@ -109,19 +109,6 @@ private:
>     void process(const QList<ImageInfo>& 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
>diff --git a/libs/iojobs/iojob.cpp b/libs/iojobs/iojob.cpp
>index f5f1dc2ece..fe8a5dced1 100644
>--- a/libs/iojobs/iojob.cpp
>+++ b/libs/iojobs/iojob.cpp
>@@ -61,6 +61,11 @@ CopyJob::CopyJob(const QUrl& src, const QUrl& dest, bool isMove)
> 
> void CopyJob::run()
> {
>+    if (m_cancel)
>+    {
>+        return;
>+    }
>+
>     QFileInfo srcInfo(m_src.toLocalFile());
>     QDir dstDir(m_dest.toLocalFile());
> 
>@@ -166,6 +171,11 @@ DeleteJob::DeleteJob(const QUrl& srcToDelete, bool useTrash, bool 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();
>diff --git a/libs/iojobs/iojobsthread.cpp b/libs/iojobs/iojobsthread.cpp
>index 5014ebc7da..d315eca7cb 100644
>--- a/libs/iojobs/iojobsthread.cpp
>+++ b/libs/iojobs/iojobsthread.cpp
>@@ -44,12 +44,14 @@ public:
> 
>     Private()
>         : jobsCount(0),
>+          operation(SourceStatusUnknown),
>           isCanceled(false),
>           keepErrors(true)
>     {
>     }
> 
>     int            jobsCount;
>+    int            operation;
>     bool           isCanceled;
> 
>     bool           keepErrors;
>@@ -81,6 +83,7 @@ void IOJobsThread::copy(const QList<QUrl>& srcFiles, const QUrl destAlbum)
>         d->jobsCount++;
>     }
> 
>+    d->operation = Copy;
>     appendJobs(collection);
> }
> 
>@@ -98,6 +101,7 @@ void IOJobsThread::move(const QList<QUrl>& srcFiles, const QUrl destAlbum)
>         d->jobsCount++;
>     }
> 
>+    d->operation = Move;
>     appendJobs(collection);
> }
> 
>@@ -107,7 +111,7 @@ void IOJobsThread::deleteFiles(const QList<QUrl>& srcsToDelete, bool useTrash)
> 
>     foreach (const QUrl& url, srcsToDelete)
>     {
>-        DeleteJob* const j = new DeleteJob(url, useTrash,true);
>+        DeleteJob* const j = new DeleteJob(url, useTrash, true);
> 
>         connectOneJob(j);
> 
>@@ -115,6 +119,15 @@ void IOJobsThread::deleteFiles(const QList<QUrl>& srcsToDelete, bool useTrash)
>         d->jobsCount++;
>     }
> 
>+    if (useTrash)
>+    {
>+        d->operation = Trash;
>+    }
>+    else
>+    {
>+        d->operation = Delete;
>+    }
>+
>     appendJobs(collection);
> }
> 
>@@ -194,6 +207,7 @@ void IOJobsThread::renameFile(const QUrl& srcToRename, const QUrl& newName)
>     collection.insert(j, 0);
>     d->jobsCount++;
> 
>+    d->operation = Rename;
>     appendJobs(collection);
> }
> 
>@@ -266,6 +280,8 @@ void IOJobsThread::oneJobFinished()
> {
>     d->jobsCount--;
> 
>+    emit oneProccessed(d->operation);
>+
>     if (d->jobsCount == 0)
>     {
>         emit finished();
>@@ -278,4 +294,9 @@ void IOJobsThread::error(const QString& errString)
>     d->errorsList.append(errString);
> }
> 
>+int IOJobsThread::operation()
>+{
>+    return d->operation;
>+}
>+
> } // namespace Digikam
>diff --git a/libs/iojobs/iojobsthread.h b/libs/iojobs/iojobsthread.h
>index d7dd979e9b..477f2de588 100644
>--- a/libs/iojobs/iojobsthread.h
>+++ b/libs/iojobs/iojobsthread.h
>@@ -41,6 +41,19 @@ class DIGIKAM_EXPORT IOJobsThread : public ActionThreadBase
> 
> public:
> 
>+enum Operation
>+{
>+    Copy                = 1 << 0,
>+    Move                = 1 << 1,
>+    Rename              = 1 << 2,
>+    Trash               = 1 << 3,
>+    Delete              = 1 << 4,
>+    SourceStatusUnknown = 1 << 20,
>+
>+    OperationMask       = 0xffff,
>+    FlagMask            = 0xffff0000
>+};
>+
>     IOJobsThread(QObject* const parent);
>     ~IOJobsThread();
> 
>@@ -90,11 +103,6 @@ public:
>      */
>     void renameFile(const QUrl& srcToRename, const QUrl& newName);
> 
>-    /**
>-     * @brief cancels thread execution
>-     */
>-    void cancel();
>-
>     /**
>      * @brief isCanceled
>      * @return true if the thread was inturrupted
>@@ -125,6 +133,12 @@ public:
>      */
>     QList<QString>& errorsList();
> 
>+   /**
>+     * @brief operation
>+     * @return
>+     */
>+    int operation();
>+
> public Q_SLOTS:
> 
>     /**
>@@ -139,12 +153,18 @@ public Q_SLOTS:
>      */
>     void error(const QString& errString);
> 
>+    /**
>+     * @brief cancels thread execution
>+     */
>+    void cancel();
>+
> Q_SIGNALS:
> 
>     void finished();
> 
>     void renamed(const QUrl& oldUrl, const QUrl& newURl);
>     void renameFailed(const QUrl& oldUrl);
>+    void oneProccessed(int operation);
> 
>     void collectionTrashItemInfo(const DTrashItemInfo& trashItemInfo);
>
Comment 12 Maik Qualmann 2018-01-23 19:52:52 UTC
I close this bug as a duplicate, because the desire for a progress bar and cancel function is also described here.

Maik

*** This bug has been marked as a duplicate of bug 233063 ***
Comment 13 caulier.gilles 2018-08-29 18:52:01 UTC
Git commit 1803bd2a555890b91095d92b0321e1357db0fa59 by Maik Qualmann.
Committed on 25/02/2018 at 19:11.
Pushed by mqualmann into branch 'development/6.0.0'.

add progress indicator and cancel function to file operations
FIXED-IN: 6.0.0

M  +2    -1    NEWS.6.0.0
M  +101  -5    libs/database/utils/dio.cpp
M  +5    -0    libs/database/utils/dio.h
M  +10   -0    libs/iojobs/iojob.cpp
M  +8    -8    libs/iojobs/iojobsmanager.cpp
M  +8    -4    libs/iojobs/iojobsmanager.h
M  +21   -7    libs/iojobs/iojobsthread.cpp
M  +20   -9    libs/iojobs/iojobsthread.h

https://commits.kde.org/digikam/1803bd2a555890b91095d92b0321e1357db0fa59