diff options
Diffstat (limited to 'src/kitemviews/private')
4 files changed, 126 insertions, 78 deletions
diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp index bd204fe8e..a19bce8b3 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.cpp +++ b/src/kitemviews/private/kdirectorycontentscounter.cpp @@ -24,8 +24,14 @@ #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), @@ -43,9 +49,12 @@ KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObj 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); @@ -58,9 +67,7 @@ KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObj 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. @@ -79,38 +86,17 @@ KDirectoryContentsCounter::~KDirectoryContentsCounter() } } -void KDirectoryContentsCounter::addDirectory(const QString& path) +void KDirectoryContentsCounter::scanDirectory(const QString& path) { startWorker(path); } -int KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path) -{ - const QString resolvedPath = QFileInfo(path).canonicalFilePath(); - - if (!m_dirWatcher->contains(resolvedPath)) { - m_dirWatcher->addDir(resolvedPath); - m_watchedDirs.insert(resolvedPath); - } - - KDirectoryContentsCounterWorker::Options options; - - if (m_model->showHiddenFiles()) { - options |= KDirectoryContentsCounterWorker::CountHiddenFiles; - } - - if (m_model->showDirectoriesOnly()) { - options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly; - } - - return KDirectoryContentsCounterWorker::subItemsCount(path, options); -} - -void KDirectoryContentsCounter::slotResult(const QString& path, int count) +void KDirectoryContentsCounter::slotResult(const QString& path, int count, long size) { m_workerIsBusy = false; - const QString resolvedPath = QFileInfo(path).canonicalFilePath(); + const QFileInfo info = QFileInfo(path); + const QString resolvedPath = info.canonicalFilePath(); if (!m_dirWatcher->contains(resolvedPath)) { m_dirWatcher->addDir(resolvedPath); @@ -121,7 +107,22 @@ void KDirectoryContentsCounter::slotResult(const QString& path, int count) startWorker(m_queue.dequeue()); } - emit result(path, count); + 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 (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)); + } + + // sends the results + emit result(resolvedPath, count, size); } void KDirectoryContentsCounter::slotDirWatchDirty(const QString& path) @@ -146,7 +147,7 @@ void KDirectoryContentsCounter::slotItemsRemoved() 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(); @@ -166,6 +167,13 @@ void KDirectoryContentsCounter::slotItemsRemoved() void KDirectoryContentsCounter::startWorker(const QString& path) { + if (s_cache->contains(path)) { + // 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); } else { @@ -185,4 +193,3 @@ void KDirectoryContentsCounter::startWorker(const QString& path) } QThread* KDirectoryContentsCounter::m_workerThread = nullptr; -int KDirectoryContentsCounter::m_workersCount = 0; diff --git a/src/kitemviews/private/kdirectorycontentscounter.h b/src/kitemviews/private/kdirectorycontentscounter.h index 349860757..0c900ec64 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.h +++ b/src/kitemviews/private/kdirectorycontentscounter.h @@ -25,6 +25,7 @@ #include <QQueue> #include <QSet> +#include <QHash> class KDirWatch; class KFileItemModel; @@ -45,28 +46,23 @@ public: * * The directory \a path is watched for changes, and the signal is emitted * again if a change occurs. - */ - void addDirectory(const QString& path); - - /** - * In contrast to \a addDirectory, this function counts the items inside - * the directory \a path synchronously and returns the result. * - * The directory is watched for changes, and the signal \a result is - * emitted if a change occurs. + * Uses a cache internally to speed up first result, + * but emit again result when the cache was updated */ - int countDirectoryContentsSynchronously(const QString& path); + void scanDirectory(const QString& path); signals: /** - * Signals that the directory \a path contains \a count items. + * Signals that the directory \a path contains \a count items of size \a + * Size calculation depends on parameter DetailsModeSettings::recursiveDirectorySizeLimit */ - void result(const QString& path, int count); + void result(const QString& path, int count, long size); void requestDirectoryContentsCount(const QString& path, KDirectoryContentsCounterWorker::Options options); private slots: - void slotResult(const QString& path, int count); + void slotResult(const QString& path, int count, long size); void slotDirWatchDirty(const QString& path); void slotItemsRemoved(); @@ -79,7 +75,6 @@ private: QQueue<QString> m_queue; static QThread* m_workerThread; - static int m_workersCount; KDirectoryContentsCounterWorker* m_worker; bool m_workerIsBusy; diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/src/kitemviews/private/kdirectorycontentscounterworker.cpp index e9c954ed9..3117d07aa 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.cpp +++ b/src/kitemviews/private/kdirectorycontentscounterworker.cpp @@ -22,44 +22,33 @@ // Required includes for subItemsCount(): #ifdef Q_OS_WIN - #include <QDir> +#include <QDir> #else - #include <QFile> - #include <qplatformdefs.h> +#include <QFile> +#include <qplatformdefs.h> #endif +#include "dolphin_detailsmodesettings.h" + KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) : QObject(parent) { qRegisterMetaType<KDirectoryContentsCounterWorker::Options>(); } -int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options) +KDirectoryContentsCounterWorker::CountResult walkDir(const QString &dirPath, + const bool countHiddenFiles, + const bool countDirectoriesOnly, + QT_DIRENT *dirEntry, + const uint allowedRecursiveLevel) { - const bool countHiddenFiles = options & CountHiddenFiles; - const bool countDirectoriesOnly = options & CountDirectoriesOnly; - -#ifdef Q_OS_WIN - QDir dir(path); - QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System; - if (countHiddenFiles) { - filters |= QDir::Hidden; - } - if (countDirectoriesOnly) { - filters |= QDir::Dirs; - } else { - filters |= QDir::AllEntries; - } - return dir.entryList(filters).count(); -#else - // Taken from kio/src/widgets/kdirmodel.cpp - // Copyright (C) 2006 David Faure <[email protected]> - int count = -1; - auto dir = QT_OPENDIR(QFile::encodeName(path)); + long size = -1; + auto dir = QT_OPENDIR(QFile::encodeName(dirPath)); if (dir) { count = 0; - QT_DIRENT *dirEntry = nullptr; + QT_STATBUF buf; + while ((dirEntry = QT_READDIR(dir))) { if (dirEntry->d_name[0] == '.') { if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) { @@ -76,20 +65,69 @@ int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options // as directory instead of trying to do an expensive stat() // (see bugs 292642 and 299997). const bool countEntry = !countDirectoriesOnly || - dirEntry->d_type == DT_DIR || - dirEntry->d_type == DT_LNK || - dirEntry->d_type == DT_UNKNOWN; + dirEntry->d_type == DT_DIR || + dirEntry->d_type == DT_LNK || + dirEntry->d_type == DT_UNKNOWN; if (countEntry) { ++count; } + + if (allowedRecursiveLevel > 0) { + + bool linkFound = false; + QString nameBuf = QStringLiteral("%1/%2").arg(dirPath, dirEntry->d_name); + + if (dirEntry->d_type == DT_REG || dirEntry->d_type == DT_LNK) { + if (QT_STAT(nameBuf.toLocal8Bit(), &buf) == 0) { + if (S_ISDIR(buf.st_mode)) { + // was a dir link, recurse + linkFound = true; + } + size += buf.st_size; + } + } + if (dirEntry->d_type == DT_DIR || linkFound) { + // recursion for dirs and dir links + size += walkDir(nameBuf, countHiddenFiles, countDirectoriesOnly, dirEntry, allowedRecursiveLevel - 1).size; + } + } } QT_CLOSEDIR(dir); } - return count; + return KDirectoryContentsCounterWorker::CountResult{count, size}; +} + +KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options) +{ + const bool countHiddenFiles = options & CountHiddenFiles; + const bool countDirectoriesOnly = options & CountDirectoriesOnly; + +#ifdef Q_OS_WIN + QDir dir(path); + QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System; + if (countHiddenFiles) { + filters |= QDir::Hidden; + } + if (countDirectoriesOnly) { + filters |= QDir::Dirs; + } else { + filters |= QDir::AllEntries; + } + return {dir.entryList(filters).count(), 0}; +#else + + const uint maxRecursiveLevel = DetailsModeSettings::directorySizeCount() ? 1 : DetailsModeSettings::recursiveDirectorySizeLimit(); + + QT_DIRENT *dirEntry = nullptr; + + auto res = walkDir(QFile::encodeName(path), countHiddenFiles, countDirectoriesOnly, dirEntry, maxRecursiveLevel); + + return res; #endif } void KDirectoryContentsCounterWorker::countDirectoryContents(const QString& path, Options options) { - emit result(path, subItemsCount(path, options)); + auto res = subItemsCount(path, options); + emit result(path, res.count, res.size); } diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.h b/src/kitemviews/private/kdirectorycontentscounterworker.h index b40da6e87..fac9978d5 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.h +++ b/src/kitemviews/private/kdirectorycontentscounterworker.h @@ -37,6 +37,14 @@ public: }; Q_DECLARE_FLAGS(Options, Option) + struct CountResult { + /// number of elements in the directory + int count; + /// Recursive sum of the size of the directory content files and folders + /// Calculation depends on DetailsModeSettings::recursiveDirectorySizeLimit + long size; + }; + explicit KDirectoryContentsCounterWorker(QObject* parent = nullptr); /** @@ -45,13 +53,13 @@ public: * * @return The number of items. */ - static int subItemsCount(const QString& path, Options options); + static CountResult subItemsCount(const QString& path, Options options); signals: /** - * Signals that the directory \a path contains \a count items. + * Signals that the directory \a path contains \a count items and optionally the size of its content. */ - void result(const QString& path, int count); + void result(const QString& path, int count, long size); public slots: /** |
