diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -21,6 +21,8 @@ #include "kfileitemmodel.h" #include "kitemlistview.h" +#include "dolphin_detailsmodesettings.h" + #include #include @@ -67,11 +69,17 @@ // The item represents a directory. Show the number of sub directories // instead of the file size of the directory. if (!roleValue.isNull()) { - const int count = roleValue.toInt(); + const int count = values.value("count").toInt(); if (count < 0) { text = i18nc("@item:intable", "Unknown"); } else { - text = i18ncp("@item:intable", "%1 item", "%1 items", count); + if (DetailsModeSettings::recursiveDirectorySizeLimit() > 0) { + const KIO::filesize_t size = roleValue.value(); + auto sizeText = KFormat().formatByteSize(size); + text = i18ncp("@item:intable", "%1 item, %2", "%1 items, %2", count, sizeText); + } else { + text = i18ncp("@item:intable", "%1 item", "%1 items", count); + } } } } else { diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -22,6 +22,7 @@ #include "kfileitemmodel.h" #include "dolphin_generalsettings.h" +#include "dolphin_detailsmodesettings.h" #include "dolphindebug.h" #include "private/kfileitemmodeldirlister.h" #include "private/kfileitemmodelsortalgorithm.h" @@ -1756,16 +1757,27 @@ // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan(): Q_ASSERT(itemB.isDir()); - const QVariant valueA = a->values.value("size"); - const QVariant valueB = b->values.value("size"); + QVariant valueA, valueB; + if (DetailsModeSettings::recursiveDirectorySizeLimit() > 0) { + // use dir size then + valueA = a->values.value("size"); + valueB = b->values.value("size"); + } else { + valueA = a->values.value("count"); + valueB = b->values.value("count"); + } if (valueA.isNull() && valueB.isNull()) { result = 0; } else if (valueA.isNull()) { result = -1; } else if (valueB.isNull()) { result = +1; } else { - result = valueA.toInt() - valueB.toInt(); + if (valueA < valueB) { + return -1; + } else { + return +1; + } } } else { // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan(): diff --git a/src/kitemviews/kfileitemmodelrolesupdater.h b/src/kitemviews/kfileitemmodelrolesupdater.h --- a/src/kitemviews/kfileitemmodelrolesupdater.h +++ b/src/kitemviews/kfileitemmodelrolesupdater.h @@ -212,7 +212,7 @@ void applyChangedBalooRoles(const QString& file); void applyChangedBalooRolesForItem(const KFileItem& file); - void slotDirectoryContentsCountReceived(const QString& path, int count); + void slotDirectoryContentsCountReceived(const QString& path, int count, long size); private: /** diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -750,7 +750,7 @@ #endif } -void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count) +void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count, long size) { const bool getSizeRole = m_roles.contains("size"); const bool getIsExpandableRole = m_roles.contains("isExpandable"); @@ -761,7 +761,8 @@ QHash data; if (getSizeRole) { - data.insert("size", count); + data.insert("count", count); + data.insert("size", QVariant::fromValue(size)); } if (getIsExpandableRole) { data.insert("isExpandable", count > 0); @@ -997,7 +998,9 @@ data.insert("type", item.mimeComment()); } else if (m_model->sortRole() == "size" && item.isLocalFile() && item.isDir()) { const QString path = item.localPath(); - data.insert("size", m_directoryContentsCounter->countDirectoryContentsSynchronously(path)); + auto res = m_directoryContentsCounter->countDirectoryContentsSynchronously(path); + data.insert("size", QVariant::fromValue(res.size)); + data.insert("count", res.count); } else { // Probably the sort role is a baloo role - just determine all roles. data = rolesData(item); diff --git a/src/kitemviews/private/kdirectorycontentscounter.h b/src/kitemviews/private/kdirectorycontentscounter.h --- a/src/kitemviews/private/kdirectorycontentscounter.h +++ b/src/kitemviews/private/kdirectorycontentscounter.h @@ -55,18 +55,19 @@ * The directory is watched for changes, and the signal \a result is * emitted if a change occurs. */ - int countDirectoryContentsSynchronously(const QString& path); + KDirectoryContentsCounterWorker::CountResult countDirectoryContentsSynchronously(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(); diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp --- a/src/kitemviews/private/kdirectorycontentscounter.cpp +++ b/src/kitemviews/private/kdirectorycontentscounter.cpp @@ -57,9 +57,7 @@ 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. @@ -83,7 +81,7 @@ startWorker(path); } -int KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path) +KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path) { if (!m_dirWatcher->contains(path)) { m_dirWatcher->addDir(path); @@ -103,7 +101,7 @@ 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; @@ -116,7 +114,7 @@ startWorker(m_queue.dequeue()); } - emit result(path, count); + emit result(path, count, size); } void KDirectoryContentsCounter::slotDirWatchDirty(const QString& path) diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.h b/src/kitemviews/private/kdirectorycontentscounterworker.h --- a/src/kitemviews/private/kdirectorycontentscounterworker.h +++ b/src/kitemviews/private/kdirectorycontentscounterworker.h @@ -37,21 +37,29 @@ }; Q_DECLARE_FLAGS(Options, Option) + struct CountResult{ + /// nb 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); /** * Counts the items inside the directory \a path using the options * \a options. * * @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 optinnaly the size of its content. */ - void result(const QString& path, int count); + void result(const QString& path, int count, long size); public slots: /** diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/src/kitemviews/private/kdirectorycontentscounterworker.cpp --- a/src/kitemviews/private/kdirectorycontentscounterworker.cpp +++ b/src/kitemviews/private/kdirectorycontentscounterworker.cpp @@ -22,44 +22,34 @@ // Required includes for subItemsCount(): #ifdef Q_OS_WIN - #include +#include #else - #include - #include +#include +#include #endif +#include "dolphin_detailsmodesettings.h" + KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) : QObject(parent) { qRegisterMetaType(); } -int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options) +KDirectoryContentsCounterWorker::CountResult walkDir(const char* 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 - int count = -1; - auto dir = QT_OPENDIR(QFile::encodeName(path)); + long size = 0; + auto dir = QT_OPENDIR(dirPath); if (dir) { count = 0; - QT_DIRENT *dirEntry = nullptr; + QT_STATBUF buf; + char name_buf[256]; + bool linkFound = false; while ((dirEntry = QT_READDIR(dir))) { if (dirEntry->d_name[0] == '.') { if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) { @@ -76,20 +66,69 @@ // 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) { + sprintf(name_buf, "%s/%s", dirPath, dirEntry->d_name); + + if (dirEntry->d_type == DT_REG || dirEntry->d_type == DT_LNK) { + if (QT_STAT(name_buf, &buf) == 0) { + if (S_ISDIR(buf.st_mode)) { + // was a dir link, recurse + linkFound = true; + } else { + size += buf.st_size; + } + } + } + if (dirEntry->d_type == DT_DIR || linkFound) { + // recursion for dirs and dir links + size += walkDir(name_buf, 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(); +#else + + const uint maxRecursiveLevel = DetailsModeSettings::recursiveDirectorySizeLimit(); + QByteArray dirPath = QFile::encodeName(path); + + QT_DIRENT *dirEntry = nullptr; + + auto res = walkDir(dirPath.constData(), 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/settings/dolphin_detailsmodesettings.kcfg b/src/settings/dolphin_detailsmodesettings.kcfg --- a/src/settings/dolphin_detailsmodesettings.kcfg +++ b/src/settings/dolphin_detailsmodesettings.kcfg @@ -44,5 +44,9 @@ true + + + 0 + diff --git a/src/settings/viewmodes/viewsettingstab.h b/src/settings/viewmodes/viewsettingstab.h --- a/src/settings/viewmodes/viewsettingstab.h +++ b/src/settings/viewmodes/viewsettingstab.h @@ -28,6 +28,7 @@ class QComboBox; class QCheckBox; class QSlider; +class QSpinBox; /** * @brief Represents one tab of the view-settings page. @@ -72,6 +73,7 @@ QComboBox* m_widthBox; QComboBox* m_maxLinesBox; QCheckBox* m_expandableFolders; + QSpinBox* m_recursiveDirectorySizeLimit; }; #endif diff --git a/src/settings/viewmodes/viewsettingstab.cpp b/src/settings/viewmodes/viewsettingstab.cpp --- a/src/settings/viewmodes/viewsettingstab.cpp +++ b/src/settings/viewmodes/viewsettingstab.cpp @@ -33,6 +33,7 @@ #include #include #include +#include ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) : QWidget(parent), @@ -42,11 +43,11 @@ m_fontRequester(nullptr), m_widthBox(nullptr), m_maxLinesBox(nullptr), - m_expandableFolders(nullptr) + m_expandableFolders(nullptr), + m_recursiveDirectorySizeLimit(nullptr) { QFormLayout* topLayout = new QFormLayout(this); - // Create "Icon Size" section const int minRange = ZoomLevelInfo::minimumLevel(); const int maxRange = ZoomLevelInfo::maximumLevel(); @@ -107,6 +108,11 @@ case DetailsMode: m_expandableFolders = new QCheckBox(i18nc("@option:check", "Expandable")); topLayout->addRow(i18nc("@label:checkbox", "Folders:"), m_expandableFolders); + + m_recursiveDirectorySizeLimit = new QSpinBox(); + m_recursiveDirectorySizeLimit->setRange(0, 20); + m_recursiveDirectorySizeLimit->setSingleStep(1); + topLayout->addRow(i18nc("@label:spinbox", "Number of allowed recursion to compute directory size:"), m_recursiveDirectorySizeLimit); break; default: break; @@ -128,6 +134,7 @@ break; case DetailsMode: connect(m_expandableFolders, &QCheckBox::toggled, this, &ViewSettingsTab::changed); + connect(m_recursiveDirectorySizeLimit, QOverload::of(&QSpinBox::valueChanged), this, &ViewSettingsTab::changed); break; default: break; @@ -153,6 +160,7 @@ break; case DetailsMode: DetailsModeSettings::setExpandableFolders(m_expandableFolders->isChecked()); + DetailsModeSettings::setRecursiveDirectorySizeLimit(m_recursiveDirectorySizeLimit->value()); break; default: break; @@ -201,6 +209,7 @@ break; case DetailsMode: m_expandableFolders->setChecked(DetailsModeSettings::expandableFolders()); + m_recursiveDirectorySizeLimit->setValue(DetailsModeSettings::recursiveDirectorySizeLimit()); break; default: break;