***************************************************************************/
#include "kdirectorycontentscounter.h"
-
-#include "kdirectorycontentscounterworker.h"
-#include <kitemviews/kfileitemmodel.h>
+#include "kitemviews/kfileitemmodel.h"
#include <KDirWatch>
+
+#include <QFileInfo>
+#include <QDir>
#include <QThread>
+namespace {
+ /// cache of directory counting result
+ static QHash<QString, QPair<int, long>> *s_cache;
+}
+
KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObject* parent) :
QObject(parent),
m_model(model),
m_queue(),
- m_worker(0),
+ m_worker(nullptr),
m_workerIsBusy(false),
- m_dirWatcher(0),
+ m_dirWatcher(nullptr),
m_watchedDirs()
{
connect(m_model, &KFileItemModel::itemsRemoved,
m_workerThread->start();
}
+ if (s_cache == nullptr) {
+ s_cache = new QHash<QString, QPair<int, long>>();
+ }
+
m_worker = new KDirectoryContentsCounterWorker();
m_worker->moveToThread(m_workerThread);
- ++m_workersCount;
connect(this, &KDirectoryContentsCounter::requestDirectoryContentsCount,
m_worker, &KDirectoryContentsCounterWorker::countDirectoryContents);
KDirectoryContentsCounter::~KDirectoryContentsCounter()
{
- --m_workersCount;
-
- if (m_workersCount > 0) {
+ if (m_workerThread->isRunning()) {
// The worker thread will continue running. It could even be running
// a method of m_worker at the moment, so we delete it using
// deleteLater() to prevent a crash.
m_workerThread->quit();
m_workerThread->wait();
delete m_workerThread;
- m_workerThread = 0;
+ m_workerThread = nullptr;
// The worker thread has finished running now, so it's safe to delete
// m_worker. deleteLater() would not work at all because the event loop
}
}
-void KDirectoryContentsCounter::addDirectory(const QString& path)
+void KDirectoryContentsCounter::scanDirectory(const QString& path)
{
startWorker(path);
}
-int KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path)
+void KDirectoryContentsCounter::slotResult(const QString& path, int count, long size)
{
- if (!m_dirWatcher->contains(path)) {
- m_dirWatcher->addDir(path);
- m_watchedDirs.insert(path);
- }
+ m_workerIsBusy = false;
- KDirectoryContentsCounterWorker::Options options;
+ const QFileInfo info = QFileInfo(path);
+ const QString resolvedPath = info.canonicalFilePath();
- if (m_model->showHiddenFiles()) {
- options |= KDirectoryContentsCounterWorker::CountHiddenFiles;
+ if (!m_dirWatcher->contains(resolvedPath)) {
+ m_dirWatcher->addDir(resolvedPath);
+ m_watchedDirs.insert(resolvedPath);
}
- if (m_model->showDirectoriesOnly()) {
- options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly;
+ if (!m_priorityQueue.isEmpty()) {
+ startWorker(m_priorityQueue.takeLast());
+ } else if (!m_queue.isEmpty()) {
+ startWorker(m_queue.takeFirst());
}
- return KDirectoryContentsCounterWorker::subItemsCount(path, options);
-}
-
-void KDirectoryContentsCounter::slotResult(const QString& path, int count)
-{
- m_workerIsBusy = false;
-
- if (!m_dirWatcher->contains(path)) {
- m_dirWatcher->addDir(path);
- m_watchedDirs.insert(path);
+ if (s_cache->contains(resolvedPath)) {
+ const auto pair = s_cache->value(resolvedPath);
+ if (pair.first == count && pair.second == size) {
+ // no change no need to send another result event
+ return;
+ }
}
- if (!m_queue.isEmpty()) {
- startWorker(m_queue.dequeue());
+ if (info.dir().path() == m_model->rootItem().url().path()) {
+ // update cache or overwrite value
+ // when path is a direct children of the current model root
+ s_cache->insert(resolvedPath, QPair<int, long>(count, size));
}
- emit result(path, count);
+ // sends the results
+ emit result(resolvedPath, count, size);
}
void KDirectoryContentsCounter::slotDirWatchDirty(const QString& path)
if (!m_watchedDirs.isEmpty()) {
// Don't let KDirWatch watch for removed items
if (allItemsRemoved) {
- foreach (const QString& path, m_watchedDirs) {
+ for (const QString& path : qAsConst(m_watchedDirs)) {
m_dirWatcher->removeDir(path);
}
m_watchedDirs.clear();
void KDirectoryContentsCounter::startWorker(const QString& path)
{
+ const bool alreadyInCache = s_cache->contains(path);
+ if (alreadyInCache) {
+ // fast path when in cache
+ // will be updated later if result has changed
+ const auto pair = s_cache->value(path);
+ emit result(path, pair.first, pair.second);
+ }
+
if (m_workerIsBusy) {
- m_queue.enqueue(path);
+ if (!m_queue.contains(path) && !m_priorityQueue.contains(path)) {
+ if (alreadyInCache) {
+ m_queue.append(path);
+ } else {
+ // append to priority queue
+ m_priorityQueue.append(path);
+ }
+ }
} else {
KDirectoryContentsCounterWorker::Options options;
}
}
-QThread* KDirectoryContentsCounter::m_workerThread = 0;
-int KDirectoryContentsCounter::m_workersCount = 0;
+QThread* KDirectoryContentsCounter::m_workerThread = nullptr;