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 showDot(false),
32 showDotDot(false),
33 showHidden(false),
34 showOnlyReadable(false),
35 caseSensitive(true)
36{
37#if QT_CONFIG(filesystemwatcher)
38 watcher = new QFileSystemWatcher(this);
39 connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
40 connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(updateFile(QString)));
41#endif // filesystemwatcher
42}
43
45{
46 QMutexLocker locker(&mutex);
47 abort = true;
48 condition.wakeOne();
49 locker.unlock();
50 wait();
51}
52
54{
55 QMutexLocker locker(&mutex);
56#if QT_CONFIG(filesystemwatcher)
57 watcher->removePaths(watcher->files());
58 watcher->removePaths(watcher->directories());
59#endif
60}
61
62void FileInfoThread::removePath(const QString &path)
63{
64 QMutexLocker locker(&mutex);
65#if QT_CONFIG(filesystemwatcher)
66 if (!path.startsWith(QLatin1Char(':')))
67 watcher->removePath(path);
68#else
69 Q_UNUSED(path);
70#endif
71 currentPath.clear();
72}
73
74void FileInfoThread::setPath(const QString &path)
75{
76 qCDebug(lcFileInfoThread) << "setPath called with path" << path;
77 Q_ASSERT(!path.isEmpty());
78
79 QMutexLocker locker(&mutex);
80#if QT_CONFIG(filesystemwatcher)
81 if (!path.startsWith(QLatin1Char(':')))
82 watcher->addPath(path);
83#endif
84 currentPath = path;
85 needUpdate = true;
87}
88
89void FileInfoThread::setRootPath(const QString &path)
90{
91 qCDebug(lcFileInfoThread) << "setRootPath called with path" << path;
92 Q_ASSERT(!path.isEmpty());
93
94 QMutexLocker locker(&mutex);
95 rootPath = path;
96}
97
98#if QT_CONFIG(filesystemwatcher)
99void FileInfoThread::dirChanged(const QString &directoryPath)
100{
101 qCDebug(lcFileInfoThread) << "dirChanged called with directoryPath" << directoryPath;
102 Q_UNUSED(directoryPath);
103 QMutexLocker locker(&mutex);
104 updateTypes |= UpdateType::Contents;
105 initiateScan();
106}
107#endif
108
109void FileInfoThread::setSortFlags(QDir::SortFlags flags)
110{
111 qCDebug(lcFileInfoThread) << "setSortFlags called with flags" << flags;
112 Q_ASSERT(flags != sortFlags);
113 QMutexLocker locker(&mutex);
114 sortFlags = flags;
115 updateTypes |= UpdateType::Sort;
116 needUpdate = true;
118}
119
120void FileInfoThread::setNameFilters(const QStringList & filters)
121{
122 qCDebug(lcFileInfoThread) << "setNameFilters called with filters" << filters;
123 QMutexLocker locker(&mutex);
124 nameFilters = filters;
125 updateTypes |= UpdateType::Contents;
127}
128
130{
131 qCDebug(lcFileInfoThread) << "setShowFiles called with show" << show;
132 QMutexLocker locker(&mutex);
133 showFiles = show;
134 updateTypes |= UpdateType::Contents;
136}
137
138void FileInfoThread::setShowDirs(bool showFolders)
139{
140 qCDebug(lcFileInfoThread) << "setShowDirs called with showFolders" << showFolders;
141 QMutexLocker locker(&mutex);
142 showDirs = showFolders;
143 updateTypes |= UpdateType::Contents;
145}
146
148{
149 qCDebug(lcFileInfoThread) << "setShowDirsFirst called with show" << show;
150 QMutexLocker locker(&mutex);
151 showDirsFirst = show;
152 updateTypes |= UpdateType::Contents;
154}
155
157{
158 qCDebug(lcFileInfoThread) << "setShowDotAndDotDot called with on" << on;
159 QMutexLocker locker(&mutex);
160 showDotAndDotDot = on;
161 updateTypes |= UpdateType::Contents;
162 needUpdate = true;
164}
165
167{
168 qCDebug(lcFileInfoThread) << "setShowDot called with on" << on;
169 QMutexLocker locker(&mutex);
170 showDot = on;
171 updateTypes |= UpdateType::Contents;
172 needUpdate = true;
174}
175
177{
178 qCDebug(lcFileInfoThread) << "setShowDotDot called with on" << on;
179 QMutexLocker locker(&mutex);
180 showDotDot = on;
181 updateTypes |= UpdateType::Contents;
182 needUpdate = true;
184}
185
187{
188 qCDebug(lcFileInfoThread) << "setShowHidden called with on" << on;
189 QMutexLocker locker(&mutex);
190 showHidden = on;
191 updateTypes |= UpdateType::Contents;
192 needUpdate = true;
194}
195
197{
198 qCDebug(lcFileInfoThread) << "setShowOnlyReadable called with on" << on;
199 QMutexLocker locker(&mutex);
200 showOnlyReadable = on;
201 updateTypes |= UpdateType::Contents;
203}
204
206{
207 qCDebug(lcFileInfoThread) << "setCaseSensitive called with on" << on;
208 QMutexLocker locker(&mutex);
209 caseSensitive = on;
210 updateTypes |= UpdateType::Contents;
212}
213
214#if QT_CONFIG(filesystemwatcher)
215void FileInfoThread::updateFile(const QString &path)
216{
217 qCDebug(lcFileInfoThread) << "updateFile called with path" << path;
218 Q_UNUSED(path);
219 QMutexLocker locker(&mutex);
220 updateTypes |= UpdateType::Contents;
221 initiateScan();
222}
223#endif
224
225void FileInfoThread::run()
226{
227 forever {
228 bool updateFiles = false;
229 QMutexLocker locker(&mutex);
230 if (abort) {
231 return;
232 }
233 if (currentPath.isEmpty() || !needUpdate) {
234 emit statusChanged(currentPath.isEmpty() ? QQuickFolderListModel::Null : QQuickFolderListModel::Ready);
235 condition.wait(&mutex);
236 }
237
238 if (abort) {
239 return;
240 }
241
242 if (!currentPath.isEmpty()) {
243 updateFiles = true;
244 emit statusChanged(QQuickFolderListModel::Loading);
245 }
246 if (updateFiles)
247 getFileInfos(currentPath);
248 locker.unlock();
249 }
250}
251
253{
254 if (scanPending)
255 return;
256 scanPending = true;
257 QPointer<FileInfoThread> guardedThis(this);
258
259 auto getFileInfosAsync = [guardedThis](){
260 if (!guardedThis)
261 return;
262 guardedThis->scanPending = false;
263 if (guardedThis->currentPath.isEmpty()) {
264 emit guardedThis->statusChanged(QQuickFolderListModel::Null);
265 return;
266 }
267 emit guardedThis->statusChanged(QQuickFolderListModel::Loading);
268 guardedThis->getFileInfos(guardedThis->currentPath);
269 emit guardedThis->statusChanged(QQuickFolderListModel::Ready);
270 };
271
272 QTimer::singleShot(0, getFileInfosAsync);
273}
274
276{
277#if QT_CONFIG(thread)
278 qCDebug(lcFileInfoThread) << "initiateScan is about to call condition.wakeAll()";
279 condition.wakeAll();
280#else
281 qCDebug(lcFileInfoThread) << "initiateScan is about to call runOnce()";
282 runOnce();
283#endif
284}
285
286QString fileInfoListToString(const QFileInfoList &fileInfoList)
287{
288 return fileInfoList.size() <= 10
289 ? QDebug::toString(fileInfoList)
290 : QString::fromLatin1("%1 files").arg(fileInfoList.size());
291}
292
293void FileInfoThread::getFileInfos(const QString &path)
294{
295 qCDebug(lcFileInfoThread) << "getFileInfos called with path" << path << "- updateType" << updateTypes;
296
297 QDir::Filters filter;
298 if (caseSensitive)
299 filter = QDir::CaseSensitive;
300 if (showFiles)
301 filter = filter | QDir::Files;
302 if (showDirs)
303 filter = filter | QDir::AllDirs | QDir::Drives;
304 if (!showDot && !showDotAndDotDot)
305 filter = filter | QDir::NoDot;
306 if (path == rootPath || (!showDotDot && !showDotAndDotDot))
307 filter = filter | QDir::NoDotDot;
308 if (showHidden)
309 filter = filter | QDir::Hidden;
310 if (showOnlyReadable)
311 filter = filter | QDir::Readable;
312 if (showDirsFirst)
313 sortFlags = sortFlags | QDir::DirsFirst;
314
315 QDir currentDir(path, QString(), sortFlags);
316 QList<FileProperty> filePropertyList;
317
318 const QFileInfoList fileInfoList = currentDir.entryInfoList(nameFilters, filter, sortFlags);
319
320 if (!fileInfoList.isEmpty()) {
321 filePropertyList.reserve(fileInfoList.size());
322 for (const QFileInfo &info : fileInfoList)
323 filePropertyList << FileProperty(info);
324
325 if (updateTypes & UpdateType::Contents) {
326 int fromIndex = 0;
327 int toIndex = currentFileList.size()-1;
328 findChangeRange(filePropertyList, fromIndex, toIndex);
329 currentFileList = filePropertyList;
330 qCDebug(lcFileInfoThread) << "- about to emit directoryUpdated with fromIndex" << fromIndex
331 << "toIndex" << toIndex << "fileInfoList" << fileInfoListToString(fileInfoList);
332 emit directoryUpdated(path, filePropertyList, fromIndex, toIndex);
333 } else {
334 currentFileList = filePropertyList;
335 if (updateTypes & UpdateType::Sort) {
336 qCDebug(lcFileInfoThread) << "- about to emit sortFinished - fileInfoList:"
337 << fileInfoListToString(fileInfoList);
338 emit sortFinished(filePropertyList);
339 } else {
340 qCDebug(lcFileInfoThread) << "- about to emit directoryChanged - fileInfoList:"
341 << fileInfoListToString(fileInfoList);
342 emit directoryChanged(path, filePropertyList);
343 }
344 }
345 } else {
346 // The directory is empty
347 if (updateTypes & UpdateType::Contents) {
348 int fromIndex = 0;
349 int toIndex = currentFileList.size()-1;
350 currentFileList.clear();
351 qCDebug(lcFileInfoThread) << "- directory is empty, about to emit directoryUpdated with fromIndex"
352 << fromIndex << "toIndex" << toIndex;
353 emit directoryUpdated(path, filePropertyList, fromIndex, toIndex);
354 } else {
355 currentFileList.clear();
356 qCDebug(lcFileInfoThread) << "- directory is empty, about to emit directoryChanged";
357 emit directoryChanged(path, filePropertyList);
358 }
359 }
360 updateTypes = UpdateType::None;
361 needUpdate = false;
362}
363
364void FileInfoThread::findChangeRange(const QList<FileProperty> &list, int &fromIndex, int &toIndex)
365{
366 if (currentFileList.size() == 0) {
367 fromIndex = 0;
368 toIndex = list.size();
369 return;
370 }
371
372 int i;
373 int listSize = list.size() < currentFileList.size() ? list.size() : currentFileList.size();
374 bool changeFound = false;
375
376 for (i=0; i < listSize; i++) {
377 if (list.at(i) != currentFileList.at(i)) {
378 changeFound = true;
379 break;
380 }
381 }
382
383 if (changeFound)
384 fromIndex = i;
385 else
386 fromIndex = i-1;
387
388 // For now I let the rest of the list be updated..
389 toIndex = list.size() > currentFileList.size() ? list.size() - 1 : currentFileList.size() - 1;
390}
391
392constexpr FileInfoThread::UpdateTypes operator|(FileInfoThread::UpdateType f1, FileInfoThread::UpdateTypes f2) noexcept
393{
394 return f2 | f1;
395}
396
397constexpr FileInfoThread::UpdateTypes operator&(FileInfoThread::UpdateType f1, FileInfoThread::UpdateTypes f2) noexcept
398{
399 return f2 & f1;
400}
401
402QT_END_NAMESPACE
403
404#include "moc_fileinfothread_p.cpp"
void removePath(const QString &path)
void setRootPath(const QString &path)
void setShowFiles(bool show)
void setShowDotAndDotDot(bool on)
void setShowDotDot(bool on)
void setShowDot(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
Combined button and popup list for selecting options.