From 629a5d44ddd89defb86c7fe9025735d00d8f0738 Mon Sep 17 00:00:00 2001 From: Méven Car Date: Wed, 10 May 2023 16:41:30 +0200 Subject: Add a TODO KF6 to solve bug 464722 CCBUG: 464722 --- src/kitemviews/kfileitemmodel.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/kitemviews/kfileitemmodel.cpp') diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index ebcd4b912..0d61775ad 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -1829,6 +1829,7 @@ QHash KFileItemModel::retrieveData(const KFileItem &item, } if (m_requestRole[ExtensionRole] && !isDir) { + // TODO KF6 use KFileItem::suffix 464722 data.insert(sharedValue("extension"), QFileInfo(item.name()).suffix()); } -- cgit v1.3 From a1c5c5cf81b5d1f6b7a0aa10b8a981cb70c5b26c Mon Sep 17 00:00:00 2001 From: Méven Car Date: Fri, 26 May 2023 15:10:09 +0000 Subject: KDirectoryContentsCounter: show intermediate dir size counting results, improve stopping, improve data caching Two user visible changes: * we can see the dir size changing as it is updated. * This makes the file counting a lot more reactive, when changing directories for instance. `KDirectoryContentsCounterWorker::walkDir` is not recursive anymore. The cache is now shared and only a single thread is used to count size recursively. --- src/kitemviews/kfileitemlistview.cpp | 15 -- src/kitemviews/kfileitemlistview.h | 8 - src/kitemviews/kfileitemlistwidget.cpp | 2 +- src/kitemviews/kfileitemmodel.cpp | 2 + src/kitemviews/kfileitemmodel.h | 5 + src/kitemviews/kfileitemmodelrolesupdater.cpp | 88 +++++-- src/kitemviews/kfileitemmodelrolesupdater.h | 6 +- .../private/kdirectorycontentscounter.cpp | 283 ++++++++++++++------- src/kitemviews/private/kdirectorycontentscounter.h | 16 +- .../private/kdirectorycontentscounterworker.cpp | 187 ++++++++------ .../private/kdirectorycontentscounterworker.h | 33 +-- src/panels/folders/folderspanel.cpp | 1 - 12 files changed, 394 insertions(+), 252 deletions(-) (limited to 'src/kitemviews/kfileitemmodel.cpp') diff --git a/src/kitemviews/kfileitemlistview.cpp b/src/kitemviews/kfileitemlistview.cpp index e2a27a5ea..668ebdfb2 100644 --- a/src/kitemviews/kfileitemlistview.cpp +++ b/src/kitemviews/kfileitemlistview.cpp @@ -41,7 +41,6 @@ KFileItemListView::KFileItemListView(QGraphicsWidget *parent) , m_modelRolesUpdater(nullptr) , m_updateVisibleIndexRangeTimer(nullptr) , m_updateIconSizeTimer(nullptr) - , m_scanDirectories(true) { setAcceptDrops(true); @@ -119,19 +118,6 @@ qlonglong KFileItemListView::localFileSizePreviewLimit() const return m_modelRolesUpdater ? m_modelRolesUpdater->localFileSizePreviewLimit() : 0; } -void KFileItemListView::setScanDirectories(bool enabled) -{ - m_scanDirectories = enabled; - if (m_modelRolesUpdater) { - m_modelRolesUpdater->setScanDirectories(m_scanDirectories); - } -} - -bool KFileItemListView::scanDirectories() -{ - return m_scanDirectories; -} - QPixmap KFileItemListView::createDragPixmap(const KItemSet &indexes) const { if (!model()) { @@ -269,7 +255,6 @@ void KFileItemListView::onModelChanged(KItemModelBase *current, KItemModelBase * if (current) { m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast(current), this); m_modelRolesUpdater->setIconSize(availableIconSize()); - m_modelRolesUpdater->setScanDirectories(scanDirectories()); applyRolesToModel(); } diff --git a/src/kitemviews/kfileitemlistview.h b/src/kitemviews/kfileitemlistview.h index 63bcf9e75..b4be0093e 100644 --- a/src/kitemviews/kfileitemlistview.h +++ b/src/kitemviews/kfileitemlistview.h @@ -71,13 +71,6 @@ public: void setLocalFileSizePreviewLimit(qlonglong size); qlonglong localFileSizePreviewLimit() const; - /** - * If set to true, directories contents are scanned to determine their size - * Default true - */ - void setScanDirectories(bool enabled); - bool scanDirectories(); - QPixmap createDragPixmap(const KItemSet &indexes) const override; /** @@ -137,7 +130,6 @@ private: KFileItemModelRolesUpdater *m_modelRolesUpdater; QTimer *m_updateVisibleIndexRangeTimer; QTimer *m_updateIconSizeTimer; - bool m_scanDirectories; friend class KFileItemListViewTest; // For unit testing }; diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp index d9644bef5..ec8683345 100644 --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -67,7 +67,7 @@ QString KFileItemListWidgetInformant::roleText(const QByteArray &role, const QHa if (values.value("isDir").toBool()) { if (!roleValue.isNull() && roleValue != -1) { // The item represents a directory. - if (DetailsModeSettings::directorySizeCount()) { + if (DetailsModeSettings::directorySizeCount() || roleValue == -2 /* size is invalid */) { // Show the number of sub directories instead of the file size of the directory. const int count = values.value("count").toInt(); text = i18ncp("@item:intable", "%1 item", "%1 items", count); diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 0d61775ad..6617fbec6 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -129,6 +129,8 @@ void KFileItemModel::refreshDirectory(const QUrl &url) } m_dirLister->openUrl(url, KDirLister::Reload); + + Q_EMIT directoryRefreshing(); } QUrl KFileItemModel::directory() const diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index afcd633b0..721569a0c 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -217,6 +217,11 @@ Q_SIGNALS: */ void directoryLoadingCompleted(); + /** + * Is emitted when the model is being refreshed (F5 key press) + */ + void directoryRefreshing(); + /** * Is emitted after the loading of a directory has been canceled. */ diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index c488fc0cf..6838d0861 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -10,6 +10,7 @@ #include "kfileitemmodel.h" #include "private/kdirectorycontentscounter.h" #include "private/kpixmapmodifier.h" +#include "qdir.h" #include #include @@ -20,6 +21,8 @@ #include #include +#include "dolphin_detailsmodesettings.h" + #if HAVE_BALOO #include "private/kbaloorolesprovider.h" #include @@ -72,7 +75,6 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel *model, QO , m_resolvableRoles() , m_enabledPlugins() , m_localFileSizePreviewLimit(0) - , m_scanDirectories(true) , m_pendingSortRoleItems() , m_pendingIndexes() , m_pendingPreviewItems() @@ -321,16 +323,6 @@ qlonglong KFileItemModelRolesUpdater::localFileSizePreviewLimit() const return m_localFileSizePreviewLimit; } -void KFileItemModelRolesUpdater::setScanDirectories(bool enabled) -{ - m_scanDirectories = enabled; -} - -bool KFileItemModelRolesUpdater::scanDirectories() const -{ - return m_scanDirectories; -} - void KFileItemModelRolesUpdater::setHoverSequenceState(const QUrl &itemUrl, int seqIdx) { const KFileItem item = m_model->fileItem(itemUrl); @@ -423,8 +415,7 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList &itemRang m_hoverSequenceLoadedItems.clear(); killPreviewJob(); - - if (m_scanDirectories) { + if (!m_model->showDirectoriesOnly()) { m_directoryContentsCounter->stopWorker(); } } else { @@ -856,10 +847,10 @@ void KFileItemModelRolesUpdater::applyChangedBalooRolesForItem(const KFileItem & #endif } -void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString &path, int count, long size) +void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString &path, int count, long long size) { - const bool getSizeRole = m_roles.contains("size"); const bool getIsExpandableRole = m_roles.contains("isExpandable"); + const bool getSizeRole = m_roles.contains("size"); if (getSizeRole || getIsExpandableRole) { const int index = m_model->index(QUrl::fromLocalFile(path)); @@ -1278,18 +1269,71 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) void KFileItemModelRolesUpdater::startDirectorySizeCounting(const KFileItem &item, int index) { - if (item.isSlow()) { + if (DetailsModeSettings::directorySizeCount() || item.isSlow() || !item.isLocalFile()) { + // fastpath no recursion necessary + + auto data = m_model->data(index); + if (data.value("size") == -2) { + // means job already started + return; + } + + auto url = item.url(); + if (!item.localPath().isEmpty()) { + // optimization for desktop:/, trash:/ + url = QUrl::fromLocalFile(item.localPath()); + } + + data.insert("isExpandable", false); + data.insert("count", 0); + data.insert("size", -2); // invalid size, -1 means size unknown + + disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + m_model->setData(index, data); + connect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + + auto listJob = KIO::listDir(url); + QObject::connect(listJob, &KIO::ListJob::entries, this, [this, index](const KJob * /*job*/, const KIO::UDSEntryList &list) { + auto data = m_model->data(index); + int origCount = data.value("count").toInt(); + int entryCount = origCount; + + for (const KIO::UDSEntry &entry : list) { + const auto name = entry.stringValue(KIO::UDSEntry::UDS_NAME); + + if (name == QStringLiteral("..") || name == QStringLiteral(".")) { + continue; + } + if (!m_model->showHiddenFiles() && name.startsWith(QLatin1Char('.'))) { + continue; + } + if (m_model->showDirectoriesOnly() && !entry.isDir()) { + continue; + } + ++entryCount; + } + + // count has changed + if (origCount < entryCount) { + QHash data; + data.insert("isExpandable", entryCount > 0); + data.insert("count", entryCount); + + disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + m_model->setData(index, data); + connect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + } + }); return; } // Tell m_directoryContentsCounter that we want to count the items // inside the directory. The result will be received in slotDirectoryContentsCountReceived. - if (m_scanDirectories && item.isLocalFile()) { - const QString path = item.localPath(); - const auto priority = index >= m_firstVisibleIndex && index <= m_lastVisibleIndex ? KDirectoryContentsCounter::PathCountPriority::High - : KDirectoryContentsCounter::PathCountPriority::Normal; - m_directoryContentsCounter->scanDirectory(path, priority); - } + const QString path = item.localPath(); + const auto priority = index >= m_firstVisibleIndex && index <= m_lastVisibleIndex ? KDirectoryContentsCounter::PathCountPriority::High + : KDirectoryContentsCounter::PathCountPriority::Normal; + + m_directoryContentsCounter->scanDirectory(path, priority); } QHash KFileItemModelRolesUpdater::rolesData(const KFileItem &item, int index) diff --git a/src/kitemviews/kfileitemmodelrolesupdater.h b/src/kitemviews/kfileitemmodelrolesupdater.h index be6f23193..78fa757f8 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.h +++ b/src/kitemviews/kfileitemmodelrolesupdater.h @@ -260,7 +260,7 @@ private Q_SLOTS: void applyChangedBalooRoles(const QString &file); void applyChangedBalooRolesForItem(const KFileItem &file); - void slotDirectoryContentsCountReceived(const QString &path, int count, long size); + void slotDirectoryContentsCountReceived(const QString &path, int count, long long size); private: /** @@ -334,6 +334,9 @@ private: void trimHoverSequenceLoadedItems(); private: + /** + * enqueue directory size counting for KFileItem item at index + */ void startDirectorySizeCounting(const KFileItem &item, int index); enum State { Idle, Paused, ResolvingSortRole, ResolvingAllRoles, PreviewJobRunning }; @@ -370,7 +373,6 @@ private: QSet m_resolvableRoles; QStringList m_enabledPlugins; qulonglong m_localFileSizePreviewLimit; - bool m_scanDirectories; // Items for which the sort role still has to be determined. QSet m_pendingSortRoleItems; diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp index 37e852ab9..e1a47419d 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.cpp +++ b/src/kitemviews/private/kdirectorycontentscounter.cpp @@ -6,6 +6,7 @@ */ #include "kdirectorycontentscounter.h" +#include "dolphin_detailsmodesettings.h" #include "kitemviews/kfileitemmodel.h" #include @@ -16,36 +17,102 @@ namespace { + +class LocalCache +{ +public: + struct cacheData { + int count = 0; + long long size = 0; + ushort refCount = 0; + qint64 timestamp = 0; + + inline operator bool() const + { + return timestamp != 0 && count != -1; + } + }; + + LocalCache() + : m_cache() + { + } + + cacheData insert(const QString &key, cacheData data, bool inserted) + { + data.timestamp = QDateTime::currentMSecsSinceEpoch(); + if (inserted) { + data.refCount += 1; + } + m_cache.insert(key, data); + return data; + } + + cacheData value(const QString &key) const + { + return m_cache.value(key); + } + + void unRefAll(const QSet &keys) + { + for (const auto &key : keys) { + auto entry = m_cache[key]; + entry.refCount -= 1; + if (entry.refCount == 0) { + m_cache.remove(key); + } + } + } + + void removeAll(const QSet &keys) + { + for (const auto &key : keys) { + m_cache.remove(key); + } + } + +private: + QHash m_cache; +}; + /// cache of directory counting result -static QHash> *s_cache; +static LocalCache *s_cache; +static QThread *s_workerThread; +static KDirectoryContentsCounterWorker *s_worker; } KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel *model, QObject *parent) : QObject(parent) , m_model(model) + , m_priorityQueue() , m_queue() - , m_worker(nullptr) , m_workerIsBusy(false) , m_dirWatcher(nullptr) , m_watchedDirs() { - connect(m_model, &KFileItemModel::itemsRemoved, this, &KDirectoryContentsCounter::slotItemsRemoved); + if (s_cache == nullptr) { + s_cache = new LocalCache(); + } - if (!m_workerThread) { - m_workerThread = new QThread(); - m_workerThread->start(); + if (!s_workerThread) { + s_workerThread = new QThread(); + s_workerThread->setObjectName(QStringLiteral("KDirectoryContentsCounterThread")); + s_workerThread->start(); } - if (s_cache == nullptr) { - s_cache = new QHash>(); + if (!s_worker) { + s_worker = new KDirectoryContentsCounterWorker(); + s_worker->moveToThread(s_workerThread); } - m_worker = new KDirectoryContentsCounterWorker(); - m_worker->moveToThread(m_workerThread); + connect(m_model, &KFileItemModel::itemsRemoved, this, &KDirectoryContentsCounter::slotItemsRemoved); + connect(m_model, &KFileItemModel::directoryRefreshing, this, &KDirectoryContentsCounter::slotDirectoryRefreshing); + + connect(this, &KDirectoryContentsCounter::requestDirectoryContentsCount, s_worker, &KDirectoryContentsCounterWorker::countDirectoryContents); - connect(this, &KDirectoryContentsCounter::requestDirectoryContentsCount, m_worker, &KDirectoryContentsCounterWorker::countDirectoryContents); - connect(this, &KDirectoryContentsCounter::stop, m_worker, &KDirectoryContentsCounterWorker::stop); - connect(m_worker, &KDirectoryContentsCounterWorker::result, this, &KDirectoryContentsCounter::slotResult); + connect(s_worker, &KDirectoryContentsCounterWorker::result, this, &KDirectoryContentsCounter::slotResult); + connect(s_worker, &KDirectoryContentsCounterWorker::intermediateResult, this, &KDirectoryContentsCounter::result); + connect(s_worker, &KDirectoryContentsCounterWorker::finished, this, &KDirectoryContentsCounter::scheduleNext); m_dirWatcher = new KDirWatch(this); connect(m_dirWatcher, &KDirWatch::dirty, this, &KDirectoryContentsCounter::slotDirWatchDirty); @@ -53,60 +120,20 @@ KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel *model, QObj KDirectoryContentsCounter::~KDirectoryContentsCounter() { - 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_worker->deleteLater(); - } else { - // There are no remaining workers -> stop the worker thread. - m_workerThread->quit(); - m_workerThread->wait(); - delete m_workerThread; - 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 - // which would deliver the event to m_worker is not running any more. - delete m_worker; - } + s_cache->unRefAll(m_watchedDirs); } -void KDirectoryContentsCounter::slotResult(const QString &path, int count, long size) +void KDirectoryContentsCounter::slotResult(const QString &path, int count, long long size) { - m_workerIsBusy = false; - - const QFileInfo info = QFileInfo(path); - const QString resolvedPath = info.canonicalFilePath(); - - if (!m_dirWatcher->contains(resolvedPath)) { + const auto fileInfo = QFileInfo(path); + const QString resolvedPath = fileInfo.canonicalFilePath(); + if (fileInfo.isReadable() && !m_watchedDirs.contains(resolvedPath)) { m_dirWatcher->addDir(resolvedPath); - m_watchedDirs.insert(resolvedPath); - } - - if (!m_priorityQueue.empty()) { - const QString firstPath = m_priorityQueue.front(); - m_priorityQueue.pop_front(); - scanDirectory(firstPath, PathCountPriority::High); - } else if (!m_queue.empty()) { - const QString firstPath = m_queue.front(); - m_queue.pop_front(); - scanDirectory(firstPath, PathCountPriority::Normal); } + bool inserted = m_watchedDirs.insert(resolvedPath) == m_watchedDirs.end(); - 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(count, size)); - } + // update cache or overwrite value + s_cache->insert(resolvedPath, {count, size, true}, inserted); // sends the results Q_EMIT result(path, count, size); @@ -131,6 +158,11 @@ void KDirectoryContentsCounter::slotItemsRemoved() { const bool allItemsRemoved = (m_model->count() == 0); + if (allItemsRemoved) { + s_cache->removeAll(m_watchedDirs); + stopWorker(); + } + if (!m_watchedDirs.isEmpty()) { // Don't let KDirWatch watch for removed items if (allItemsRemoved) { @@ -138,7 +170,6 @@ void KDirectoryContentsCounter::slotItemsRemoved() m_dirWatcher->removeDir(path); } m_watchedDirs.clear(); - m_queue.clear(); } else { QMutableSetIterator it(m_watchedDirs); while (it.hasNext()) { @@ -152,46 +183,102 @@ void KDirectoryContentsCounter::slotItemsRemoved() } } -void KDirectoryContentsCounter::scanDirectory(const QString &path, PathCountPriority priority) +void KDirectoryContentsCounter::slotDirectoryRefreshing() +{ + s_cache->removeAll(m_watchedDirs); +} + +void KDirectoryContentsCounter::scheduleNext() { - const QString resolvedPath = QFileInfo(path).canonicalFilePath(); - const bool alreadyInCache = s_cache->contains(resolvedPath); - if (alreadyInCache) { + if (!m_priorityQueue.empty()) { + m_currentPath = m_priorityQueue.front(); + m_priorityQueue.pop_front(); + } else if (!m_queue.empty()) { + m_currentPath = m_queue.front(); + m_queue.pop_front(); + } else { + m_currentPath.clear(); + m_workerIsBusy = false; + return; + } + + const auto fileInfo = QFileInfo(m_currentPath); + const QString resolvedPath = fileInfo.canonicalFilePath(); + const auto pair = s_cache->value(resolvedPath); + if (pair) { // fast path when in cache // will be updated later if result has changed - const auto pair = s_cache->value(resolvedPath); - Q_EMIT result(path, pair.first, pair.second); - } - - if (m_workerIsBusy) { - // only enqueue path not yet in queue - if (std::find(m_queue.begin(), m_queue.end(), path) == m_queue.end() - && std::find(m_priorityQueue.begin(), m_priorityQueue.end(), path) == m_priorityQueue.end()) { - if (priority == PathCountPriority::Normal) { - if (alreadyInCache) { - // if we already knew the dir size, it gets lower priority - m_queue.push_back(path); - } else { - m_queue.push_front(path); - } - } else { - // append to priority queue - m_priorityQueue.push_back(path); - } - } - } else { - KDirectoryContentsCounterWorker::Options options; + Q_EMIT result(m_currentPath, pair.count, pair.size); + } + + // if scanned fully recently, skip rescan + if (pair && pair.timestamp >= fileInfo.fileTime(QFile::FileModificationTime).toMSecsSinceEpoch()) { + scheduleNext(); + return; + } + + KDirectoryContentsCounterWorker::Options options; + + if (m_model->showHiddenFiles()) { + options |= KDirectoryContentsCounterWorker::CountHiddenFiles; + } + + m_workerIsBusy = true; + Q_EMIT requestDirectoryContentsCount(m_currentPath, options, DetailsModeSettings::recursiveDirectorySizeLimit()); +} - if (m_model->showHiddenFiles()) { - options |= KDirectoryContentsCounterWorker::CountHiddenFiles; +void KDirectoryContentsCounter::enqueuePathScanning(const QString &path, bool alreadyInCache, PathCountPriority priority) +{ + // ensure to update the entry in the queue + auto it = std::find(m_queue.begin(), m_queue.end(), path); + if (it != m_queue.end()) { + m_queue.erase(it); + } else { + it = std::find(m_priorityQueue.begin(), m_priorityQueue.end(), path); + if (it != m_priorityQueue.end()) { + m_priorityQueue.erase(it); } + } - if (m_model->showDirectoriesOnly()) { - options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly; + if (priority == PathCountPriority::Normal) { + if (alreadyInCache) { + // we already knew the dir size + // otherwise it gets lower priority + m_queue.push_back(path); + } else { + m_queue.push_front(path); } + } else { + // append to priority queue + m_priorityQueue.push_front(path); + } +} + +void KDirectoryContentsCounter::scanDirectory(const QString &path, PathCountPriority priority) +{ + if (m_workerIsBusy && m_currentPath == path) { + // already listing + return; + } + + const auto fileInfo = QFileInfo(path); + const QString resolvedPath = fileInfo.canonicalFilePath(); + const auto pair = s_cache->value(resolvedPath); + if (pair) { + // fast path when in cache + // will be updated later if result has changed + Q_EMIT result(path, pair.count, pair.size); + } - Q_EMIT requestDirectoryContentsCount(path, options); - m_workerIsBusy = true; + // if scanned fully recently, skip rescan + if (pair && pair.timestamp >= fileInfo.fileTime(QFile::FileModificationTime).toMSecsSinceEpoch()) { + return; + } + + enqueuePathScanning(path, pair, priority); + + if (!m_workerIsBusy && !s_worker->stopping()) { + scheduleNext(); } } @@ -199,7 +286,9 @@ void KDirectoryContentsCounter::stopWorker() { m_queue.clear(); m_priorityQueue.clear(); - Q_EMIT stop(); -} -QThread *KDirectoryContentsCounter::m_workerThread = nullptr; + if (m_workerIsBusy && m_currentPath == s_worker->scannedPath()) { + s_worker->stop(); + } + m_currentPath.clear(); +} diff --git a/src/kitemviews/private/kdirectorycontentscounter.h b/src/kitemviews/private/kdirectorycontentscounter.h index 9a5e4a86b..552b5560e 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.h +++ b/src/kitemviews/private/kdirectorycontentscounter.h @@ -49,32 +49,32 @@ Q_SIGNALS: * 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, long size); + void result(const QString &path, int count, long long size); - void requestDirectoryContentsCount(const QString &path, KDirectoryContentsCounterWorker::Options options); - - void stop(); + void requestDirectoryContentsCount(const QString &path, KDirectoryContentsCounterWorker::Options options, int maxRecursiveLevel); private Q_SLOTS: - void slotResult(const QString &path, int count, long size); + void slotResult(const QString &path, int count, long long size); void slotDirWatchDirty(const QString &path); void slotItemsRemoved(); + void slotDirectoryRefreshing(); + void scheduleNext(); private: + void enqueuePathScanning(const QString &path, bool alreadyInCache, PathCountPriority priority); + KFileItemModel *m_model; // Used as FIFO queues. std::list m_priorityQueue; std::list m_queue; - static QThread *m_workerThread; - - KDirectoryContentsCounterWorker *m_worker; bool m_workerIsBusy; KDirWatch *m_dirWatcher; QSet m_watchedDirs; // Required as sadly KDirWatch does not offer a getter method // to get all watched directories. + QString m_currentPath; }; #endif diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/src/kitemviews/private/kdirectorycontentscounterworker.cpp index 2227ebbc2..eb456da25 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.cpp +++ b/src/kitemviews/private/kdirectorycontentscounterworker.cpp @@ -7,16 +7,16 @@ #include "kdirectorycontentscounterworker.h" -// Required includes for subItemsCount(): +// Required includes for countDirectoryContents(): #ifdef Q_OS_WIN #include #else -#include -#include +#include +#include +#include +#include #endif -#include "dolphin_detailsmodesettings.h" - KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent) : QObject(parent) { @@ -24,100 +24,135 @@ KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent } #ifndef Q_OS_WIN -KDirectoryContentsCounterWorker::CountResult -KDirectoryContentsCounterWorker::walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDirectoriesOnly, const uint allowedRecursiveLevel) +void KDirectoryContentsCounterWorker::walkDir(const QString &dirPath, bool countHiddenFiles, uint allowedRecursiveLevel) { - int count = -1; - long size = -1; - auto dir = QT_OPENDIR(QFile::encodeName(dirPath)); - if (dir) { - count = 0; - size = 0; - QT_DIRENT *dirEntry; - QT_STATBUF buf; - - while (!m_stopping && (dirEntry = QT_READDIR(dir))) { - if (dirEntry->d_name[0] == '.') { - if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) { - // Skip "." or hidden files - continue; - } - if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') { - // Skip ".." - continue; - } + QByteArray text = dirPath.toLocal8Bit(); + char *rootPath = new char[text.size() + 1]; + ::strncpy(rootPath, text.constData(), text.size() + 1); + char *path[2]{rootPath, nullptr}; + + // follow symlink only for root dir + auto tree = ::fts_open(path, FTS_COMFOLLOW | FTS_PHYSICAL | FTS_XDEV, nullptr); + if (!tree) { + delete[] rootPath; + return; + } + + FTSENT *node; + long long totalSize = -1; + int totalCount = -1; + QElapsedTimer timer; + timer.start(); + + while ((node = fts_read(tree)) && !m_stopping) { + auto info = node->fts_info; + + if (info == FTS_DC) { + // ignore directories clausing cycles + continue; + } + if (info == FTS_DNR) { + // ignore directories that can’t be read + continue; + } + if (info == FTS_ERR) { + // ignore directories causing errors + fts_set(tree, node, FTS_SKIP); + continue; + } + if (info == FTS_DP) { + // ignore end traversal of dir + continue; + } + + if (!countHiddenFiles && node->fts_name[0] == '.' && strncmp(".git", node->fts_name, 4) != 0) { + // skip hidden files, except .git dirs + if (info == FTS_D) { + fts_set(tree, node, FTS_SKIP); + } + continue; + } + + if (info == FTS_F) { + // only count files that are physical (aka skip /proc/kcore...) + // naive size counting not taking into account effective disk space used (aka size/block_size * block_size) + // skip directory size (usually a 4KB block) + if (node->fts_statp->st_blocks > 0) { + totalSize += node->fts_statp->st_size; } + } - // If only directories are counted, consider an unknown file type and links also - // 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; - if (countEntry) { - ++count; + if (info == FTS_D) { + if (node->fts_level == 0) { + // first read was sucessful, we can init counters + totalSize = 0; + totalCount = 0; } - if (allowedRecursiveLevel > 0) { - QString nameBuf = QStringLiteral("%1/%2").arg(dirPath, dirEntry->d_name); - - if (dirEntry->d_type == DT_REG) { - if (QT_STAT(nameBuf.toLocal8Bit(), &buf) == 0) { - size += buf.st_size; - } - } - if (!m_stopping && dirEntry->d_type == DT_DIR) { - // recursion for dirs - auto subdirResult = walkDir(nameBuf, countHiddenFiles, countDirectoriesOnly, allowedRecursiveLevel - 1); - if (subdirResult.size > 0) { - size += subdirResult.size; - } - } + if (node->fts_level > (int)allowedRecursiveLevel) { + // skip too deep nodes + fts_set(tree, node, FTS_SKIP); + continue; } } - QT_CLOSEDIR(dir); + // count first level elements + if (node->fts_level == 1) { + ++totalCount; + } + + // delay intermediate results + if (timer.hasExpired(200) || node->fts_level == 0) { + Q_EMIT intermediateResult(dirPath, totalCount, totalSize); + timer.restart(); + } + } + + delete[] rootPath; + fts_close(tree); + if (errno != 0) { + return; + } + + if (!m_stopping) { + Q_EMIT result(dirPath, totalCount, totalSize); } - return KDirectoryContentsCounterWorker::CountResult{count, size}; } #endif -KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounterWorker::subItemsCount(const QString &path, Options options) +void KDirectoryContentsCounterWorker::stop() +{ + m_stopping = true; +} + +bool KDirectoryContentsCounterWorker::stopping() const +{ + return m_stopping; +} + +QString KDirectoryContentsCounterWorker::scannedPath() const +{ + return m_scannedPath; +} + +void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options, int maxRecursiveLevel) { const bool countHiddenFiles = options & CountHiddenFiles; - const bool countDirectoriesOnly = options & CountDirectoriesOnly; #ifdef Q_OS_WIN QDir dir(path); - QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System; + QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System | QDir::AllEntries; if (countHiddenFiles) { filters |= QDir::Hidden; } - if (countDirectoriesOnly) { - filters |= QDir::Dirs; - } else { - filters |= QDir::AllEntries; - } - return {static_cast(dir.entryList(filters).count()), 0}; -#else - const uint maxRecursiveLevel = DetailsModeSettings::directorySizeCount() ? 1 : DetailsModeSettings::recursiveDirectorySizeLimit(); + Q_EMIT result(path, static_cast(dir.entryList(filters).count()), 0); +#else - auto res = walkDir(path, countHiddenFiles, countDirectoriesOnly, maxRecursiveLevel); + m_scannedPath = path; + walkDir(path, countHiddenFiles, maxRecursiveLevel); - return res; #endif -} - -void KDirectoryContentsCounterWorker::stop() -{ - m_stopping = true; -} -void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options) -{ m_stopping = false; - - auto res = subItemsCount(path, options); - - if (!m_stopping) { - Q_EMIT result(path, res.count, res.size); - } + Q_EMIT finished(); } diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.h b/src/kitemviews/private/kdirectorycontentscounterworker.h index 5266960cd..077df9f69 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.h +++ b/src/kitemviews/private/kdirectorycontentscounterworker.h @@ -17,32 +17,20 @@ class KDirectoryContentsCounterWorker : public QObject Q_OBJECT public: - enum Option { NoOptions = 0x0, CountHiddenFiles = 0x1, CountDirectoriesOnly = 0x2 }; + enum Option { NoOptions = 0x0, CountHiddenFiles = 0x1 }; 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); - /** - * Counts the items inside the directory \a path using the options - * \a options. - * - * @return The number of items. - */ - CountResult subItemsCount(const QString &path, Options options); - + bool stopping() const; + QString scannedPath() const; Q_SIGNALS: /** * Signals that the directory \a path contains \a count items and optionally the size of its content. */ - void result(const QString &path, int count, long size); + void result(const QString &path, int count, long long size); + void intermediateResult(const QString &path, int count, long long size); + void finished(); public Q_SLOTS: /** @@ -52,16 +40,17 @@ public Q_SLOTS: // Note that the full type name KDirectoryContentsCounterWorker::Options // is needed here. Just using 'Options' is OK for the compiler, but // confuses moc. - void countDirectoryContents(const QString &path, KDirectoryContentsCounterWorker::Options options); + void countDirectoryContents(const QString &path, KDirectoryContentsCounterWorker::Options options, int maxRecursiveLevel); void stop(); private: #ifndef Q_OS_WIN - KDirectoryContentsCounterWorker::CountResult - walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDirectoriesOnly, const uint allowedRecursiveLevel); + void walkDir(const QString &dirPath, bool countHiddenFiles, uint allowedRecursiveLevel); #endif - bool m_stopping = false; + QString m_scannedPath; + + std::atomic m_stopping = false; }; Q_DECLARE_METATYPE(KDirectoryContentsCounterWorker::Options) diff --git a/src/panels/folders/folderspanel.cpp b/src/panels/folders/folderspanel.cpp index 914692000..f304654cb 100644 --- a/src/panels/folders/folderspanel.cpp +++ b/src/panels/folders/folderspanel.cpp @@ -131,7 +131,6 @@ void FoldersPanel::showEvent(QShowEvent *event) // This assures that no performance and memory overhead is given when the folders panel is not // used at all and stays invisible. KFileItemListView *view = new KFileItemListView(); - view->setScanDirectories(false); view->setWidgetCreator(new KItemListWidgetCreator()); view->setSupportsItemExpanding(true); // Set the opacity to 0 initially. The opacity will be increased after the loading of the initial tree -- cgit v1.3 From 3338c2834af81e00c3f2b73ad7061fefce89f226 Mon Sep 17 00:00:00 2001 From: Méven Car Date: Tue, 13 Jun 2023 09:40:27 +0000 Subject: Settings Add ViewModes > Content display This does not move the settings location in files though. baby step for https://invent.kde.org/system/dolphin/-/issues/36 --- src/CMakeLists.txt | 6 + src/dolphinviewcontainer.cpp | 11 +- src/kitemviews/kfileitemlistwidget.cpp | 14 +- src/kitemviews/kfileitemmodel.cpp | 8 +- src/kitemviews/kfileitemmodelrolesupdater.cpp | 5 +- src/kitemviews/kitemlistwidget.cpp | 2 +- .../private/kdirectorycontentscounter.cpp | 4 +- src/kitemviews/private/kdirectorycontentscounter.h | 2 +- src/settings/dolphin_contentdisplaysettings.kcfg | 31 +++++ src/settings/dolphin_contentdisplaysettings.kcfgc | 4 + src/settings/dolphin_detailsmodesettings.kcfg | 21 --- src/settings/dolphin_detailsmodesettings.upd | 11 +- src/settings/viewmodes/contentdisplaytab.cpp | 148 +++++++++++++++++++++ src/settings/viewmodes/contentdisplaytab.h | 39 ++++++ src/settings/viewmodes/viewsettingspage.cpp | 23 +++- src/settings/viewmodes/viewsettingspage.h | 4 + src/settings/viewmodes/viewsettingstab.cpp | 129 ++---------------- src/settings/viewmodes/viewsettingstab.h | 8 -- src/views/dolphinitemlistview.cpp | 3 + src/views/dolphinview.h | 5 +- 20 files changed, 305 insertions(+), 173 deletions(-) create mode 100644 src/settings/dolphin_contentdisplaysettings.kcfg create mode 100644 src/settings/dolphin_contentdisplaysettings.kcfgc create mode 100644 src/settings/viewmodes/contentdisplaytab.cpp create mode 100644 src/settings/viewmodes/contentdisplaytab.h (limited to 'src/kitemviews/kfileitemmodel.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 97bc770d7..62c70ba79 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -183,6 +183,7 @@ kconfig_add_kcfg_files(dolphinprivate settings/dolphin_directoryviewpropertysettings.kcfgc settings/dolphin_detailsmodesettings.kcfgc settings/dolphin_iconsmodesettings.kcfgc + settings/dolphin_contentdisplaysettings.kcfgc settings/dolphin_generalsettings.kcfgc settings/dolphin_contextmenusettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc @@ -305,6 +306,7 @@ target_sources(dolphinstatic PRIVATE settings/viewmodes/viewsettingspage.cpp settings/viewmodes/viewmodesettings.cpp settings/viewmodes/viewsettingstab.cpp + settings/viewmodes/contentdisplaytab.cpp statusbar/dolphinstatusbar.cpp statusbar/mountpointobserver.cpp statusbar/mountpointobservercache.cpp @@ -364,6 +366,7 @@ target_sources(dolphinstatic PRIVATE settings/viewmodes/viewsettingspage.h settings/viewmodes/viewmodesettings.h settings/viewmodes/viewsettingstab.h + settings/viewmodes/contentdisplaytab.h statusbar/dolphinstatusbar.h statusbar/mountpointobserver.h statusbar/mountpointobservercache.h @@ -408,6 +411,7 @@ kconfig_add_kcfg_files(dolphinstatic settings/dolphin_detailsmodesettings.kcfgc settings/dolphin_contextmenusettings.kcfgc settings/dolphin_iconsmodesettings.kcfgc + settings/dolphin_contentdisplaysettings.kcfgc search/dolphin_searchsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc ) @@ -544,6 +548,7 @@ if(NOT WIN32) settings/dolphin_directoryviewpropertysettings.kcfgc settings/dolphin_detailsmodesettings.kcfgc settings/dolphin_iconsmodesettings.kcfgc + settings/dolphin_contentdisplaysettings.kcfgc settings/dolphin_generalsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc ) @@ -586,6 +591,7 @@ install( FILES settings/dolphin_directoryviewpropertysettings.kcfg settings/dolphin_compactmodesettings.kcfg settings/dolphin_iconsmodesettings.kcfg settings/dolphin_detailsmodesettings.kcfg + settings/dolphin_contentdisplaysettings.kcfg settings/dolphin_versioncontrolsettings.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR} ) diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index 2dc366129..8627891c0 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -6,8 +6,11 @@ #include "dolphinviewcontainer.h" +#include "dolphin_compactmodesettings.h" +#include "dolphin_contentdisplaysettings.h" #include "dolphin_detailsmodesettings.h" #include "dolphin_generalsettings.h" +#include "dolphin_iconsmodesettings.h" #include "dolphindebug.h" #include "dolphinplacesmodelsingleton.h" #include "filterbar/filterbar.h" @@ -176,11 +179,9 @@ DolphinViewContainer::DolphinViewContainer(const QUrl &url, QWidget *parent) setSearchModeEnabled(isSearchUrl(url)); - connect(DetailsModeSettings::self(), &KCoreConfigSkeleton::configChanged, this, [=]() { - if (view()->viewMode() == DolphinView::Mode::DetailsView) { - view()->reload(); - } - }); + // Update view as the ContentDisplaySettings change + // this happens here and not in DolphinView as DolphinviewContainer and DolphinView are not in the same build target ATM + connect(ContentDisplaySettings::self(), &KCoreConfigSkeleton::configChanged, m_view, &DolphinView::reload); KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel(); connect(placesModel, &KFilePlacesModel::dataChanged, this, &DolphinViewContainer::slotPlacesModelChanged); diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp index ec8683345..385067af0 100644 --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -9,7 +9,7 @@ #include "kfileitemmodel.h" #include "kitemlistview.h" -#include "dolphin_detailsmodesettings.h" +#include "dolphin_contentdisplaysettings.h" #include #include @@ -56,7 +56,7 @@ QString KFileItemListWidgetInformant::roleText(const QByteArray &role, const QHa // use a hash + switch for a linear runtime. auto formatDate = [formatter, local](const QDateTime &time) { - if (DetailsModeSettings::useShortRelativeDates()) { + if (ContentDisplaySettings::useShortRelativeDates()) { return formatter.formatRelativeDateTime(time, QLocale::ShortFormat); } else { return local.toString(time, QLocale::ShortFormat); @@ -67,7 +67,7 @@ QString KFileItemListWidgetInformant::roleText(const QByteArray &role, const QHa if (values.value("isDir").toBool()) { if (!roleValue.isNull() && roleValue != -1) { // The item represents a directory. - if (DetailsModeSettings::directorySizeCount() || roleValue == -2 /* size is invalid */) { + if (ContentDisplaySettings::directorySizeCount() || roleValue == -2 /* size is invalid */) { // Show the number of sub directories instead of the file size of the directory. const int count = values.value("count").toInt(); text = i18ncp("@item:intable", "%1 item", "%1 items", count); @@ -101,14 +101,14 @@ QString KFileItemListWidgetInformant::roleText(const QByteArray &role, const QHa } else if (role == "permissions") { const auto permissions = roleValue.value(); - switch (DetailsModeSettings::usePermissionsFormat()) { - case DetailsModeSettings::EnumUsePermissionsFormat::SymbolicFormat: + switch (ContentDisplaySettings::usePermissionsFormat()) { + case ContentDisplaySettings::EnumUsePermissionsFormat::SymbolicFormat: text = permissions.at(0).toString(); break; - case DetailsModeSettings::EnumUsePermissionsFormat::NumericFormat: + case ContentDisplaySettings::EnumUsePermissionsFormat::NumericFormat: text = QString::number(permissions.at(1).toInt(), 8); break; - case DetailsModeSettings::EnumUsePermissionsFormat::CombinedFormat: + case ContentDisplaySettings::EnumUsePermissionsFormat::CombinedFormat: text = QString("%1 (%2)").arg(permissions.at(0).toString()).arg(permissions.at(1).toInt(), 0, 8); break; } diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 6617fbec6..5ac6f9be1 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -8,7 +8,7 @@ #include "kfileitemmodel.h" -#include "dolphin_detailsmodesettings.h" +#include "dolphin_contentdisplaysettings.h" #include "dolphin_generalsettings.h" #include "dolphindebug.h" #include "private/kfileitemmodelsortalgorithm.h" @@ -1991,7 +1991,7 @@ bool KFileItemModel::lessThan(const ItemData *a, const ItemData *b, const QColla } } - if (m_sortDirsFirst || (DetailsModeSettings::directorySizeCount() && m_sortRole == SizeRole)) { + if (m_sortDirsFirst || (ContentDisplaySettings::directorySizeCount() && m_sortRole == SizeRole)) { const bool isDirA = a->item.isDir(); const bool isDirB = b->item.isDir(); if (isDirA && !isDirB) { @@ -2043,7 +2043,7 @@ int KFileItemModel::sortRoleCompare(const ItemData *a, const ItemData *b, const break; case SizeRole: { - if (DetailsModeSettings::directorySizeCount() && itemA.isDir()) { + if (ContentDisplaySettings::directorySizeCount() && itemA.isDir()) { // folders first then // items A and B are folders thanks to lessThan checks auto valueA = a->values.value("count"); @@ -2303,7 +2303,7 @@ QList> KFileItemModel::sizeRoleGroups() const KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U; QString newGroupValue; if (!item.isNull() && item.isDir()) { - if (DetailsModeSettings::directorySizeCount() || m_sortDirsFirst) { + if (ContentDisplaySettings::directorySizeCount() || m_sortDirsFirst) { newGroupValue = i18nc("@title:group Size", "Folders"); } else { fileSize = m_itemData.at(i)->values.value("size").toULongLong(); diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index 6838d0861..9d8ae55da 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -10,7 +10,6 @@ #include "kfileitemmodel.h" #include "private/kdirectorycontentscounter.h" #include "private/kpixmapmodifier.h" -#include "qdir.h" #include #include @@ -21,7 +20,7 @@ #include #include -#include "dolphin_detailsmodesettings.h" +#include "dolphin_contentdisplaysettings.h" #if HAVE_BALOO #include "private/kbaloorolesprovider.h" @@ -1269,7 +1268,7 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) void KFileItemModelRolesUpdater::startDirectorySizeCounting(const KFileItem &item, int index) { - if (DetailsModeSettings::directorySizeCount() || item.isSlow() || !item.isLocalFile()) { + if (ContentDisplaySettings::directorySizeCount() || item.isSlow() || !item.isLocalFile()) { // fastpath no recursion necessary auto data = m_model->data(index); diff --git a/src/kitemviews/kitemlistwidget.cpp b/src/kitemviews/kitemlistwidget.cpp index 1d6b9641a..2c8ef70a5 100644 --- a/src/kitemviews/kitemlistwidget.cpp +++ b/src/kitemviews/kitemlistwidget.cpp @@ -40,6 +40,7 @@ KItemListWidget::KItemListWidget(KItemListWidgetInformant *informant, QGraphicsI , m_data() , m_visibleRoles() , m_columnWidths() + , m_sidePadding(0) , m_styleOption() , m_siblingsInfo() , m_hoverOpacity(0) @@ -49,7 +50,6 @@ KItemListWidget::KItemListWidget(KItemListWidgetInformant *informant, QGraphicsI , m_selectionToggle(nullptr) , m_editedRole() , m_iconSize(-1) - , m_sidePadding(0) { connect(&m_hoverSequenceTimer, &QTimer::timeout, this, &KItemListWidget::slotHoverSequenceTimerTimeout); } diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp index e1a47419d..648b20b6f 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.cpp +++ b/src/kitemviews/private/kdirectorycontentscounter.cpp @@ -6,7 +6,7 @@ */ #include "kdirectorycontentscounter.h" -#include "dolphin_detailsmodesettings.h" +#include "dolphin_contentdisplaysettings.h" #include "kitemviews/kfileitemmodel.h" #include @@ -224,7 +224,7 @@ void KDirectoryContentsCounter::scheduleNext() } m_workerIsBusy = true; - Q_EMIT requestDirectoryContentsCount(m_currentPath, options, DetailsModeSettings::recursiveDirectorySizeLimit()); + Q_EMIT requestDirectoryContentsCount(m_currentPath, options, ContentDisplaySettings::recursiveDirectorySizeLimit()); } void KDirectoryContentsCounter::enqueuePathScanning(const QString &path, bool alreadyInCache, PathCountPriority priority) diff --git a/src/kitemviews/private/kdirectorycontentscounter.h b/src/kitemviews/private/kdirectorycontentscounter.h index 552b5560e..0da3ccd7d 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.h +++ b/src/kitemviews/private/kdirectorycontentscounter.h @@ -47,7 +47,7 @@ public: Q_SIGNALS: /** * Signals that the directory \a path contains \a count items of size \a - * Size calculation depends on parameter DetailsModeSettings::recursiveDirectorySizeLimit + * Size calculation depends on parameter ContentDisplaySettings::recursiveDirectorySizeLimit */ void result(const QString &path, int count, long long size); diff --git a/src/settings/dolphin_contentdisplaysettings.kcfg b/src/settings/dolphin_contentdisplaysettings.kcfg new file mode 100644 index 000000000..36895b869 --- /dev/null +++ b/src/settings/dolphin_contentdisplaysettings.kcfg @@ -0,0 +1,31 @@ + + + + + + + + true + + + + 10 + + + + true + + + + + + + + + 0 + + + diff --git a/src/settings/dolphin_contentdisplaysettings.kcfgc b/src/settings/dolphin_contentdisplaysettings.kcfgc new file mode 100644 index 000000000..c986578b2 --- /dev/null +++ b/src/settings/dolphin_contentdisplaysettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_contentdisplaysettings.kcfg +ClassName=ContentDisplaySettings +Singleton=yes +Mutators=true diff --git a/src/settings/dolphin_detailsmodesettings.kcfg b/src/settings/dolphin_detailsmodesettings.kcfg index 9efdebe71..98fe0efff 100644 --- a/src/settings/dolphin_detailsmodesettings.kcfg +++ b/src/settings/dolphin_detailsmodesettings.kcfg @@ -39,26 +39,5 @@ true - - - true - - - - 10 - - - - true - - - - - - - - - 0 - diff --git a/src/settings/dolphin_detailsmodesettings.upd b/src/settings/dolphin_detailsmodesettings.upd index 091fee52c..da8f4b9cd 100644 --- a/src/settings/dolphin_detailsmodesettings.upd +++ b/src/settings/dolphin_detailsmodesettings.upd @@ -5,4 +5,13 @@ Version=5 Id=rename-leading-padding File=dolphinrc Group=DetailsMode -Key=LeadingPadding,SidePadding \ No newline at end of file +Key=LeadingPadding,SidePadding + +#Rename Move content-display from detailsMode +Id=move-content-display +File=dolphinrc +Group=DetailsMode,ContentDisplay +Key=DirectorySizeCount +Key=RecursiveDirectorySizeLimit +Key=UseShortRelativeDates +Key=UsePermissionsFormat diff --git a/src/settings/viewmodes/contentdisplaytab.cpp b/src/settings/viewmodes/contentdisplaytab.cpp new file mode 100644 index 000000000..442bd3b49 --- /dev/null +++ b/src/settings/viewmodes/contentdisplaytab.cpp @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: 2023 Méven Car + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "contentdisplaytab.h" +#include "dolphin_contentdisplaysettings.h" + +#include +#include + +#include +#include +#include +#include +#include + +ContentDisplayTab::ContentDisplayTab(QWidget *parent) + : SettingsPageBase(parent) + , m_numberOfItems(nullptr) + , m_sizeOfContents(nullptr) + , m_recursiveDirectorySizeLimit(nullptr) + , m_useRelatetiveDates(nullptr) + , m_useShortDates(nullptr) + , m_useSymbolicPermissions(nullptr) + , m_useNumericPermissions(nullptr) + , m_useCombinedPermissions(nullptr) +{ + QFormLayout *topLayout = new QFormLayout(this); + +#ifndef Q_OS_WIN + // Sorting properties + m_numberOfItems = new QRadioButton(i18nc("option:radio", "Number of items")); + m_sizeOfContents = new QRadioButton(i18nc("option:radio", "Size of contents, up to ")); + + QButtonGroup *sortingModeGroup = new QButtonGroup(this); + sortingModeGroup->addButton(m_numberOfItems); + sortingModeGroup->addButton(m_sizeOfContents); + + m_recursiveDirectorySizeLimit = new QSpinBox(); + connect(m_recursiveDirectorySizeLimit, &QSpinBox::valueChanged, this, [this](int value) { + m_recursiveDirectorySizeLimit->setSuffix(i18np(" level deep", " levels deep", value)); + }); + m_recursiveDirectorySizeLimit->setRange(1, 20); + m_recursiveDirectorySizeLimit->setSingleStep(1); + + QHBoxLayout *contentsSizeLayout = new QHBoxLayout(); + contentsSizeLayout->addWidget(m_sizeOfContents); + contentsSizeLayout->addWidget(m_recursiveDirectorySizeLimit); + + topLayout->addRow(i18nc("@title:group", "Folder size displays:"), m_numberOfItems); + topLayout->addRow(QString(), contentsSizeLayout); +#endif + + QDateTime thirtyMinutesAgo = QDateTime::currentDateTime().addSecs(-30 * 60); + QLocale local; + KFormat formatter(local); + + m_useRelatetiveDates = new QRadioButton( + i18nc("option:radio as in relative date", "Relative (e.g. '%1')", formatter.formatRelativeDateTime(thirtyMinutesAgo, QLocale::ShortFormat))); + m_useShortDates = + new QRadioButton(i18nc("option:radio as in absolute date", "Absolute (e.g. '%1')", local.toString(thirtyMinutesAgo, QLocale::ShortFormat))); + + QButtonGroup *dateFormatGroup = new QButtonGroup(this); + dateFormatGroup->addButton(m_useRelatetiveDates); + dateFormatGroup->addButton(m_useShortDates); + + topLayout->addRow(i18nc("@title:group", "Date style:"), m_useRelatetiveDates); + topLayout->addRow(QString(), m_useShortDates); + + m_useSymbolicPermissions = new QRadioButton(i18nc("option:radio as symbolic style ", "Symbolic (e.g. 'drwxr-xr-x')")); + m_useNumericPermissions = new QRadioButton(i18nc("option:radio as numeric style", "Numeric (Octal) (e.g. '755')")); + m_useCombinedPermissions = new QRadioButton(i18nc("option:radio as combined style", "Combined (e.g. 'drwxr-xr-x (755)')")); + + topLayout->addRow(i18nc("@title:group", "Permissions style:"), m_useSymbolicPermissions); + topLayout->addRow(QString(), m_useNumericPermissions); + topLayout->addRow(QString(), m_useCombinedPermissions); + + QButtonGroup *permissionsFormatGroup = new QButtonGroup(this); + permissionsFormatGroup->addButton(m_useSymbolicPermissions); + permissionsFormatGroup->addButton(m_useNumericPermissions); + permissionsFormatGroup->addButton(m_useCombinedPermissions); + +#ifndef Q_OS_WIN + connect(m_recursiveDirectorySizeLimit, &QSpinBox::valueChanged, this, &SettingsPageBase::changed); + connect(m_numberOfItems, &QRadioButton::toggled, this, &SettingsPageBase::changed); + connect(m_sizeOfContents, &QRadioButton::toggled, this, [=]() { + m_recursiveDirectorySizeLimit->setEnabled(m_sizeOfContents->isChecked()); + }); +#endif + + connect(m_useRelatetiveDates, &QRadioButton::toggled, this, &SettingsPageBase::changed); + connect(m_useShortDates, &QRadioButton::toggled, this, &SettingsPageBase::changed); + connect(m_useSymbolicPermissions, &QRadioButton::toggled, this, &SettingsPageBase::changed); + connect(m_useNumericPermissions, &QRadioButton::toggled, this, &SettingsPageBase::changed); + connect(m_useCombinedPermissions, &QRadioButton::toggled, this, &SettingsPageBase::changed); + + loadSettings(); +} + +void ContentDisplayTab::applySettings() +{ + auto settings = ContentDisplaySettings::self(); +#ifndef Q_OS_WIN + settings->setDirectorySizeCount(m_numberOfItems->isChecked()); + settings->setRecursiveDirectorySizeLimit(m_recursiveDirectorySizeLimit->value()); +#endif + + settings->setUseShortRelativeDates(m_useRelatetiveDates->isChecked()); + + if (m_useSymbolicPermissions->isChecked()) { + settings->setUsePermissionsFormat(ContentDisplaySettings::EnumUsePermissionsFormat::SymbolicFormat); + } else if (m_useNumericPermissions->isChecked()) { + settings->setUsePermissionsFormat(ContentDisplaySettings::EnumUsePermissionsFormat::NumericFormat); + } else if (m_useCombinedPermissions->isChecked()) { + settings->setUsePermissionsFormat(ContentDisplaySettings::EnumUsePermissionsFormat::CombinedFormat); + } + settings->save(); +} + +void ContentDisplayTab::loadSettings() +{ + auto settings = ContentDisplaySettings::self(); +#ifndef Q_OS_WIN + if (settings->directorySizeCount()) { + m_numberOfItems->setChecked(true); + m_recursiveDirectorySizeLimit->setEnabled(false); + } else { + m_sizeOfContents->setChecked(true); + m_recursiveDirectorySizeLimit->setEnabled(true); + } + m_recursiveDirectorySizeLimit->setValue(settings->recursiveDirectorySizeLimit()); +#endif + m_useRelatetiveDates->setChecked(settings->useShortRelativeDates()); + m_useShortDates->setChecked(!settings->useShortRelativeDates()); + m_useSymbolicPermissions->setChecked(settings->usePermissionsFormat() == ContentDisplaySettings::EnumUsePermissionsFormat::SymbolicFormat); + m_useNumericPermissions->setChecked(settings->usePermissionsFormat() == ContentDisplaySettings::EnumUsePermissionsFormat::NumericFormat); + m_useCombinedPermissions->setChecked(settings->usePermissionsFormat() == ContentDisplaySettings::EnumUsePermissionsFormat::CombinedFormat); +} + +void ContentDisplayTab::restoreDefaults() +{ + auto settings = ContentDisplaySettings::self(); + settings->useDefaults(true); + loadSettings(); + settings->useDefaults(false); +} diff --git a/src/settings/viewmodes/contentdisplaytab.h b/src/settings/viewmodes/contentdisplaytab.h new file mode 100644 index 000000000..5484bf413 --- /dev/null +++ b/src/settings/viewmodes/contentdisplaytab.h @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2023 Méven Car + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef GENERALTAB_H +#define GENERALTAB_H + +#include "settings/settingspagebase.h" + +class QRadioButton; +class QSpinBox; + +class ContentDisplayTab : public SettingsPageBase +{ + Q_OBJECT + +public: + ContentDisplayTab(QWidget *parent); + +public: + // SettingsPageBase interface + void applySettings() override; + void restoreDefaults() override; + +private: + void loadSettings(); + + QRadioButton *m_numberOfItems; + QRadioButton *m_sizeOfContents; + QSpinBox *m_recursiveDirectorySizeLimit; + QRadioButton *m_useRelatetiveDates; + QRadioButton *m_useShortDates; + QRadioButton *m_useSymbolicPermissions; + QRadioButton *m_useNumericPermissions; + QRadioButton *m_useCombinedPermissions; +}; + +#endif // GENERALTAB_H diff --git a/src/settings/viewmodes/viewsettingspage.cpp b/src/settings/viewmodes/viewsettingspage.cpp index cb0b5ecee..33409ec25 100644 --- a/src/settings/viewmodes/viewsettingspage.cpp +++ b/src/settings/viewmodes/viewsettingspage.cpp @@ -6,7 +6,7 @@ #include "viewsettingspage.h" -#include "views/dolphinview.h" +#include "contentdisplaytab.h" #include "viewsettingstab.h" #include @@ -21,7 +21,14 @@ ViewSettingsPage::ViewSettingsPage(QWidget *parent) QVBoxLayout *topLayout = new QVBoxLayout(this); topLayout->setContentsMargins(0, 0, 0, 0); - QTabWidget *tabWidget = new QTabWidget(this); + tabWidget = new QTabWidget(this); + + // Content Display Tab + contentDisplayTab = new ContentDisplayTab(tabWidget); + tabWidget->addTab(contentDisplayTab, + QIcon::fromTheme(QStringLiteral("view-choose")), + i18nc("@title:tab how file items columns are displayed", "Content Display")); + connect(contentDisplayTab, &SettingsPageBase::changed, this, &ViewSettingsPage::changed); // Initialize 'Icons' tab ViewSettingsTab *iconsTab = new ViewSettingsTab(ViewSettingsTab::IconsMode, tabWidget); @@ -51,6 +58,8 @@ ViewSettingsPage::~ViewSettingsPage() void ViewSettingsPage::applySettings() { + contentDisplayTab->applySettings(); + for (ViewSettingsTab *tab : qAsConst(m_tabs)) { tab->applySettings(); } @@ -58,7 +67,15 @@ void ViewSettingsPage::applySettings() void ViewSettingsPage::restoreDefaults() { + if (tabWidget->currentWidget() == contentDisplayTab) { + contentDisplayTab->restoreDefaults(); + return; + } + for (ViewSettingsTab *tab : qAsConst(m_tabs)) { - tab->restoreDefaultSettings(); + if (tabWidget->currentWidget() == tab) { + tab->restoreDefaultSettings(); + return; + } } } diff --git a/src/settings/viewmodes/viewsettingspage.h b/src/settings/viewmodes/viewsettingspage.h index 797c0b712..e52f6b2c0 100644 --- a/src/settings/viewmodes/viewsettingspage.h +++ b/src/settings/viewmodes/viewsettingspage.h @@ -10,6 +10,8 @@ class ViewSettingsTab; class QWidget; +class ContentDisplayTab; +class QTabWidget; /** * @brief Page for the 'View' settings of the Dolphin settings dialog. @@ -32,6 +34,8 @@ public: void restoreDefaults() override; private: + ContentDisplayTab *contentDisplayTab; + QTabWidget *tabWidget; QList m_tabs; }; diff --git a/src/settings/viewmodes/viewsettingstab.cpp b/src/settings/viewmodes/viewsettingstab.cpp index 1d4c5f5d5..df850e530 100644 --- a/src/settings/viewmodes/viewsettingstab.cpp +++ b/src/settings/viewmodes/viewsettingstab.cpp @@ -14,7 +14,6 @@ #include "settings/viewmodes/viewmodesettings.h" #include "views/zoomlevelinfo.h" -#include #include #include @@ -35,12 +34,6 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget *parent) , m_widthBox(nullptr) , m_maxLinesBox(nullptr) , m_expandableFolders(nullptr) - , m_recursiveDirectorySizeLimit(nullptr) - , m_useRelatetiveDates(nullptr) - , m_useShortDates(nullptr) - , m_useSymbolicPermissions(nullptr) - , m_useNumericPermissions(nullptr) - , m_useCombinedPermissions(nullptr) { QFormLayout *topLayout = new QFormLayout(this); @@ -113,61 +106,6 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget *parent) topLayout->addRow(i18nc("@title:group", "Open files and folders:"), m_entireRow); // clang-format on topLayout->addRow(QString(), m_iconAndNameOnly); - -#ifndef Q_OS_WIN - // Sorting properties - m_numberOfItems = new QRadioButton(i18nc("option:radio", "Number of items")); - m_sizeOfContents = new QRadioButton(i18nc("option:radio", "Size of contents, up to ")); - - QButtonGroup *sortingModeGroup = new QButtonGroup(this); - sortingModeGroup->addButton(m_numberOfItems); - sortingModeGroup->addButton(m_sizeOfContents); - - m_recursiveDirectorySizeLimit = new QSpinBox(); - connect(m_recursiveDirectorySizeLimit, &QSpinBox::valueChanged, this, [this](int value) { - m_recursiveDirectorySizeLimit->setSuffix(i18np(" level deep", " levels deep", value)); - }); - m_recursiveDirectorySizeLimit->setRange(1, 20); - m_recursiveDirectorySizeLimit->setSingleStep(1); - - QHBoxLayout *contentsSizeLayout = new QHBoxLayout(); - contentsSizeLayout->addWidget(m_sizeOfContents); - contentsSizeLayout->addWidget(m_recursiveDirectorySizeLimit); - - topLayout->addRow(i18nc("@title:group", "Folder size displays:"), m_numberOfItems); - topLayout->addRow(QString(), contentsSizeLayout); -#endif - - QDateTime thirtyMinutesAgo = QDateTime::currentDateTime().addSecs(-30 * 60); - QLocale local; - KFormat formatter(local); - - m_useRelatetiveDates = new QRadioButton( - i18nc("option:radio as in relative date", "Relative (e.g. '%1')", formatter.formatRelativeDateTime(thirtyMinutesAgo, QLocale::ShortFormat))); - m_useShortDates = - new QRadioButton(i18nc("option:radio as in absolute date", "Absolute (e.g. '%1')", local.toString(thirtyMinutesAgo, QLocale::ShortFormat))); - - QButtonGroup *dateFormatGroup = new QButtonGroup(this); - dateFormatGroup->addButton(m_useRelatetiveDates); - dateFormatGroup->addButton(m_useShortDates); - - topLayout->addRow(i18nc("@title:group", "Date style:"), m_useRelatetiveDates); - topLayout->addRow(QString(), m_useShortDates); - - m_useSymbolicPermissions = new QRadioButton(i18nc("option:radio as symbolic style ", "Symbolic (e.g. 'drwxr-xr-x')")); - - m_useNumericPermissions = new QRadioButton(i18nc("option:radio as numeric style", "Numeric (Octal) (e.g. '755')")); - - m_useCombinedPermissions = new QRadioButton(i18nc("option:radio as combined style", "Combined (e.g. 'drwxr-xr-x (755)')")); - - topLayout->addRow(i18nc("@title:group", "Permissions style:"), m_useSymbolicPermissions); - topLayout->addRow(QString(), m_useNumericPermissions); - topLayout->addRow(QString(), m_useCombinedPermissions); - - QButtonGroup *permissionsFormatGroup = new QButtonGroup(this); - permissionsFormatGroup->addButton(m_useSymbolicPermissions); - permissionsFormatGroup->addButton(m_useNumericPermissions); - permissionsFormatGroup->addButton(m_useCombinedPermissions); break; } @@ -188,18 +126,6 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget *parent) case DetailsMode: connect(m_entireRow, &QCheckBox::toggled, this, &ViewSettingsTab::changed); connect(m_expandableFolders, &QCheckBox::toggled, this, &ViewSettingsTab::changed); -#ifndef Q_OS_WIN - connect(m_recursiveDirectorySizeLimit, &QSpinBox::valueChanged, this, &ViewSettingsTab::changed); - connect(m_numberOfItems, &QRadioButton::toggled, this, &ViewSettingsTab::changed); - connect(m_sizeOfContents, &QRadioButton::toggled, this, [=]() { - m_recursiveDirectorySizeLimit->setEnabled(m_sizeOfContents->isChecked()); - }); -#endif - connect(m_useRelatetiveDates, &QRadioButton::toggled, this, &ViewSettingsTab::changed); - connect(m_useShortDates, &QRadioButton::toggled, this, &ViewSettingsTab::changed); - connect(m_useSymbolicPermissions, &QRadioButton::toggled, this, &ViewSettingsTab::changed); - connect(m_useNumericPermissions, &QRadioButton::toggled, this, &ViewSettingsTab::changed); - connect(m_useCombinedPermissions, &QRadioButton::toggled, this, &ViewSettingsTab::changed); break; default: break; @@ -212,50 +138,35 @@ ViewSettingsTab::~ViewSettingsTab() void ViewSettingsTab::applySettings() { - const QFont font = m_fontRequester->currentFont(); - const bool useSystemFont = (m_fontRequester->mode() == DolphinFontRequester::SystemFont); - switch (m_mode) { case IconsMode: IconsModeSettings::setTextWidthIndex(m_widthBox->currentIndex()); IconsModeSettings::setMaximumTextLines(m_maxLinesBox->currentIndex()); + IconsModeSettings::self()->save(); break; case CompactMode: CompactModeSettings::setMaximumTextWidthIndex(m_widthBox->currentIndex()); + CompactModeSettings::self()->save(); break; case DetailsMode: + auto detailsModeSettings = DetailsModeSettings::self(); // We need side-padding when the full row is a click target to still be able to not click items. // So here the default padding is enabled when the full row highlight is enabled. - if (m_entireRow->isChecked() && !DetailsModeSettings::highlightEntireRow()) { - auto detailsModeSettings = DetailsModeSettings::self(); + if (m_entireRow->isChecked() && !detailsModeSettings->highlightEntireRow()) { const bool usedDefaults = detailsModeSettings->useDefaults(true); const uint defaultSidePadding = detailsModeSettings->sidePadding(); detailsModeSettings->useDefaults(usedDefaults); - if (DetailsModeSettings::sidePadding() < defaultSidePadding) { - DetailsModeSettings::setSidePadding(defaultSidePadding); + if (detailsModeSettings->sidePadding() < defaultSidePadding) { + detailsModeSettings->setSidePadding(defaultSidePadding); } - } else if (!m_entireRow->isChecked() && DetailsModeSettings::highlightEntireRow()) { + } else if (!m_entireRow->isChecked() && detailsModeSettings->highlightEntireRow()) { // The full row click target is disabled so now most of the view area can be used to interact // with the view background. Having an extra side padding has no usability benefit in this case. - DetailsModeSettings::setSidePadding(0); - } - DetailsModeSettings::setHighlightEntireRow(m_entireRow->isChecked()); - DetailsModeSettings::setExpandableFolders(m_expandableFolders->isChecked()); -#ifndef Q_OS_WIN - DetailsModeSettings::setDirectorySizeCount(m_numberOfItems->isChecked()); - DetailsModeSettings::setRecursiveDirectorySizeLimit(m_recursiveDirectorySizeLimit->value()); -#endif - DetailsModeSettings::setUseShortRelativeDates(m_useRelatetiveDates->isChecked()); - - if (m_useSymbolicPermissions->isChecked()) { - DetailsModeSettings::setUsePermissionsFormat(DetailsModeSettings::EnumUsePermissionsFormat::SymbolicFormat); - } else if (m_useNumericPermissions->isChecked()) { - DetailsModeSettings::setUsePermissionsFormat(DetailsModeSettings::EnumUsePermissionsFormat::NumericFormat); - } else if (m_useCombinedPermissions->isChecked()) { - DetailsModeSettings::setUsePermissionsFormat(DetailsModeSettings::EnumUsePermissionsFormat::CombinedFormat); + detailsModeSettings->setSidePadding(0); } - break; - default: + detailsModeSettings->setHighlightEntireRow(m_entireRow->isChecked()); + detailsModeSettings->setExpandableFolders(m_expandableFolders->isChecked()); + detailsModeSettings->save(); break; } @@ -266,6 +177,9 @@ void ViewSettingsTab::applySettings() settings.setIconSize(iconSize); settings.setPreviewSize(previewSize); + const QFont font = m_fontRequester->currentFont(); + const bool useSystemFont = (m_fontRequester->mode() == DolphinFontRequester::SystemFont); + settings.setUseSystemFont(useSystemFont); settings.setViewFont(font); @@ -294,21 +208,6 @@ void ViewSettingsTab::loadSettings() m_entireRow->setChecked(DetailsModeSettings::highlightEntireRow()); m_iconAndNameOnly->setChecked(!m_entireRow->isChecked()); m_expandableFolders->setChecked(DetailsModeSettings::expandableFolders()); -#ifndef Q_OS_WIN - if (DetailsModeSettings::directorySizeCount()) { - m_numberOfItems->setChecked(true); - m_recursiveDirectorySizeLimit->setEnabled(false); - } else { - m_sizeOfContents->setChecked(true); - m_recursiveDirectorySizeLimit->setEnabled(true); - } - m_recursiveDirectorySizeLimit->setValue(DetailsModeSettings::recursiveDirectorySizeLimit()); -#endif - m_useRelatetiveDates->setChecked(DetailsModeSettings::useShortRelativeDates()); - m_useShortDates->setChecked(!DetailsModeSettings::useShortRelativeDates()); - m_useSymbolicPermissions->setChecked(DetailsModeSettings::usePermissionsFormat() == DetailsModeSettings::EnumUsePermissionsFormat::SymbolicFormat); - m_useNumericPermissions->setChecked(DetailsModeSettings::usePermissionsFormat() == DetailsModeSettings::EnumUsePermissionsFormat::NumericFormat); - m_useCombinedPermissions->setChecked(DetailsModeSettings::usePermissionsFormat() == DetailsModeSettings::EnumUsePermissionsFormat::CombinedFormat); break; default: break; diff --git a/src/settings/viewmodes/viewsettingstab.h b/src/settings/viewmodes/viewsettingstab.h index b59fb399e..6be8fc3b5 100644 --- a/src/settings/viewmodes/viewsettingstab.h +++ b/src/settings/viewmodes/viewsettingstab.h @@ -55,14 +55,6 @@ private: QRadioButton *m_entireRow; QRadioButton *m_iconAndNameOnly; QCheckBox *m_expandableFolders; - QRadioButton *m_numberOfItems; - QRadioButton *m_sizeOfContents; - QSpinBox *m_recursiveDirectorySizeLimit; - QRadioButton *m_useRelatetiveDates; - QRadioButton *m_useShortDates; - QRadioButton *m_useSymbolicPermissions; - QRadioButton *m_useNumericPermissions; - QRadioButton *m_useCombinedPermissions; }; #endif diff --git a/src/views/dolphinitemlistview.cpp b/src/views/dolphinitemlistview.cpp index a24e31347..9902f651b 100644 --- a/src/views/dolphinitemlistview.cpp +++ b/src/views/dolphinitemlistview.cpp @@ -7,6 +7,7 @@ #include "dolphinitemlistview.h" #include "dolphin_compactmodesettings.h" +#include "dolphin_contentdisplaysettings.h" #include "dolphin_detailsmodesettings.h" #include "dolphin_generalsettings.h" #include "dolphin_iconsmodesettings.h" @@ -81,6 +82,8 @@ void DolphinItemListView::readSettings() CompactModeSettings::self()->load(); DetailsModeSettings::self()->load(); + ContentDisplaySettings::self()->load(); + beginTransaction(); setEnabledSelectionToggles(m_selectionTogglesEnabled); diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index 705272308..21ca49c24 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -225,8 +225,6 @@ public: /** Returns the additional information which should be shown for the items. */ QList visibleRoles() const; - void reload(); - /** * Refreshes the view to get synchronized with the settings (e.g. icons size, * font, ...). @@ -347,6 +345,9 @@ public: bool eventFilter(QObject *watched, QEvent *event) override; public Q_SLOTS: + + void reload(); + /** * Changes the directory to \a url. If the current directory is equal to * \a url, nothing will be done (use DolphinView::reload() instead). -- cgit v1.3 From 7f3967cf38f8ab707681981dd0bc6b220c3dbf83 Mon Sep 17 00:00:00 2001 From: Méven Car Date: Thu, 15 Jun 2023 12:32:00 +0200 Subject: Hide application/x-trash files following showHidenFiles BUG: 3212 --- src/kitemviews/kfileitemmodel.cpp | 40 ++++++++++++++++++++++++- src/kitemviews/kfileitemmodel.h | 6 ++++ src/kitemviews/private/kfileitemmodelfilter.cpp | 32 ++++++++++++++++---- src/kitemviews/private/kfileitemmodelfilter.h | 8 +++++ 4 files changed, 79 insertions(+), 7 deletions(-) (limited to 'src/kitemviews/kfileitemmodel.cpp') diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 5ac6f9be1..629332703 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -105,6 +105,8 @@ KFileItemModel::KFileItemModel(QObject *parent) connect(m_resortAllItemsTimer, &QTimer::timeout, this, &KFileItemModel::resortAllItems); connect(GeneralSettings::self(), &GeneralSettings::sortingChoiceChanged, this, &KFileItemModel::slotSortingChoiceChanged); + + setShowTrashMime(m_dirLister->showHiddenFiles()); } KFileItemModel::~KFileItemModel() @@ -238,6 +240,27 @@ bool KFileItemModel::sortHiddenLast() const return m_sortHiddenLast; } +void KFileItemModel::setShowTrashMime(bool show) +{ + const auto trashMime = QStringLiteral("application/x-trash"); + QStringList excludeFilter = m_filter.excludeMimeTypes(); + bool wasShown = !excludeFilter.contains(trashMime); + + if (show) { + if (!wasShown) { + excludeFilter.removeAll(trashMime); + } + } else { + if (wasShown) { + excludeFilter.append(trashMime); + } + } + + if (wasShown != show) { + setExcludeMimeTypeFilter(excludeFilter); + } +} + void KFileItemModel::setShowHiddenFiles(bool show) { #if KIO_VERSION < QT_VERSION_CHECK(5, 100, 0) @@ -245,6 +268,7 @@ void KFileItemModel::setShowHiddenFiles(bool show) #else m_dirLister->setShowHiddenFiles(show); #endif + setShowTrashMime(show); m_dirLister->emitChanges(); if (show) { dispatchPendingItemsToInsert(); @@ -740,6 +764,20 @@ QStringList KFileItemModel::mimeTypeFilters() const return m_filter.mimeTypes(); } +void KFileItemModel::setExcludeMimeTypeFilter(const QStringList &filters) +{ + if (m_filter.excludeMimeTypes() != filters) { + dispatchPendingItemsToInsert(); + m_filter.setExcludeMimeTypes(filters); + applyFilters(); + } +} + +QStringList KFileItemModel::excludeMimeTypeFilter() const +{ + return m_filter.excludeMimeTypes(); +} + void KFileItemModel::applyFilters() { // ===STEP 1=== @@ -1823,7 +1861,7 @@ QHash KFileItemModel::retrieveData(const KFileItem &item, } if (m_requestRole[IsHiddenRole]) { - data.insert(sharedValue("isHidden"), item.isHidden()); + data.insert(sharedValue("isHidden"), item.isHidden() || item.mimetype() == QStringLiteral("application/x-trash")); } if (m_requestRole[NameRole]) { diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index 721569a0c..3c2721d8f 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -182,6 +182,9 @@ public: void setMimeTypeFilters(const QStringList &filters); QStringList mimeTypeFilters() const; + void setExcludeMimeTypeFilter(const QStringList &filters); + QStringList excludeMimeTypeFilter() const; + struct RoleInfo { QByteArray role; QString translation; @@ -199,6 +202,9 @@ public: */ static QList rolesInformation(); + /** set to true to hide application/x-trash files */ + void setShowTrashMime(bool show); + Q_SIGNALS: /** * Is emitted if the loading of a directory has been started. It is diff --git a/src/kitemviews/private/kfileitemmodelfilter.cpp b/src/kitemviews/private/kfileitemmodelfilter.cpp index f199c314f..0f9530801 100644 --- a/src/kitemviews/private/kfileitemmodelfilter.cpp +++ b/src/kitemviews/private/kfileitemmodelfilter.cpp @@ -8,6 +8,8 @@ #include +#include + #include KFileItemModelFilter::KFileItemModelFilter() @@ -56,15 +58,25 @@ QStringList KFileItemModelFilter::mimeTypes() const return m_mimeTypes; } +void KFileItemModelFilter::setExcludeMimeTypes(const QStringList &types) +{ + m_excludeMimeTypes = types; +} + +QStringList KFileItemModelFilter::excludeMimeTypes() const +{ + return m_excludeMimeTypes; +} + bool KFileItemModelFilter::hasSetFilters() const { - return (!m_pattern.isEmpty() || !m_mimeTypes.isEmpty()); + return (!m_pattern.isEmpty() || !m_mimeTypes.isEmpty() || !m_excludeMimeTypes.isEmpty()); } bool KFileItemModelFilter::matches(const KFileItem &item) const { const bool hasPatternFilter = !m_pattern.isEmpty(); - const bool hasMimeTypesFilter = !m_mimeTypes.isEmpty(); + const bool hasMimeTypesFilter = !m_mimeTypes.isEmpty() || !m_excludeMimeTypes.isEmpty(); // If no filter is set, return true. if (!hasPatternFilter && !hasMimeTypesFilter) { @@ -95,10 +107,18 @@ bool KFileItemModelFilter::matchesPattern(const KFileItem &item) const bool KFileItemModelFilter::matchesType(const KFileItem &item) const { - for (const QString &mimeType : qAsConst(m_mimeTypes)) { - if (item.mimetype() == mimeType) { - return true; - } + bool excluded = std::any_of(m_excludeMimeTypes.constBegin(), m_excludeMimeTypes.constEnd(), [item](const QString &excludeMimetype) { + return item.mimetype() == excludeMimetype; + }); + if (excluded) { + return false; + } + + bool included = std::any_of(m_mimeTypes.constBegin(), m_mimeTypes.constEnd(), [item](const QString &mimeType) { + return item.mimetype() == mimeType; + }); + if (included) { + return true; } return m_mimeTypes.isEmpty(); diff --git a/src/kitemviews/private/kfileitemmodelfilter.h b/src/kitemviews/private/kfileitemmodelfilter.h index 959590da8..ce6cbeebb 100644 --- a/src/kitemviews/private/kfileitemmodelfilter.h +++ b/src/kitemviews/private/kfileitemmodelfilter.h @@ -44,6 +44,13 @@ public: void setMimeTypes(const QStringList &types); QStringList mimeTypes() const; + /** + * Set the list of mimetypes that are used for comparison and excluded with the + * item in KFileItemModelFilter::matchesMimeType. + */ + void setExcludeMimeTypes(const QStringList &types); + QStringList excludeMimeTypes() const; + /** * @return True if either the pattern or mimetype filters has been set. */ @@ -73,5 +80,6 @@ private: // faster comparison in matches(). QString m_pattern; // Property set by setPattern(). QStringList m_mimeTypes; // Property set by setMimeTypes() + QStringList m_excludeMimeTypes; // Property set by setExcludeMimeTypes() }; #endif -- cgit v1.3