]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kdirectorycontentscounter.cpp
Merge remote-tracking branch 'origin/release/21.08'
[dolphin.git] / src / kitemviews / private / kdirectorycontentscounter.cpp
1 /*
2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
3 * SPDX-FileCopyrightText: 2013 Frank Reininghaus <frank78ac@googlemail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "kdirectorycontentscounter.h"
9 #include "kitemviews/kfileitemmodel.h"
10
11 #include <KDirWatch>
12
13 #include <QFileInfo>
14 #include <QDir>
15 #include <QThread>
16
17 namespace {
18 /// cache of directory counting result
19 static QHash<QString, QPair<int, long>> *s_cache;
20 }
21
22 KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObject* parent) :
23 QObject(parent),
24 m_model(model),
25 m_queue(),
26 m_worker(nullptr),
27 m_workerIsBusy(false),
28 m_dirWatcher(nullptr),
29 m_watchedDirs()
30 {
31 connect(m_model, &KFileItemModel::itemsRemoved,
32 this, &KDirectoryContentsCounter::slotItemsRemoved);
33
34 if (!m_workerThread) {
35 m_workerThread = new QThread();
36 m_workerThread->start();
37 }
38
39 if (s_cache == nullptr) {
40 s_cache = new QHash<QString, QPair<int, long>>();
41 }
42
43 m_worker = new KDirectoryContentsCounterWorker();
44 m_worker->moveToThread(m_workerThread);
45
46 connect(this, &KDirectoryContentsCounter::requestDirectoryContentsCount,
47 m_worker, &KDirectoryContentsCounterWorker::countDirectoryContents);
48 connect(m_worker, &KDirectoryContentsCounterWorker::result,
49 this, &KDirectoryContentsCounter::slotResult);
50
51 m_dirWatcher = new KDirWatch(this);
52 connect(m_dirWatcher, &KDirWatch::dirty, this, &KDirectoryContentsCounter::slotDirWatchDirty);
53 }
54
55 KDirectoryContentsCounter::~KDirectoryContentsCounter()
56 {
57 if (m_workerThread->isRunning()) {
58 // The worker thread will continue running. It could even be running
59 // a method of m_worker at the moment, so we delete it using
60 // deleteLater() to prevent a crash.
61 m_worker->deleteLater();
62 } else {
63 // There are no remaining workers -> stop the worker thread.
64 m_workerThread->quit();
65 m_workerThread->wait();
66 delete m_workerThread;
67 m_workerThread = nullptr;
68
69 // The worker thread has finished running now, so it's safe to delete
70 // m_worker. deleteLater() would not work at all because the event loop
71 // which would deliver the event to m_worker is not running any more.
72 delete m_worker;
73 }
74 }
75
76 void KDirectoryContentsCounter::scanDirectory(const QString& path)
77 {
78 startWorker(path);
79 }
80
81 void KDirectoryContentsCounter::slotResult(const QString& path, int count, long size)
82 {
83 m_workerIsBusy = false;
84
85 const QFileInfo info = QFileInfo(path);
86 const QString resolvedPath = info.canonicalFilePath();
87
88 if (!m_dirWatcher->contains(resolvedPath)) {
89 m_dirWatcher->addDir(resolvedPath);
90 m_watchedDirs.insert(resolvedPath);
91 }
92
93 if (!m_priorityQueue.empty()) {
94 const QString firstPath = m_priorityQueue.front();
95 m_priorityQueue.pop_front();
96 startWorker(firstPath);
97 } else if (!m_queue.empty()) {
98 const QString firstPath = m_queue.front();
99 m_queue.pop_front();
100 startWorker(firstPath);
101 }
102
103 if (s_cache->contains(resolvedPath)) {
104 const auto pair = s_cache->value(resolvedPath);
105 if (pair.first == count && pair.second == size) {
106 // no change no need to send another result event
107 return;
108 }
109 }
110
111 if (info.dir().path() == m_model->rootItem().url().path()) {
112 // update cache or overwrite value
113 // when path is a direct children of the current model root
114 s_cache->insert(resolvedPath, QPair<int, long>(count, size));
115 }
116
117 // sends the results
118 Q_EMIT result(path, count, size);
119 }
120
121 void KDirectoryContentsCounter::slotDirWatchDirty(const QString& path)
122 {
123 const int index = m_model->index(QUrl::fromLocalFile(path));
124 if (index >= 0) {
125 if (!m_model->fileItem(index).isDir()) {
126 // If INotify is used, KDirWatch issues the dirty() signal
127 // also for changed files inside the directory, even if we
128 // don't enable this behavior explicitly (see bug 309740).
129 return;
130 }
131
132 startWorker(path);
133 }
134 }
135
136 void KDirectoryContentsCounter::slotItemsRemoved()
137 {
138 const bool allItemsRemoved = (m_model->count() == 0);
139
140 if (!m_watchedDirs.isEmpty()) {
141 // Don't let KDirWatch watch for removed items
142 if (allItemsRemoved) {
143 for (const QString& path : qAsConst(m_watchedDirs)) {
144 m_dirWatcher->removeDir(path);
145 }
146 m_watchedDirs.clear();
147 m_queue.clear();
148 } else {
149 QMutableSetIterator<QString> it(m_watchedDirs);
150 while (it.hasNext()) {
151 const QString& path = it.next();
152 if (m_model->index(QUrl::fromLocalFile(path)) < 0) {
153 m_dirWatcher->removeDir(path);
154 it.remove();
155 }
156 }
157 }
158 }
159 }
160
161 void KDirectoryContentsCounter::startWorker(const QString& path)
162 {
163 const QString resolvedPath = QFileInfo(path).canonicalFilePath();
164 const bool alreadyInCache = s_cache->contains(resolvedPath);
165 if (alreadyInCache) {
166 // fast path when in cache
167 // will be updated later if result has changed
168 const auto pair = s_cache->value(resolvedPath);
169 Q_EMIT result(path, pair.first, pair.second);
170 }
171
172 if (m_workerIsBusy) {
173 if (std::find(m_queue.begin(), m_queue.end(), path) == m_queue.end() &&
174 std::find(m_priorityQueue.begin(), m_priorityQueue.end(), path) == m_priorityQueue.end()) {
175 if (alreadyInCache) {
176 m_queue.push_back(path);
177 } else {
178 // append to priority queue
179 m_priorityQueue.push_back(path);
180 }
181 }
182 } else {
183 KDirectoryContentsCounterWorker::Options options;
184
185 if (m_model->showHiddenFiles()) {
186 options |= KDirectoryContentsCounterWorker::CountHiddenFiles;
187 }
188
189 if (m_model->showDirectoriesOnly()) {
190 options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly;
191 }
192
193 Q_EMIT requestDirectoryContentsCount(path, options);
194 m_workerIsBusy = true;
195 }
196 }
197
198 QThread* KDirectoryContentsCounter::m_workerThread = nullptr;