File indexing completed on 2024-05-19 04:29:13

0001 /*
0002  *  SPDX-FileCopyrightText: 2023 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 #include "KisIdleTasksManager.h"
0007 
0008 #include <QQueue>
0009 #include <kis_idle_watcher.h>
0010 #include <kis_image.h>
0011 #include <KisMpl.h>
0012 #include <boost/none.hpp>
0013 
0014 
0015 namespace {
0016 struct TaskStruct {
0017     int id = 0;
0018     KisIdleTaskStrokeStrategyFactory factory;
0019 };
0020 }
0021 
0022 struct KisIdleTasksManager::Private
0023 {
0024     KisImageWSP image;
0025     KisIdleWatcher idleWatcher;
0026     QVector<TaskStruct> tasks;
0027     QQueue<int> queue;
0028     QWeakPointer<boost::none_t> currentTaskCookie;
0029 };
0030 
0031 KisIdleTasksManager::KisIdleTasksManager()
0032     : m_d(new Private())
0033 {
0034     connect(&m_d->idleWatcher, SIGNAL(startedIdleMode()), SLOT(slotImageIsIdle()));
0035     connect(&m_d->idleWatcher, SIGNAL(imageModified()), SLOT(slotImageIsModified()));
0036 }
0037 
0038 KisIdleTasksManager::~KisIdleTasksManager() = default;
0039 
0040 void KisIdleTasksManager::setImage(KisImageSP image)
0041 {
0042     m_d->idleWatcher.setTrackedImage(image);
0043     m_d->image = image;
0044     m_d->queue.clear();
0045 
0046     if (image) {
0047         slotImageIsModified();
0048         m_d->idleWatcher.triggerCountdownNoDelay();
0049     }
0050 }
0051 
0052 int KisIdleTasksManager::addIdleTask(KisIdleTaskStrokeStrategyFactory factory)
0053 {
0054     /**
0055      * TODO: don't restart the whole queue on the the task change, just
0056      * restart the currently added task
0057      */
0058 
0059     const int newId =
0060         !m_d->tasks.isEmpty() ?
0061         m_d->tasks.last().id + 1 : 0;
0062 
0063     m_d->tasks.append({newId, factory});
0064     triggerIdleTask(newId);
0065 
0066     return newId;
0067 }
0068 
0069 void KisIdleTasksManager::removeIdleTask(int id)
0070 {
0071     {
0072         auto it = std::remove_if(m_d->tasks.begin(), m_d->tasks.end(),
0073                                  kismpl::mem_equal_to(&TaskStruct::id, id));
0074         KIS_SAFE_ASSERT_RECOVER_NOOP(it != m_d->tasks.end());
0075         m_d->tasks.erase(it, m_d->tasks.end());
0076     }
0077 
0078     {
0079         auto it = std::remove(m_d->queue.begin(), m_d->queue.end(), id);
0080         m_d->queue.erase(it, m_d->queue.end());
0081     }
0082 }
0083 
0084 void KisIdleTasksManager::triggerIdleTask(int id)
0085 {
0086     {
0087         // just verify that this tasks actually exists
0088         auto it = std::find_if(m_d->tasks.begin(), m_d->tasks.end(),
0089                                kismpl::mem_equal_to(&TaskStruct::id, id));
0090         KIS_SAFE_ASSERT_RECOVER_NOOP(it != m_d->tasks.end());
0091     }
0092 
0093     auto it = std::find(m_d->queue.begin(), m_d->queue.end(), id);
0094     if (it == m_d->queue.end()) {
0095         m_d->queue.enqueue(id);
0096     }
0097 
0098     m_d->idleWatcher.triggerCountdownNoDelay();
0099 }
0100 
0101 KisIdleTasksManager::TaskGuard
0102 KisIdleTasksManager::addIdleTaskWithGuard(KisIdleTaskStrokeStrategyFactory factory)
0103 {
0104     return {addIdleTask(factory), this};
0105 }
0106 
0107 void KisIdleTasksManager::slotImageIsModified()
0108 {
0109     m_d->queue.clear();
0110     m_d->queue.reserve(m_d->tasks.size());
0111     std::transform(m_d->tasks.begin(), m_d->tasks.end(),
0112                    std::back_inserter(m_d->queue),
0113                    std::mem_fn(&TaskStruct::id));
0114 }
0115 
0116 void KisIdleTasksManager::slotImageIsIdle()
0117 {
0118     KisImageSP image = m_d->image;
0119     if (!image) return;
0120 
0121     if (m_d->currentTaskCookie) {
0122         m_d->idleWatcher.restartCountdown();
0123         return;
0124     }
0125 
0126     if (m_d->queue.isEmpty()) return;
0127 
0128     const int newTaskId = m_d->queue.dequeue();
0129 
0130     auto it = std::find_if(m_d->tasks.begin(), m_d->tasks.end(),
0131                            kismpl::mem_equal_to(&TaskStruct::id, newTaskId));
0132     KIS_SAFE_ASSERT_RECOVER_NOOP(it != m_d->tasks.end());
0133 
0134     KisIdleTaskStrokeStrategy *strategy = it->factory(image);
0135 
0136     connect(strategy, SIGNAL(sigIdleTaskFinished()), SLOT(slotTaskIsCompleted()));
0137     m_d->currentTaskCookie = strategy->idleTaskCookie();
0138 
0139     KisStrokeId strokeId = image->startStroke(strategy);
0140     image->endStroke(strokeId);
0141 }
0142 
0143 void KisIdleTasksManager::slotTaskIsCompleted()
0144 {
0145     if (m_d->queue.isEmpty()) {
0146         // all tasks are completed!
0147     } else {
0148         if (m_d->idleWatcher.isIdle()) {
0149             slotImageIsIdle();
0150         } else if (!m_d->idleWatcher.isCounting()) {
0151             m_d->idleWatcher.restartCountdown();
0152         }
0153     }
0154 }
0155