Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
fileinfothread.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include <qdiriterator.h>
7#include <qpointer.h>
8#include <qtimer.h>
9
10#include <QDebug>
11#include <QtCore/qloggingcategory.h>
12
14
15Q_STATIC_LOGGING_CATEGORY(lcFileInfoThread, "qt.labs.folderlistmodel.fileinfothread")
16
17FileInfoThread::FileInfoThread(QObject *parent)
18 : QThread(parent),
19 abort(false),
20 scanPending(false),
21#if QT_CONFIG(filesystemwatcher)
22 watcher(nullptr),
23#endif
24 sortFlags(QDir::Name),
25 needUpdate(true),
26 updateTypes(UpdateType::None),
27 showFiles(true),
28 showDirs(true),
29 showDirsFirst(false),
30 showDotAndDotDot(false),
31 showHidden(false),
32 showOnlyReadable(false),
33 caseSensitive(true)
34{
35#if QT_CONFIG(filesystemwatcher)
36 watcher = new QFileSystemWatcher(this);
37 connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
38 connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(updateFile(QString)));
39#endif // filesystemwatcher
40}
41
43{
44 QMutexLocker locker(&mutex);
45 abort = true;
46 condition.wakeOne();
47 locker.unlock();
48 wait();
49}
50
52{
53 QMutexLocker locker(&mutex);
54#if QT_CONFIG(filesystemwatcher)
55 watcher->removePaths(watcher->files());
56 watcher->removePaths(watcher->directories());
57#endif
58}
59
60void FileInfoThread::removePath(const QString &path)
61{
62 QMutexLocker locker(&mutex);
63#if QT_CONFIG(filesystemwatcher)
64 if (!path.startsWith(QLatin1Char(':')))
65 watcher->removePath(path);
66#else
67 Q_UNUSED(path);
68#endif
69 currentPath.clear();
70}
71
72void FileInfoThread::setPath(const QString &path)
73{
74 qCDebug(lcFileInfoThread) << "setPath called with path" << path;
75 Q_ASSERT(!path.isEmpty());
76
77 QMutexLocker locker(&mutex);
78#if QT_CONFIG(filesystemwatcher)
79 if (!path.startsWith(QLatin1Char(':')))
80 watcher->addPath(path);
81#endif
82 currentPath = path;
83 needUpdate = true;
85}
86
87void FileInfoThread::setRootPath(const QString &path)
88{
89 qCDebug(lcFileInfoThread) << "setRootPath called with path" << path;
90 Q_ASSERT(!path.isEmpty());
91
92 QMutexLocker locker(&mutex);
93 rootPath = path;
94}
95
96#if QT_CONFIG(filesystemwatcher)
97void FileInfoThread::dirChanged(const QString &directoryPath)
98{
99 qCDebug(lcFileInfoThread) << "dirChanged called with directoryPath" << directoryPath;
100 Q_UNUSED(directoryPath);
101 QMutexLocker locker(&mutex);
102 updateTypes |= UpdateType::Contents;
103 initiateScan();
104}
105#endif
106
107void FileInfoThread::setSortFlags(QDir::SortFlags flags)
108{
109 qCDebug(lcFileInfoThread) << "setSortFlags called with flags" << flags;
110 Q_ASSERT(flags != sortFlags);
111 QMutexLocker locker(&mutex);
112 sortFlags = flags;
113 updateTypes |= UpdateType::Sort;
114 needUpdate = true;
116}
117
118void FileInfoThread::setNameFilters(const QStringList & filters)
119{
120 qCDebug(lcFileInfoThread) << "setNameFilters called with filters" << filters;
121 QMutexLocker locker(&mutex);
122 nameFilters = filters;
123 updateTypes |= UpdateType::Contents;
125}
126
128{
129 qCDebug(lcFileInfoThread) << "setShowFiles called with show" << show;
130 QMutexLocker locker(&mutex);
131 showFiles = show;
132 updateTypes |= UpdateType::Contents;
134}
135
136void FileInfoThread::setShowDirs(bool showFolders)
137{
138 qCDebug(lcFileInfoThread) << "setShowDirs called with showFolders" << showFolders;
139 QMutexLocker locker(&mutex);
140 showDirs = showFolders;
141 updateTypes |= UpdateType::Contents;
143}
144
146{
147 qCDebug(lcFileInfoThread) << "setShowDirsFirst called with show" << show;
148 QMutexLocker locker(&mutex);
149 showDirsFirst = show;
150 updateTypes |= UpdateType::Contents;
152}
153
155{
156 qCDebug(lcFileInfoThread) << "setShowDotAndDotDot called with on" << on;
157 QMutexLocker locker(&mutex);
158 showDotAndDotDot = on;
159 updateTypes |= UpdateType::Contents;
160 needUpdate = true;
162}
163
165{
166 qCDebug(lcFileInfoThread) << "setShowHidden called with on" << on;
167 QMutexLocker locker(&mutex);
168 showHidden = on;
169 updateTypes |= UpdateType::Contents;
170 needUpdate = true;
172}
173
175{
176 qCDebug(lcFileInfoThread) << "setShowOnlyReadable called with on" << on;
177 QMutexLocker locker(&mutex);
178 showOnlyReadable = on;
179 updateTypes |= UpdateType::Contents;
181}
182
184{
185 qCDebug(lcFileInfoThread) << "setCaseSensitive called with on" << on;
186 QMutexLocker locker(&mutex);
187 caseSensitive = on;
188 updateTypes |= UpdateType::Contents;
190}
191
192#if QT_CONFIG(filesystemwatcher)
193void FileInfoThread::updateFile(const QString &path)
194{
195 qCDebug(lcFileInfoThread) << "updateFile called with path" << path;
196 Q_UNUSED(path);
197 QMutexLocker locker(&mutex);
198 updateTypes |= UpdateType::Contents;
199 initiateScan();
200}
201#endif
202
203void FileInfoThread::run()
204{
205 forever {
206 bool updateFiles = false;
207 QMutexLocker locker(&mutex);
208 if (abort) {
209 return;
210 }
211 if (currentPath.isEmpty() || !needUpdate) {
212 emit statusChanged(currentPath.isEmpty() ? QQuickFolderListModel::Null : QQuickFolderListModel::Ready);
213 condition.wait(&mutex);
214 }
215
216 if (abort) {
217 return;
218 }
219
220 if (!currentPath.isEmpty()) {
221 updateFiles = true;
222 emit statusChanged(QQuickFolderListModel::Loading);
223 }
224 if (updateFiles)
225 getFileInfos(currentPath);
226 locker.unlock();
227 }
228}
229
231{
232 if (scanPending)
233 return;
234 scanPending = true;
235 QPointer<FileInfoThread> guardedThis(this);
236
237 auto getFileInfosAsync = [guardedThis](){
238 if (!guardedThis)
239 return;
240 guardedThis->scanPending = false;
241 if (guardedThis->currentPath.isEmpty()) {
242 emit guardedThis->statusChanged(QQuickFolderListModel::Null);
243 return;
244 }
245 emit guardedThis->statusChanged(QQuickFolderListModel::Loading);
246 guardedThis->getFileInfos(guardedThis->currentPath);
247 emit guardedThis->statusChanged(QQuickFolderListModel::Ready);
248 };
249
250 QTimer::singleShot(0, getFileInfosAsync);
251}
252
254{
255#if QT_CONFIG(thread)
256 qCDebug(lcFileInfoThread) << "initiateScan is about to call condition.wakeAll()";
257 condition.wakeAll();
258#else
259 qCDebug(lcFileInfoThread) << "initiateScan is about to call runOnce()";
260 runOnce();
261#endif
262}
263
264QString fileInfoListToString(const QFileInfoList &fileInfoList)
265{
266 return fileInfoList.size() <= 10
267 ? QDebug::toString(fileInfoList)
268 : QString::fromLatin1("%1 files").arg(fileInfoList.size());
269}
270
271void FileInfoThread::getFileInfos(const QString &path)
272{
273 qCDebug(lcFileInfoThread) << "getFileInfos called with path" << path << "- updateType" << updateTypes;
274
275 QDir::Filters filter;
276 if (caseSensitive)
277 filter = QDir::CaseSensitive;
278 if (showFiles)
279 filter = filter | QDir::Files;
280 if (showDirs)
281 filter = filter | QDir::AllDirs | QDir::Drives;
282 if (!showDotAndDotDot)
283 filter = filter | QDir::NoDot | QDir::NoDotDot;
284 else if (path == rootPath)
285 filter = filter | QDir::NoDotDot;
286 if (showHidden)
287 filter = filter | QDir::Hidden;
288 if (showOnlyReadable)
289 filter = filter | QDir::Readable;
290 if (showDirsFirst)
291 sortFlags = sortFlags | QDir::DirsFirst;
292
293 QDir currentDir(path, QString(), sortFlags);
294 QList<FileProperty> filePropertyList;
295
296 const QFileInfoList fileInfoList = currentDir.entryInfoList(nameFilters, filter, sortFlags);
297
298 if (!fileInfoList.isEmpty()) {
299 filePropertyList.reserve(fileInfoList.size());
300 for (const QFileInfo &info : fileInfoList)
301 filePropertyList << FileProperty(info);
302
303 if (updateTypes & UpdateType::Contents) {
304 int fromIndex = 0;
305 int toIndex = currentFileList.size()-1;
306 findChangeRange(filePropertyList, fromIndex, toIndex);
307 currentFileList = filePropertyList;
308 qCDebug(lcFileInfoThread) << "- about to emit directoryUpdated with fromIndex" << fromIndex
309 << "toIndex" << toIndex << "fileInfoList" << fileInfoListToString(fileInfoList);
310 emit directoryUpdated(path, filePropertyList, fromIndex, toIndex);
311 } else {
312 currentFileList = filePropertyList;
313 if (updateTypes & UpdateType::Sort) {
314 qCDebug(lcFileInfoThread) << "- about to emit sortFinished - fileInfoList:"
315 << fileInfoListToString(fileInfoList);
316 emit sortFinished(filePropertyList);
317 } else {
318 qCDebug(lcFileInfoThread) << "- about to emit directoryChanged - fileInfoList:"
319 << fileInfoListToString(fileInfoList);
320 emit directoryChanged(path, filePropertyList);
321 }
322 }
323 } else {
324 // The directory is empty
325 if (updateTypes & UpdateType::Contents) {
326 int fromIndex = 0;
327 int toIndex = currentFileList.size()-1;
328 currentFileList.clear();
329 qCDebug(lcFileInfoThread) << "- directory is empty, about to emit directoryUpdated with fromIndex"
330 << fromIndex << "toIndex" << toIndex;
331 emit directoryUpdated(path, filePropertyList, fromIndex, toIndex);
332 } else {
333 currentFileList.clear();
334 qCDebug(lcFileInfoThread) << "- directory is empty, about to emit directoryChanged";
335 emit directoryChanged(path, filePropertyList);
336 }
337 }
338 updateTypes = UpdateType::None;
339 needUpdate = false;
340}
341
342void FileInfoThread::findChangeRange(const QList<FileProperty> &list, int &fromIndex, int &toIndex)
343{
344 if (currentFileList.size() == 0) {
345 fromIndex = 0;
346 toIndex = list.size();
347 return;
348 }
349
350 int i;
351 int listSize = list.size() < currentFileList.size() ? list.size() : currentFileList.size();
352 bool changeFound = false;
353
354 for (i=0; i < listSize; i++) {
355 if (list.at(i) != currentFileList.at(i)) {
356 changeFound = true;
357 break;
358 }
359 }
360
361 if (changeFound)
362 fromIndex = i;
363 else
364 fromIndex = i-1;
365
366 // For now I let the rest of the list be updated..
367 toIndex = list.size() > currentFileList.size() ? list.size() - 1 : currentFileList.size() - 1;
368}
369
370constexpr FileInfoThread::UpdateTypes operator|(FileInfoThread::UpdateType f1, FileInfoThread::UpdateTypes f2) noexcept
371{
372 return f2 | f1;
373}
374
375constexpr FileInfoThread::UpdateTypes operator&(FileInfoThread::UpdateType f1, FileInfoThread::UpdateTypes f2) noexcept
376{
377 return f2 & f1;
378}
379
380QT_END_NAMESPACE
381
382#include "moc_fileinfothread_p.cpp"
void removePath(const QString &path)
void setRootPath(const QString &path)
void setShowFiles(bool show)
void setShowDotAndDotDot(bool on)
void setNameFilters(const QStringList &nameFilters)
void setShowDirsFirst(bool show)
void setCaseSensitive(bool on)
void setShowDirs(bool showFolders)
void findChangeRange(const QList< FileProperty > &list, int &fromIndex, int &toIndex)
void setShowHidden(bool on)
void getFileInfos(const QString &path)
void setShowOnlyReadable(bool on)
void setPath(const QString &path)
void setSortFlags(QDir::SortFlags flags)
QString fileInfoListToString(const QFileInfoList &fileInfoList)
constexpr FileInfoThread::UpdateTypes operator|(FileInfoThread::UpdateType f1, FileInfoThread::UpdateTypes f2) noexcept
constexpr FileInfoThread::UpdateTypes operator&(FileInfoThread::UpdateType f1, FileInfoThread::UpdateTypes f2) noexcept