Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qfileinfogatherer.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 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
5#include <qcoreapplication.h>
6#include <qdebug.h>
7#include <qdirlisting.h>
8#include <private/qabstractfileiconprovider_p.h>
9#include <private/qfileinfo_p.h>
10#ifndef Q_OS_WIN
11# include <unistd.h>
12# include <sys/types.h>
13#endif
14#if defined(Q_OS_VXWORKS)
15# include "qplatformdefs.h"
16#endif
17
19
20using namespace Qt::StringLiterals;
21
22#ifdef QT_BUILD_INTERNAL
23Q_CONSTINIT static QBasicAtomicInt fetchedRoot = Q_BASIC_ATOMIC_INITIALIZER(false);
24Q_AUTOTEST_EXPORT void qt_test_resetFetchedRoot()
25{
26 fetchedRoot.storeRelaxed(false);
27}
28
29Q_AUTOTEST_EXPORT bool qt_test_isFetchedRoot()
30{
31 return fetchedRoot.loadRelaxed();
32}
33#endif
34
36{
37 QString driveName = drive.absoluteFilePath();
38#ifdef Q_OS_WIN
39 if (driveName.startsWith(u'/')) // UNC host
40 return drive.fileName();
41 if (driveName.endsWith(u'/'))
42 driveName.chop(1);
43#endif // Q_OS_WIN
44 return driveName;
45}
46
51 : QThread(parent)
52 , m_iconProvider(&defaultProvider)
53{
55}
56
65
67{
68 if (event->type() == QEvent::DeferredDelete && isRunning()) {
69 // We have been asked to shut down later but were blocked,
70 // so the owning QFileSystemModel proceeded with its shut-down
71 // and deferred the destruction of the gatherer.
72 // If we are still blocked now, then we have three bad options:
73 // terminate, wait forever (preventing the process from shutting down),
74 // or accept a memory leak.
76 if (!wait(5000)) {
77 // If the application is shutting down, then we terminate.
78 // Otherwise assume that sooner or later the thread will finish,
79 // and we delete it then.
81 terminate();
82 else
83 connect(this, &QThread::finished, this, [this]{ delete this; });
84 return true;
85 }
86 }
87
88 return QThread::event(event);
89}
90
92{
94 QMutexLocker locker(&mutex);
95 condition.wakeAll();
96}
97
99{
101#ifdef Q_OS_WIN
102 m_resolveSymlinks = enable;
103#endif
104}
105
106void QFileInfoGatherer::driveAdded()
107{
109}
110
111void QFileInfoGatherer::driveRemoved()
112{
113 QStringList drives;
114 const QFileInfoList driveInfoList = QDir::drives();
115 for (const QFileInfo &fi : driveInfoList)
116 drives.append(translateDriveName(fi));
117 emit newListOfFiles(QString(), drives);
118}
119
121{
122#ifdef Q_OS_WIN
123 return m_resolveSymlinks;
124#else
125 return false;
126#endif
127}
128
130{
131 m_iconProvider = provider;
132}
133
135{
136 return m_iconProvider;
137}
138
145{
146 QMutexLocker locker(&mutex);
147 // See if we already have this dir/file in our queue
148 qsizetype loc = 0;
149 while ((loc = this->path.lastIndexOf(path, loc - 1)) != -1) {
150 if (this->files.at(loc) == files)
151 return;
152 }
153
154#if QT_CONFIG(thread)
155 this->path.push(path);
156 this->files.push(files);
157 condition.wakeAll();
158#else // !QT_CONFIG(thread)
159 getFileInfos(path, files);
160#endif // QT_CONFIG(thread)
161
162#if QT_CONFIG(filesystemwatcher)
163 if (files.isEmpty()
164 && !path.isEmpty()
165 && !path.startsWith("//"_L1) /*don't watch UNC path*/) {
168 }
169#endif
170}
171
178{
179 QString dir = filePath.mid(0, filePath.lastIndexOf(u'/'));
180 QString fileName = filePath.mid(dir.size() + 1);
182}
183
185{
186#if QT_CONFIG(filesystemwatcher)
187 if (m_watcher)
188 return m_watcher->files();
189#endif
190 return {};
191}
192
194{
195#if QT_CONFIG(filesystemwatcher)
196 if (m_watcher)
197 return m_watcher->directories();
198#endif
199 return {};
200}
201
202void QFileInfoGatherer::createWatcher()
203{
204#if QT_CONFIG(filesystemwatcher)
205 m_watcher = new QFileSystemWatcher(this);
208# if defined(Q_OS_WIN)
209 const QVariant listener = m_watcher->property("_q_driveListener");
210 if (listener.canConvert<QObject *>()) {
211 if (QObject *driveListener = listener.value<QObject *>()) {
212 connect(driveListener, SIGNAL(driveAdded()), this, SLOT(driveAdded()));
213 connect(driveListener, SIGNAL(driveRemoved()), this, SLOT(driveRemoved()));
214 }
215 }
216# endif // Q_OS_WIN
217#endif
218}
219
221{
222#if QT_CONFIG(filesystemwatcher)
223 if (m_watching) {
224 if (m_watcher == nullptr)
225 createWatcher();
226 m_watcher->addPaths(paths);
227 }
228#else
230#endif
231}
232
234{
235#if QT_CONFIG(filesystemwatcher)
236 if (m_watcher && !paths.isEmpty())
237 m_watcher->removePaths(paths);
238#else
240#endif
241}
242
244{
245 bool result = false;
246#if QT_CONFIG(filesystemwatcher)
247 QMutexLocker locker(&mutex);
248 result = m_watching;
249#endif
250 return result;
251}
252
263{
264#if QT_CONFIG(filesystemwatcher)
265 QMutexLocker locker(&mutex);
266 if (v != m_watching) {
267 m_watching = v;
268 if (!m_watching)
269 delete std::exchange(m_watcher, nullptr);
270 }
271#else
272 Q_UNUSED(v);
273#endif
274}
275
276/*
277 List all files in \a directoryPath
278
279 \sa listed()
280*/
282{
283#if QT_CONFIG(filesystemwatcher)
284 QMutexLocker locker(&mutex);
287#endif
288}
289
290/*
291 Remove a \a path from the watcher
292
293 \sa listed()
294*/
296{
297#if QT_CONFIG(filesystemwatcher)
298 QMutexLocker locker(&mutex);
300#else
301 Q_UNUSED(path);
302#endif
303}
304
305/*
306 List all files in \a directoryPath
307
308 \sa listed()
309*/
310void QFileInfoGatherer::list(const QString &directoryPath)
311{
312 fetchExtendedInformation(directoryPath, QStringList());
313}
314
315/*
316 Until aborted wait to fetch a directory or files
317*/
319{
320 forever {
321 // Disallow termination while we are holding a mutex or can be
322 // woken up cleanly.
324 QMutexLocker locker(&mutex);
325 while (!isInterruptionRequested() && path.isEmpty())
326 condition.wait(&mutex);
328 return;
329 const QString thisPath = std::as_const(path).front();
330 path.pop_front();
331 const QStringList thisList = std::as_const(files).front();
332 files.pop_front();
333 locker.unlock();
334
335 // Some of the system APIs we call when gathering file infomration
336 // might hang (e.g. waiting for network), so we explicitly allow
337 // termination now.
339 getFileInfos(thisPath, thisList);
340 }
341}
342
344{
345 QExtendedInformation info(fileInfo);
346 if (m_iconProvider) {
347 info.icon = m_iconProvider->icon(fileInfo);
348 info.displayType = m_iconProvider->type(fileInfo);
349 } else {
351 }
352#if QT_CONFIG(filesystemwatcher)
353 // ### Not ready to listen all modifications by default
354 static const bool watchFiles = qEnvironmentVariableIsSet("QT_FILESYSTEMMODEL_WATCH_FILES");
355 if (watchFiles) {
356 if (!fileInfo.exists() && !fileInfo.isSymLink()) {
357 const_cast<QFileInfoGatherer *>(this)->
359 } else {
360 const QString path = fileInfo.absoluteFilePath();
361 if (!path.isEmpty() && fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable()
362 && !watchedFiles().contains(path)) {
363 const_cast<QFileInfoGatherer *>(this)->watchPaths(QStringList(path));
364 }
365 }
366 }
367#endif // filesystemwatcher
368
369#ifdef Q_OS_WIN
370 if (m_resolveSymlinks && info.isSymLink(/* ignoreNtfsSymLinks = */ true)) {
371 QFileInfo resolvedInfo(QFileInfo(fileInfo.symLinkTarget()).canonicalFilePath());
372 if (resolvedInfo.exists()) {
373 emit nameResolved(fileInfo.filePath(), resolvedInfo.fileName());
374 }
375 }
376#endif
377 return info;
378}
379
380/*
381 Get specific file info's, batch the files so update when we have 100
382 items and every 200ms after that
383 */
384void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &files)
385{
386 // List drives
387 if (path.isEmpty()) {
388#ifdef QT_BUILD_INTERNAL
389 fetchedRoot.storeRelaxed(true);
390#endif
391 QList<std::pair<QString, QFileInfo>> updatedFiles;
392 auto addToUpdatedFiles = [&updatedFiles](QFileInfo &&fileInfo) {
393 fileInfo.stat();
394 updatedFiles.emplace_back(std::pair{translateDriveName(fileInfo), fileInfo});
395 };
396
397 if (files.isEmpty()) {
398 // QDir::drives() calls QFSFileEngine::drives() which creates the QFileInfoList on
399 // the stack and return it, so this list is not shared, so no detaching.
400 QFileInfoList infoList = QDir::drives();
401 updatedFiles.reserve(infoList.size());
402 for (auto rit = infoList.rbegin(), rend = infoList.rend(); rit != rend; ++rit)
403 addToUpdatedFiles(std::move(*rit));
404 } else {
405 updatedFiles.reserve(files.size());
406 for (auto rit = files.crbegin(), rend = files.crend(); rit != rend; ++rit)
407 addToUpdatedFiles(QFileInfo(*rit));
408 }
409 emit updates(path, updatedFiles);
410 return;
411 }
412
414 base.start();
415 QFileInfo fileInfo;
416 bool firstTime = true;
417 QList<std::pair<QString, QFileInfo>> updatedFiles;
418 QStringList filesToCheck = files;
419
420 QStringList allFiles;
421 if (files.isEmpty()) {
422 constexpr auto dirFilters = QDir::AllEntries | QDir::System | QDir::Hidden;
423 for (const auto &dirEntry : QDirListing(path, dirFilters)) {
425 break;
426 fileInfo = dirEntry.fileInfo();
427 fileInfo.stat();
428 allFiles.append(fileInfo.fileName());
429 fetch(fileInfo, base, firstTime, updatedFiles, path);
430 }
431 }
432 if (!allFiles.isEmpty())
433 emit newListOfFiles(path, allFiles);
434
435 QStringList::const_iterator filesIt = filesToCheck.constBegin();
436 while (!isInterruptionRequested() && filesIt != filesToCheck.constEnd()) {
437 fileInfo.setFile(path + QDir::separator() + *filesIt);
438 ++filesIt;
439 fileInfo.stat();
440 fetch(fileInfo, base, firstTime, updatedFiles, path);
441 }
442 if (!updatedFiles.isEmpty())
443 emit updates(path, updatedFiles);
445}
446
447void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QElapsedTimer &base, bool &firstTime,
448 QList<std::pair<QString, QFileInfo>> &updatedFiles, const QString &path)
449{
450 updatedFiles.emplace_back(std::pair(fileInfo.fileName(), fileInfo));
451 QElapsedTimer current;
452 current.start();
453 if ((firstTime && updatedFiles.size() > 100) || base.msecsTo(current) > 1000) {
454 emit updates(path, updatedFiles);
455 updatedFiles.clear();
456 base = current;
457 firstTime = false;
458 }
459}
460
462
463#include "moc_qfileinfogatherer_p.cpp"
static QString getFileType(const QFileInfo &info)
virtual QIcon icon(IconType) const
Returns an icon set for the given type, using the current icon theme.
virtual QString type(const QFileInfo &) const
Returns the type of the file described by info.
static bool closingDown()
Returns true if the application objects are being destroyed; otherwise returns false.
The QDirListing class provides an STL-style iterator for directory entries.
Definition qdirlisting.h:18
static QChar separator()
Returns the native directory separator: "/" under Unix and "\\" under Windows.
Definition qdir.h:209
static QFileInfoList drives()
Returns a list of the root directories on this system.
Definition qdir.cpp:1987
@ Hidden
Definition qdir.h:35
@ AllEntries
Definition qdir.h:26
@ System
Definition qdir.h:36
\inmodule QtCore
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
\inmodule QtCore
Definition qcoreevent.h:45
@ DeferredDelete
Definition qcoreevent.h:100
QAbstractFileIconProvider * iconProvider() const
QStringList watchedFiles() const
void list(const QString &directoryPath)
QFileInfoGatherer(QObject *parent=nullptr)
Creates thread.
QStringList watchedDirectories() const
void setIconProvider(QAbstractFileIconProvider *provider)
void newListOfFiles(const QString &directory, const QStringList &listOfFiles) const
void updates(const QString &directory, const QList< std::pair< QString, QFileInfo > > &updates)
void unwatchPaths(const QStringList &paths)
void fetchExtendedInformation(const QString &path, const QStringList &files)
Fetch extended information for all files in path.
void setResolveSymlinks(bool enable)
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
void nameResolved(const QString &fileName, const QString &resolvedName) const
void directoryLoaded(const QString &path)
void updateFile(const QString &path)
Fetch extended information for all filePath.
void watchPaths(const QStringList &paths)
QExtendedInformation getInfo(const QFileInfo &info) const
~QFileInfoGatherer()
Destroys thread.
void removePath(const QString &path)
bool isSymLink() const
QString symLinkTarget() const
void stat()
Reads all attributes from the file system.
QString fileName() const
void setFile(const QString &file)
QString absoluteFilePath() const
bool isFile() const
Returns true if this object points to a file or to a symbolic link to a file.
QString canonicalFilePath() const
Returns the file system entry's canonical path, including the entry's name, that is,...
QString filePath() const
Returns the path of the file system entry this QFileInfo refers to; the path may be absolute or relat...
bool exists() const
Returns true if the file system entry this QFileInfo refers to exists; otherwise returns false.
bool isReadable() const
Returns true if the user can read the file system entry this QFileInfo refers to; otherwise returns f...
void fileChanged(const QString &path, QPrivateSignal)
This signal is emitted when the file at the specified path is modified, renamed or removed from disk.
void directoryChanged(const QString &path, QPrivateSignal)
This signal is emitted when the directory at a specified path is modified (e.g., when a file is added...
void pop_front() noexcept
Definition qlist.h:680
\inmodule QtCore
Definition qmutex.h:313
void unlock() noexcept
Unlocks this mutex locker.
Definition qmutex.h:319
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
QChar front() const
Definition qstring.h:230
void terminate()
Definition qthread.cpp:1003
bool isInterruptionRequested() const
Definition qthread.cpp:1075
bool isRunning() const
Definition qthread.cpp:1064
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition qthread.cpp:1029
static void setTerminationEnabled(bool enabled=true)
Definition qthread.cpp:1080
@ LowPriority
Definition qthread.h:44
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
Definition qthread.cpp:1023
void finished(QPrivateSignal)
void requestInterruption()
Definition qthread.cpp:1070
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
bool canConvert(QMetaType targetType) const
Definition qvariant.h:345
list append(new Employee("Blackpool", "Stephen"))
Combined button and popup list for selecting options.
#define Q_BASIC_ATOMIC_INITIALIZER(a)
QList< QString > QStringList
Constructs a string list that contains the given string, str.
static QString translateDriveName(const QFileInfo &drive)
#define forever
Definition qforeach.h:78
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLsizei const GLfloat * v
[13]
GLenum condition
GLsizei const GLuint * paths
GLboolean enable
GLuint start
struct _cl_event * event
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
#define Q_AUTOTEST_EXPORT
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
#define emit
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
static const uint base
Definition qurlidna.cpp:20
QString dir
[11]
QStringList files
[8]
QHostInfo info
[0]