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