1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qfilesystemmodel_p.h"
41#include "qfilesystemmodel.h"
42#include <qlocale.h>
43#include <qmimedata.h>
44#include <qurl.h>
45#include <qdebug.h>
46#if QT_CONFIG(messagebox)
47#include <qmessagebox.h>
48#endif
49#include <qapplication.h>
50#include <QtCore/qcollator.h>
51#if QT_CONFIG(regularexpression)
52# include <QtCore/qregularexpression.h>
53#endif
54
55#include <algorithm>
56
57#ifdef Q_OS_WIN
58# include <QtCore/QVarLengthArray>
59# include <qt_windows.h>
60# ifndef Q_OS_WINRT
61# include <shlobj.h>
62# endif
63#endif
64
65QT_BEGIN_NAMESPACE
66
67/*!
68 \enum QFileSystemModel::Roles
69 \value FileIconRole
70 \value FilePathRole
71 \value FileNameRole
72 \value FilePermissions
73*/
74
75/*!
76 \class QFileSystemModel
77 \since 4.4
78
79 \brief The QFileSystemModel class provides a data model for the local filesystem.
80
81 \ingroup model-view
82 \inmodule QtWidgets
83
84 This class provides access to the local filesystem, providing functions
85 for renaming and removing files and directories, and for creating new
86 directories. In the simplest case, it can be used with a suitable display
87 widget as part of a browser or filter.
88
89 QFileSystemModel can be accessed using the standard interface provided by
90 QAbstractItemModel, but it also provides some convenience functions that are
91 specific to a directory model.
92 The fileInfo(), isDir(), fileName() and filePath() functions provide information
93 about the underlying files and directories related to items in the model.
94 Directories can be created and removed using mkdir(), rmdir().
95
96 \note QFileSystemModel requires an instance of \l QApplication.
97
98 \section1 Example Usage
99
100 A directory model that displays the contents of a default directory
101 is usually constructed with a parent object:
102
103 \snippet shareddirmodel/main.cpp 2
104
105 A tree view can be used to display the contents of the model
106
107 \snippet shareddirmodel/main.cpp 4
108
109 and the contents of a particular directory can be displayed by
110 setting the tree view's root index:
111
112 \snippet shareddirmodel/main.cpp 7
113
114 The view's root index can be used to control how much of a
115 hierarchical model is displayed. QFileSystemModel provides a convenience
116 function that returns a suitable model index for a path to a
117 directory within the model.
118
119 \section1 Caching and Performance
120
121 QFileSystemModel will not fetch any files or directories until setRootPath()
122 is called. This will prevent any unnecessary querying on the file system
123 until that point such as listing the drives on Windows.
124
125 Unlike QDirModel, QFileSystemModel uses a separate thread to populate
126 itself so it will not cause the main thread to hang as the file system
127 is being queried. Calls to rowCount() will return 0 until the model
128 populates a directory.
129
130 QFileSystemModel keeps a cache with file information. The cache is
131 automatically kept up to date using the QFileSystemWatcher.
132
133 \sa {Model Classes}
134*/
135
136/*!
137 \fn bool QFileSystemModel::rmdir(const QModelIndex &index)
138
139 Removes the directory corresponding to the model item \a index in the
140 file system model and \b{deletes the corresponding directory from the
141 file system}, returning true if successful. If the directory cannot be
142 removed, false is returned.
143
144 \warning This function deletes directories from the file system; it does
145 \b{not} move them to a location where they can be recovered.
146
147 \sa remove()
148*/
149
150/*!
151 \fn QIcon QFileSystemModel::fileName(const QModelIndex &index) const
152
153 Returns the file name for the item stored in the model under the given
154 \a index.
155*/
156
157/*!
158 \fn QIcon QFileSystemModel::fileIcon(const QModelIndex &index) const
159
160 Returns the icon for the item stored in the model under the given
161 \a index.
162*/
163
164/*!
165 \fn QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const
166
167 Returns the QFileInfo for the item stored in the model under the given
168 \a index.
169*/
170QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const
171{
172 Q_D(const QFileSystemModel);
173 return d->node(index)->fileInfo();
174}
175
176/*!
177 \fn void QFileSystemModel::rootPathChanged(const QString &newPath);
178
179 This signal is emitted whenever the root path has been changed to a \a newPath.
180*/
181
182/*!
183 \fn void QFileSystemModel::fileRenamed(const QString &path, const QString &oldName, const QString &newName)
184
185 This signal is emitted whenever a file with the \a oldName is successfully
186 renamed to \a newName. The file is located in in the directory \a path.
187*/
188
189/*!
190 \since 4.7
191 \fn void QFileSystemModel::directoryLoaded(const QString &path)
192
193 This signal is emitted when the gatherer thread has finished to load the \a path.
194
195*/
196
197/*!
198 \fn bool QFileSystemModel::remove(const QModelIndex &index)
199
200 Removes the model item \a index from the file system model and \b{deletes the
201 corresponding file from the file system}, returning true if successful. If the
202 item cannot be removed, false is returned.
203
204 \warning This function deletes files from the file system; it does \b{not}
205 move them to a location where they can be recovered.
206
207 \sa rmdir()
208*/
209
210bool QFileSystemModel::remove(const QModelIndex &aindex)
211{
212 Q_D(QFileSystemModel);
213
214 const QString path = d->filePath(index: aindex);
215 const QFileInfo fileInfo(path);
216#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
217 // QTBUG-65683: Remove file system watchers prior to deletion to prevent
218 // failure due to locked files on Windows.
219 const QStringList watchedPaths = d->unwatchPathsAt(aindex);
220#endif // filesystemwatcher && Q_OS_WIN
221 const bool success = (fileInfo.isFile() || fileInfo.isSymLink())
222 ? QFile::remove(fileName: path) : QDir(path).removeRecursively();
223#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
224 if (!success)
225 d->watchPaths(watchedPaths);
226#endif // filesystemwatcher && Q_OS_WIN
227 return success;
228}
229
230/*!
231 Constructs a file system model with the given \a parent.
232*/
233QFileSystemModel::QFileSystemModel(QObject *parent) :
234 QFileSystemModel(*new QFileSystemModelPrivate, parent)
235{
236}
237
238/*!
239 \internal
240*/
241QFileSystemModel::QFileSystemModel(QFileSystemModelPrivate &dd, QObject *parent)
242 : QAbstractItemModel(dd, parent)
243{
244 Q_D(QFileSystemModel);
245 d->init();
246}
247
248/*!
249 Destroys this file system model.
250*/
251QFileSystemModel::~QFileSystemModel() = default;
252
253/*!
254 \reimp
255*/
256QModelIndex QFileSystemModel::index(int row, int column, const QModelIndex &parent) const
257{
258 Q_D(const QFileSystemModel);
259 if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
260 return QModelIndex();
261
262 // get the parent node
263 QFileSystemModelPrivate::QFileSystemNode *parentNode = (d->indexValid(index: parent) ? d->node(index: parent) :
264 const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&d->root));
265 Q_ASSERT(parentNode);
266
267 // now get the internal pointer for the index
268 const int i = d->translateVisibleLocation(parent: parentNode, row);
269 if (i >= parentNode->visibleChildren.size())
270 return QModelIndex();
271 const QString &childName = parentNode->visibleChildren.at(i);
272 const QFileSystemModelPrivate::QFileSystemNode *indexNode = parentNode->children.value(key: childName);
273 Q_ASSERT(indexNode);
274
275 return createIndex(arow: row, acolumn: column, adata: const_cast<QFileSystemModelPrivate::QFileSystemNode*>(indexNode));
276}
277
278/*!
279 \reimp
280*/
281QModelIndex QFileSystemModel::sibling(int row, int column, const QModelIndex &idx) const
282{
283 if (row == idx.row() && column < QFileSystemModelPrivate::NumColumns) {
284 // cheap sibling operation: just adjust the column:
285 return createIndex(arow: row, acolumn: column, adata: idx.internalPointer());
286 } else {
287 // for anything else: call the default implementation
288 // (this could probably be optimized, too):
289 return QAbstractItemModel::sibling(row, column, idx);
290 }
291}
292
293/*!
294 \overload
295
296 Returns the model item index for the given \a path and \a column.
297*/
298QModelIndex QFileSystemModel::index(const QString &path, int column) const
299{
300 Q_D(const QFileSystemModel);
301 QFileSystemModelPrivate::QFileSystemNode *node = d->node(path, fetch: false);
302 return d->index(node, column);
303}
304
305/*!
306 \internal
307
308 Return the QFileSystemNode that goes to index.
309 */
310QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QModelIndex &index) const
311{
312 if (!index.isValid())
313 return const_cast<QFileSystemNode*>(&root);
314 QFileSystemModelPrivate::QFileSystemNode *indexNode = static_cast<QFileSystemModelPrivate::QFileSystemNode*>(index.internalPointer());
315 Q_ASSERT(indexNode);
316 return indexNode;
317}
318
319#ifdef Q_OS_WIN32
320static QString qt_GetLongPathName(const QString &strShortPath)
321{
322 if (strShortPath.isEmpty()
323 || strShortPath == QLatin1String(".") || strShortPath == QLatin1String(".."))
324 return strShortPath;
325 if (strShortPath.length() == 2 && strShortPath.endsWith(QLatin1Char(':')))
326 return strShortPath.toUpper();
327 const QString absPath = QDir(strShortPath).absolutePath();
328 if (absPath.startsWith(QLatin1String("//"))
329 || absPath.startsWith(QLatin1String("\\\\"))) // unc
330 return QDir::fromNativeSeparators(absPath);
331 if (absPath.startsWith(QLatin1Char('/')))
332 return QString();
333 const QString inputString = QLatin1String("\\\\?\\") + QDir::toNativeSeparators(absPath);
334 QVarLengthArray<TCHAR, MAX_PATH> buffer(MAX_PATH);
335 DWORD result = ::GetLongPathName((wchar_t*)inputString.utf16(),
336 buffer.data(),
337 buffer.size());
338 if (result > DWORD(buffer.size())) {
339 buffer.resize(result);
340 result = ::GetLongPathName((wchar_t*)inputString.utf16(),
341 buffer.data(),
342 buffer.size());
343 }
344 if (result > 4) {
345 QString longPath = QString::fromWCharArray(buffer.data() + 4); // ignoring prefix
346 longPath[0] = longPath.at(0).toUpper(); // capital drive letters
347 return QDir::fromNativeSeparators(longPath);
348 } else {
349 return QDir::fromNativeSeparators(strShortPath);
350 }
351}
352#endif
353
354/*!
355 \internal
356
357 Given a path return the matching QFileSystemNode or &root if invalid
358*/
359QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QString &path, bool fetch) const
360{
361 Q_Q(const QFileSystemModel);
362 Q_UNUSED(q);
363 if (path.isEmpty() || path == myComputer() || path.startsWith(c: QLatin1Char(':')))
364 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
365
366 // Construct the nodes up to the new root path if they need to be built
367 QString absolutePath;
368#ifdef Q_OS_WIN32
369 QString longPath = qt_GetLongPathName(path);
370#else
371 QString longPath = path;
372#endif
373 if (longPath == rootDir.path())
374 absolutePath = rootDir.absolutePath();
375 else
376 absolutePath = QDir(longPath).absolutePath();
377
378 // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const?
379 QStringList pathElements = absolutePath.split(sep: QLatin1Char('/'), behavior: Qt::SkipEmptyParts);
380 if ((pathElements.isEmpty())
381#if !defined(Q_OS_WIN)
382 && QDir::fromNativeSeparators(pathName: longPath) != QLatin1String("/")
383#endif
384 )
385 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
386 QModelIndex index = QModelIndex(); // start with "My Computer"
387 QString elementPath;
388 QChar separator = QLatin1Char('/');
389 QString trailingSeparator;
390#if defined(Q_OS_WIN)
391 if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path
392 QString host = QLatin1String("\\\\") + pathElements.constFirst();
393 if (absolutePath == QDir::fromNativeSeparators(host))
394 absolutePath.append(QLatin1Char('/'));
395 if (longPath.endsWith(QLatin1Char('/')) && !absolutePath.endsWith(QLatin1Char('/')))
396 absolutePath.append(QLatin1Char('/'));
397 if (absolutePath.endsWith(QLatin1Char('/')))
398 trailingSeparator = QLatin1String("\\");
399 int r = 0;
400 auto rootNode = const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
401 auto it = root.children.constFind(host);
402 if (it != root.children.cend()) {
403 host = it.key(); // Normalize case for lookup in visibleLocation()
404 } else {
405 if (pathElements.count() == 1 && !absolutePath.endsWith(QLatin1Char('/')))
406 return rootNode;
407 QFileInfo info(host);
408 if (!info.exists())
409 return rootNode;
410 QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
411 p->addNode(rootNode, host,info);
412 p->addVisibleFiles(rootNode, QStringList(host));
413 }
414 r = rootNode->visibleLocation(host);
415 r = translateVisibleLocation(rootNode, r);
416 index = q->index(r, 0, QModelIndex());
417 pathElements.pop_front();
418 separator = QLatin1Char('\\');
419 elementPath = host;
420 elementPath.append(separator);
421 } else {
422 if (!pathElements.at(0).contains(QLatin1Char(':'))) {
423 QString rootPath = QDir(longPath).rootPath();
424 pathElements.prepend(rootPath);
425 }
426 if (pathElements.at(0).endsWith(QLatin1Char('/')))
427 pathElements[0].chop(1);
428 }
429#else
430 // add the "/" item, since it is a valid path element on Unix
431 if (absolutePath[0] == QLatin1Char('/'))
432 pathElements.prepend(t: QLatin1String("/"));
433#endif
434
435 QFileSystemModelPrivate::QFileSystemNode *parent = node(index);
436
437 for (int i = 0; i < pathElements.count(); ++i) {
438 QString element = pathElements.at(i);
439 if (i != 0)
440 elementPath.append(c: separator);
441 elementPath.append(s: element);
442 if (i == pathElements.count() - 1)
443 elementPath.append(s: trailingSeparator);
444#ifdef Q_OS_WIN
445 // On Windows, "filename " and "filename" are equivalent and
446 // "filename . " and "filename" are equivalent
447 // "filename......." and "filename" are equivalent Task #133928
448 // whereas "filename .txt" is still "filename .txt"
449 // If after stripping the characters there is nothing left then we
450 // just return the parent directory as it is assumed that the path
451 // is referring to the parent
452 while (element.endsWith(QLatin1Char('.')) || element.endsWith(QLatin1Char(' ')))
453 element.chop(1);
454 // Only filenames that can't possibly exist will be end up being empty
455 if (element.isEmpty())
456 return parent;
457#endif
458 bool alreadyExisted = parent->children.contains(key: element);
459
460 // we couldn't find the path element, we create a new node since we
461 // _know_ that the path is valid
462 if (alreadyExisted) {
463 if ((parent->children.count() == 0)
464 || (parent->caseSensitive()
465 && parent->children.value(key: element)->fileName != element)
466 || (!parent->caseSensitive()
467 && parent->children.value(key: element)->fileName.toLower() != element.toLower()))
468 alreadyExisted = false;
469 }
470
471 QFileSystemModelPrivate::QFileSystemNode *node;
472 if (!alreadyExisted) {
473 // Someone might call ::index("file://cookie/monster/doesn't/like/veggies"),
474 // a path that doesn't exists, I.E. don't blindly create directories.
475 QFileInfo info(elementPath);
476 if (!info.exists())
477 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
478 QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
479 node = p->addNode(parentNode: parent, fileName: element,info);
480#if QT_CONFIG(filesystemwatcher)
481 node->populate(fileInfo: fileInfoGatherer.getInfo(info));
482#endif
483 } else {
484 node = parent->children.value(key: element);
485 }
486
487 Q_ASSERT(node);
488 if (!node->isVisible) {
489 // It has been filtered out
490 if (alreadyExisted && node->hasInformation() && !fetch)
491 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
492
493 QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
494 p->addVisibleFiles(parentNode: parent, newFiles: QStringList(element));
495 if (!p->bypassFilters.contains(key: node))
496 p->bypassFilters[node] = 1;
497 QString dir = q->filePath(index: this->index(node: parent));
498 if (!node->hasInformation() && fetch) {
499 Fetching f = { .dir: std::move(dir), .file: std::move(element), .node: node };
500 p->toFetch.append(t: std::move(f));
501 p->fetchingTimer.start(msec: 0, obj: const_cast<QFileSystemModel*>(q));
502 }
503 }
504 parent = node;
505 }
506
507 return parent;
508}
509
510/*!
511 \reimp
512*/
513void QFileSystemModel::timerEvent(QTimerEvent *event)
514{
515 Q_D(QFileSystemModel);
516 if (event->timerId() == d->fetchingTimer.timerId()) {
517 d->fetchingTimer.stop();
518#if QT_CONFIG(filesystemwatcher)
519 for (int i = 0; i < d->toFetch.count(); ++i) {
520 const QFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node;
521 if (!node->hasInformation()) {
522 d->fileInfoGatherer.fetchExtendedInformation(path: d->toFetch.at(i).dir,
523 files: QStringList(d->toFetch.at(i).file));
524 } else {
525 // qDebug("yah!, you saved a little gerbil soul");
526 }
527 }
528#endif
529 d->toFetch.clear();
530 }
531}
532
533/*!
534 Returns \c true if the model item \a index represents a directory;
535 otherwise returns \c false.
536*/
537bool QFileSystemModel::isDir(const QModelIndex &index) const
538{
539 // This function is for public usage only because it could create a file info
540 Q_D(const QFileSystemModel);
541 if (!index.isValid())
542 return true;
543 QFileSystemModelPrivate::QFileSystemNode *n = d->node(index);
544 if (n->hasInformation())
545 return n->isDir();
546 return fileInfo(index).isDir();
547}
548
549/*!
550 Returns the size in bytes of \a index. If the file does not exist, 0 is returned.
551 */
552qint64 QFileSystemModel::size(const QModelIndex &index) const
553{
554 Q_D(const QFileSystemModel);
555 if (!index.isValid())
556 return 0;
557 return d->node(index)->size();
558}
559
560/*!
561 Returns the type of file \a index such as "Directory" or "JPEG file".
562 */
563QString QFileSystemModel::type(const QModelIndex &index) const
564{
565 Q_D(const QFileSystemModel);
566 if (!index.isValid())
567 return QString();
568 return d->node(index)->type();
569}
570
571/*!
572 Returns the date and time when \a index was last modified.
573 */
574QDateTime QFileSystemModel::lastModified(const QModelIndex &index) const
575{
576 Q_D(const QFileSystemModel);
577 if (!index.isValid())
578 return QDateTime();
579 return d->node(index)->lastModified();
580}
581
582/*!
583 \reimp
584*/
585QModelIndex QFileSystemModel::parent(const QModelIndex &index) const
586{
587 Q_D(const QFileSystemModel);
588 if (!d->indexValid(index))
589 return QModelIndex();
590
591 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index);
592 Q_ASSERT(indexNode != nullptr);
593 QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
594 if (parentNode == nullptr || parentNode == &d->root)
595 return QModelIndex();
596
597 // get the parent's row
598 QFileSystemModelPrivate::QFileSystemNode *grandParentNode = parentNode->parent;
599 Q_ASSERT(grandParentNode->children.contains(parentNode->fileName));
600 int visualRow = d->translateVisibleLocation(parent: grandParentNode, row: grandParentNode->visibleLocation(childName: grandParentNode->children.value(key: parentNode->fileName)->fileName));
601 if (visualRow == -1)
602 return QModelIndex();
603 return createIndex(arow: visualRow, acolumn: 0, adata: parentNode);
604}
605
606/*
607 \internal
608
609 return the index for node
610*/
611QModelIndex QFileSystemModelPrivate::index(const QFileSystemModelPrivate::QFileSystemNode *node, int column) const
612{
613 Q_Q(const QFileSystemModel);
614 QFileSystemModelPrivate::QFileSystemNode *parentNode = (node ? node->parent : nullptr);
615 if (node == &root || !parentNode)
616 return QModelIndex();
617
618 // get the parent's row
619 Q_ASSERT(node);
620 if (!node->isVisible)
621 return QModelIndex();
622
623 int visualRow = translateVisibleLocation(parent: parentNode, row: parentNode->visibleLocation(childName: node->fileName));
624 return q->createIndex(arow: visualRow, acolumn: column, adata: const_cast<QFileSystemNode*>(node));
625}
626
627/*!
628 \reimp
629*/
630bool QFileSystemModel::hasChildren(const QModelIndex &parent) const
631{
632 Q_D(const QFileSystemModel);
633 if (parent.column() > 0)
634 return false;
635
636 if (!parent.isValid()) // drives
637 return true;
638
639 const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index: parent);
640 Q_ASSERT(indexNode);
641 return (indexNode->isDir());
642}
643
644/*!
645 \reimp
646 */
647bool QFileSystemModel::canFetchMore(const QModelIndex &parent) const
648{
649 Q_D(const QFileSystemModel);
650 if (!d->setRootPath)
651 return false;
652 const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index: parent);
653 return (!indexNode->populatedChildren);
654}
655
656/*!
657 \reimp
658 */
659void QFileSystemModel::fetchMore(const QModelIndex &parent)
660{
661 Q_D(QFileSystemModel);
662 if (!d->setRootPath)
663 return;
664 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index: parent);
665 if (indexNode->populatedChildren)
666 return;
667 indexNode->populatedChildren = true;
668#if QT_CONFIG(filesystemwatcher)
669 d->fileInfoGatherer.list(directoryPath: filePath(index: parent));
670#endif
671}
672
673/*!
674 \reimp
675*/
676int QFileSystemModel::rowCount(const QModelIndex &parent) const
677{
678 Q_D(const QFileSystemModel);
679 if (parent.column() > 0)
680 return 0;
681
682 if (!parent.isValid())
683 return d->root.visibleChildren.count();
684
685 const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(index: parent);
686 return parentNode->visibleChildren.count();
687}
688
689/*!
690 \reimp
691*/
692int QFileSystemModel::columnCount(const QModelIndex &parent) const
693{
694 return (parent.column() > 0) ? 0 : QFileSystemModelPrivate::NumColumns;
695}
696
697/*!
698 Returns the data stored under the given \a role for the item "My Computer".
699
700 \sa Qt::ItemDataRole
701 */
702QVariant QFileSystemModel::myComputer(int role) const
703{
704#if QT_CONFIG(filesystemwatcher)
705 Q_D(const QFileSystemModel);
706#endif
707 switch (role) {
708 case Qt::DisplayRole:
709 return QFileSystemModelPrivate::myComputer();
710#if QT_CONFIG(filesystemwatcher)
711 case Qt::DecorationRole:
712 return d->fileInfoGatherer.iconProvider()->icon(type: QFileIconProvider::Computer);
713#endif
714 }
715 return QVariant();
716}
717
718/*!
719 \reimp
720*/
721QVariant QFileSystemModel::data(const QModelIndex &index, int role) const
722{
723 Q_D(const QFileSystemModel);
724 if (!index.isValid() || index.model() != this)
725 return QVariant();
726
727 switch (role) {
728 case Qt::EditRole:
729 if (index.column() == 0)
730 return d->name(index);
731 Q_FALLTHROUGH();
732 case Qt::DisplayRole:
733 switch (index.column()) {
734 case 0: return d->displayName(index);
735 case 1: return d->size(index);
736 case 2: return d->type(index);
737 case 3: return d->time(index);
738 default:
739 qWarning(msg: "data: invalid display value column %d", index.column());
740 break;
741 }
742 break;
743 case FilePathRole:
744 return filePath(index);
745 case FileNameRole:
746 return d->name(index);
747 case Qt::DecorationRole:
748 if (index.column() == 0) {
749 QIcon icon = d->icon(index);
750#if QT_CONFIG(filesystemwatcher)
751 if (icon.isNull()) {
752 if (d->node(index)->isDir())
753 icon = d->fileInfoGatherer.iconProvider()->icon(type: QFileIconProvider::Folder);
754 else
755 icon = d->fileInfoGatherer.iconProvider()->icon(type: QFileIconProvider::File);
756 }
757#endif // filesystemwatcher
758 return icon;
759 }
760 break;
761 case Qt::TextAlignmentRole:
762 if (index.column() == 1)
763 return QVariant(Qt::AlignTrailing | Qt::AlignVCenter);
764 break;
765 case FilePermissions:
766 int p = permissions(index);
767 return p;
768 }
769
770 return QVariant();
771}
772
773/*!
774 \internal
775*/
776QString QFileSystemModelPrivate::size(const QModelIndex &index) const
777{
778 if (!index.isValid())
779 return QString();
780 const QFileSystemNode *n = node(index);
781 if (n->isDir()) {
782#ifdef Q_OS_MAC
783 return QLatin1String("--");
784#else
785 return QLatin1String("");
786#endif
787 // Windows - ""
788 // OS X - "--"
789 // Konqueror - "4 KB"
790 // Nautilus - "9 items" (the number of children)
791 }
792 return size(bytes: n->size());
793}
794
795QString QFileSystemModelPrivate::size(qint64 bytes)
796{
797 return QLocale::system().formattedDataSize(bytes);
798}
799
800/*!
801 \internal
802*/
803QString QFileSystemModelPrivate::time(const QModelIndex &index) const
804{
805 if (!index.isValid())
806 return QString();
807#if QT_CONFIG(datestring)
808 return QLocale::system().toString(dateTime: node(index)->lastModified(), format: QLocale::ShortFormat);
809#else
810 Q_UNUSED(index);
811 return QString();
812#endif
813}
814
815/*
816 \internal
817*/
818QString QFileSystemModelPrivate::type(const QModelIndex &index) const
819{
820 if (!index.isValid())
821 return QString();
822 return node(index)->type();
823}
824
825/*!
826 \internal
827*/
828QString QFileSystemModelPrivate::name(const QModelIndex &index) const
829{
830 if (!index.isValid())
831 return QString();
832 QFileSystemNode *dirNode = node(index);
833 if (
834#if QT_CONFIG(filesystemwatcher)
835 fileInfoGatherer.resolveSymlinks() &&
836#endif
837 !resolvedSymLinks.isEmpty() && dirNode->isSymLink(/* ignoreNtfsSymLinks = */ true)) {
838 QString fullPath = QDir::fromNativeSeparators(pathName: filePath(index));
839 return resolvedSymLinks.value(key: fullPath, defaultValue: dirNode->fileName);
840 }
841 return dirNode->fileName;
842}
843
844/*!
845 \internal
846*/
847QString QFileSystemModelPrivate::displayName(const QModelIndex &index) const
848{
849#if defined(Q_OS_WIN)
850 QFileSystemNode *dirNode = node(index);
851 if (!dirNode->volumeName.isEmpty())
852 return dirNode->volumeName;
853#endif
854 return name(index);
855}
856
857/*!
858 \internal
859*/
860QIcon QFileSystemModelPrivate::icon(const QModelIndex &index) const
861{
862 if (!index.isValid())
863 return QIcon();
864 return node(index)->icon();
865}
866
867static void displayRenameFailedMessage(const QString &newName)
868{
869#if QT_CONFIG(messagebox)
870 const QString message =
871 QFileSystemModel::tr(s: "<b>The name \"%1\" cannot be used.</b>"
872 "<p>Try using another name, with fewer characters or no punctuation marks.")
873 .arg(a: newName);
874 QMessageBox::information(parent: nullptr, title: QFileSystemModel::tr(s: "Invalid filename"),
875 text: message, button0: QMessageBox::Ok);
876#else
877 Q_UNUSED(newName)
878#endif // QT_CONFIG(messagebox)
879}
880
881/*!
882 \reimp
883*/
884bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role)
885{
886 Q_D(QFileSystemModel);
887 if (!idx.isValid()
888 || idx.column() != 0
889 || role != Qt::EditRole
890 || (flags(index: idx) & Qt::ItemIsEditable) == 0) {
891 return false;
892 }
893
894 QString newName = value.toString();
895 QString oldName = idx.data().toString();
896 if (newName == oldName)
897 return true;
898
899 const QString parentPath = filePath(index: parent(index: idx));
900
901 if (newName.isEmpty() || QDir::toNativeSeparators(pathName: newName).contains(c: QDir::separator())) {
902 displayRenameFailedMessage(newName);
903 return false;
904 }
905
906#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
907 // QTBUG-65683: Remove file system watchers prior to renaming to prevent
908 // failure due to locked files on Windows.
909 const QStringList watchedPaths = d->unwatchPathsAt(idx);
910#endif // filesystemwatcher && Q_OS_WIN
911 if (!QDir(parentPath).rename(oldName, newName)) {
912#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
913 d->watchPaths(watchedPaths);
914#endif
915 displayRenameFailedMessage(newName);
916 return false;
917 } else {
918 /*
919 *After re-naming something we don't want the selection to change*
920 - can't remove rows and later insert
921 - can't quickly remove and insert
922 - index pointer can't change because treeview doesn't use persistant index's
923
924 - if this get any more complicated think of changing it to just
925 use layoutChanged
926 */
927
928 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index: idx);
929 QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
930 int visibleLocation = parentNode->visibleLocation(childName: parentNode->children.value(key: indexNode->fileName)->fileName);
931
932 parentNode->visibleChildren.removeAt(i: visibleLocation);
933 QScopedPointer<QFileSystemModelPrivate::QFileSystemNode> nodeToRename(parentNode->children.take(key: oldName));
934 nodeToRename->fileName = newName;
935 nodeToRename->parent = parentNode;
936#if QT_CONFIG(filesystemwatcher)
937 nodeToRename->populate(fileInfo: d->fileInfoGatherer.getInfo(info: QFileInfo(parentPath, newName)));
938#endif
939 nodeToRename->isVisible = true;
940 parentNode->children[newName] = nodeToRename.take();
941 parentNode->visibleChildren.insert(i: visibleLocation, t: newName);
942
943 d->delayedSort();
944 emit fileRenamed(path: parentPath, oldName, newName);
945 }
946 return true;
947}
948
949/*!
950 \reimp
951*/
952QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
953{
954 switch (role) {
955 case Qt::DecorationRole:
956 if (section == 0) {
957 // ### TODO oh man this is ugly and doesn't even work all the way!
958 // it is still 2 pixels off
959 QImage pixmap(16, 1, QImage::Format_ARGB32_Premultiplied);
960 pixmap.fill(color: Qt::transparent);
961 return pixmap;
962 }
963 break;
964 case Qt::TextAlignmentRole:
965 return Qt::AlignLeft;
966 }
967
968 if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
969 return QAbstractItemModel::headerData(section, orientation, role);
970
971 QString returnValue;
972 switch (section) {
973 case 0: returnValue = tr(s: "Name");
974 break;
975 case 1: returnValue = tr(s: "Size");
976 break;
977 case 2: returnValue =
978#ifdef Q_OS_MAC
979 tr("Kind", "Match OS X Finder");
980#else
981 tr(s: "Type", c: "All other platforms");
982#endif
983 break;
984 // Windows - Type
985 // OS X - Kind
986 // Konqueror - File Type
987 // Nautilus - Type
988 case 3: returnValue = tr(s: "Date Modified");
989 break;
990 default: return QVariant();
991 }
992 return returnValue;
993}
994
995/*!
996 \reimp
997*/
998Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const
999{
1000 Q_D(const QFileSystemModel);
1001 Qt::ItemFlags flags = QAbstractItemModel::flags(index);
1002 if (!index.isValid())
1003 return flags;
1004
1005 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index);
1006 if (d->nameFilterDisables && !d->passNameFilters(node: indexNode)) {
1007 flags &= ~Qt::ItemIsEnabled;
1008 // ### TODO you shouldn't be able to set this as the current item, task 119433
1009 return flags;
1010 }
1011
1012 flags |= Qt::ItemIsDragEnabled;
1013 if (d->readOnly)
1014 return flags;
1015 if ((index.column() == 0) && indexNode->permissions() & QFile::WriteUser) {
1016 flags |= Qt::ItemIsEditable;
1017 if (indexNode->isDir())
1018 flags |= Qt::ItemIsDropEnabled;
1019 else
1020 flags |= Qt::ItemNeverHasChildren;
1021 }
1022 return flags;
1023}
1024
1025/*!
1026 \internal
1027*/
1028void QFileSystemModelPrivate::_q_performDelayedSort()
1029{
1030 Q_Q(QFileSystemModel);
1031 q->sort(column: sortColumn, order: sortOrder);
1032}
1033
1034
1035/*
1036 \internal
1037 Helper functor used by sort()
1038*/
1039class QFileSystemModelSorter
1040{
1041public:
1042 inline QFileSystemModelSorter(int column) : sortColumn(column)
1043 {
1044 naturalCompare.setNumericMode(true);
1045 naturalCompare.setCaseSensitivity(Qt::CaseInsensitive);
1046 }
1047
1048 bool compareNodes(const QFileSystemModelPrivate::QFileSystemNode *l,
1049 const QFileSystemModelPrivate::QFileSystemNode *r) const
1050 {
1051 switch (sortColumn) {
1052 case 0: {
1053#ifndef Q_OS_MAC
1054 // place directories before files
1055 bool left = l->isDir();
1056 bool right = r->isDir();
1057 if (left ^ right)
1058 return left;
1059#endif
1060 return naturalCompare.compare(s1: l->fileName, s2: r->fileName) < 0;
1061 }
1062 case 1:
1063 {
1064 // Directories go first
1065 bool left = l->isDir();
1066 bool right = r->isDir();
1067 if (left ^ right)
1068 return left;
1069
1070 qint64 sizeDifference = l->size() - r->size();
1071 if (sizeDifference == 0)
1072 return naturalCompare.compare(s1: l->fileName, s2: r->fileName) < 0;
1073
1074 return sizeDifference < 0;
1075 }
1076 case 2:
1077 {
1078 int compare = naturalCompare.compare(s1: l->type(), s2: r->type());
1079 if (compare == 0)
1080 return naturalCompare.compare(s1: l->fileName, s2: r->fileName) < 0;
1081
1082 return compare < 0;
1083 }
1084 case 3:
1085 {
1086 if (l->lastModified() == r->lastModified())
1087 return naturalCompare.compare(s1: l->fileName, s2: r->fileName) < 0;
1088
1089 return l->lastModified() < r->lastModified();
1090 }
1091 }
1092 Q_ASSERT(false);
1093 return false;
1094 }
1095
1096 bool operator()(const QFileSystemModelPrivate::QFileSystemNode *l,
1097 const QFileSystemModelPrivate::QFileSystemNode *r) const
1098 {
1099 return compareNodes(l, r);
1100 }
1101
1102
1103private:
1104 QCollator naturalCompare;
1105 int sortColumn;
1106};
1107
1108/*
1109 \internal
1110
1111 Sort all of the children of parent
1112*/
1113void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent)
1114{
1115 Q_Q(QFileSystemModel);
1116 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(index: parent);
1117 if (indexNode->children.count() == 0)
1118 return;
1119
1120 QVector<QFileSystemModelPrivate::QFileSystemNode*> values;
1121
1122 for (auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) {
1123 if (filtersAcceptsNode(node: iterator.value())) {
1124 values.append(t: iterator.value());
1125 } else {
1126 iterator.value()->isVisible = false;
1127 }
1128 }
1129 QFileSystemModelSorter ms(column);
1130 std::sort(first: values.begin(), last: values.end(), comp: ms);
1131 // First update the new visible list
1132 indexNode->visibleChildren.clear();
1133 //No more dirty item we reset our internal dirty index
1134 indexNode->dirtyChildrenIndex = -1;
1135 const int numValues = values.count();
1136 indexNode->visibleChildren.reserve(size: numValues);
1137 for (int i = 0; i < numValues; ++i) {
1138 indexNode->visibleChildren.append(t: values.at(i)->fileName);
1139 values.at(i)->isVisible = true;
1140 }
1141
1142 if (!disableRecursiveSort) {
1143 for (int i = 0; i < q->rowCount(parent); ++i) {
1144 const QModelIndex childIndex = q->index(row: i, column: 0, parent);
1145 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(index: childIndex);
1146 //Only do a recursive sort on visible nodes
1147 if (indexNode->isVisible)
1148 sortChildren(column, parent: childIndex);
1149 }
1150 }
1151}
1152
1153/*!
1154 \reimp
1155*/
1156void QFileSystemModel::sort(int column, Qt::SortOrder order)
1157{
1158 Q_D(QFileSystemModel);
1159 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
1160 return;
1161
1162 emit layoutAboutToBeChanged();
1163 QModelIndexList oldList = persistentIndexList();
1164 QVector<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > oldNodes;
1165 const int nodeCount = oldList.count();
1166 oldNodes.reserve(size: nodeCount);
1167 for (int i = 0; i < nodeCount; ++i) {
1168 const QModelIndex &oldNode = oldList.at(i);
1169 QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(index: oldNode), oldNode.column());
1170 oldNodes.append(t: pair);
1171 }
1172
1173 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
1174 //we sort only from where we are, don't need to sort all the model
1175 d->sortChildren(column, parent: index(path: rootPath()));
1176 d->sortColumn = column;
1177 d->forceSort = false;
1178 }
1179 d->sortOrder = order;
1180
1181 QModelIndexList newList;
1182 const int numOldNodes = oldNodes.size();
1183 newList.reserve(size: numOldNodes);
1184 for (int i = 0; i < numOldNodes; ++i) {
1185 const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &oldNode = oldNodes.at(i);
1186 newList.append(t: d->index(node: oldNode.first, column: oldNode.second));
1187 }
1188 changePersistentIndexList(from: oldList, to: newList);
1189 emit layoutChanged();
1190}
1191
1192/*!
1193 Returns a list of MIME types that can be used to describe a list of items
1194 in the model.
1195*/
1196QStringList QFileSystemModel::mimeTypes() const
1197{
1198 return QStringList(QLatin1String("text/uri-list"));
1199}
1200
1201/*!
1202 Returns an object that contains a serialized description of the specified
1203 \a indexes. The format used to describe the items corresponding to the
1204 indexes is obtained from the mimeTypes() function.
1205
1206 If the list of indexes is empty, \nullptr is returned rather than a
1207 serialized empty list.
1208*/
1209QMimeData *QFileSystemModel::mimeData(const QModelIndexList &indexes) const
1210{
1211 QList<QUrl> urls;
1212 QList<QModelIndex>::const_iterator it = indexes.begin();
1213 for (; it != indexes.end(); ++it)
1214 if ((*it).column() == 0)
1215 urls << QUrl::fromLocalFile(localfile: filePath(index: *it));
1216 QMimeData *data = new QMimeData();
1217 data->setUrls(urls);
1218 return data;
1219}
1220
1221/*!
1222 Handles the \a data supplied by a drag and drop operation that ended with
1223 the given \a action over the row in the model specified by the \a row and
1224 \a column and by the \a parent index. Returns true if the operation was
1225 successful.
1226
1227 \sa supportedDropActions()
1228*/
1229bool QFileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
1230 int row, int column, const QModelIndex &parent)
1231{
1232 Q_UNUSED(row);
1233 Q_UNUSED(column);
1234 if (!parent.isValid() || isReadOnly())
1235 return false;
1236
1237 bool success = true;
1238 QString to = filePath(index: parent) + QDir::separator();
1239
1240 QList<QUrl> urls = data->urls();
1241 QList<QUrl>::const_iterator it = urls.constBegin();
1242
1243 switch (action) {
1244 case Qt::CopyAction:
1245 for (; it != urls.constEnd(); ++it) {
1246 QString path = (*it).toLocalFile();
1247 success = QFile::copy(fileName: path, newName: to + QFileInfo(path).fileName()) && success;
1248 }
1249 break;
1250 case Qt::LinkAction:
1251 for (; it != urls.constEnd(); ++it) {
1252 QString path = (*it).toLocalFile();
1253 success = QFile::link(oldname: path, newName: to + QFileInfo(path).fileName()) && success;
1254 }
1255 break;
1256 case Qt::MoveAction:
1257 for (; it != urls.constEnd(); ++it) {
1258 QString path = (*it).toLocalFile();
1259 success = QFile::rename(oldName: path, newName: to + QFileInfo(path).fileName()) && success;
1260 }
1261 break;
1262 default:
1263 return false;
1264 }
1265
1266 return success;
1267}
1268
1269/*!
1270 \reimp
1271*/
1272Qt::DropActions QFileSystemModel::supportedDropActions() const
1273{
1274 return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
1275}
1276
1277/*!
1278 \enum QFileSystemModel::Option
1279 \since 5.14
1280
1281 \value DontWatchForChanges Do not add file watchers to the paths.
1282 This reduces overhead when using the model for simple tasks
1283 like line edit completion.
1284
1285 \value DontResolveSymlinks Don't resolve symlinks in the file
1286 system model. By default, symlinks are resolved.
1287
1288 \value DontUseCustomDirectoryIcons Always use the default directory icon.
1289 Some platforms allow the user to set a different icon. Custom icon lookup
1290 causes a big performance impact over network or removable drives.
1291 This sets the QFileIconProvider::DontUseCustomDirectoryIcons
1292 option in the icon provider accordingly.
1293
1294 \sa resolveSymlinks
1295*/
1296
1297/*!
1298 \since 5.14
1299 Sets the given \a option to be enabled if \a on is true; otherwise,
1300 clears the given \a option.
1301
1302 Options should be set before changing properties.
1303
1304 \sa options, testOption()
1305*/
1306void QFileSystemModel::setOption(Option option, bool on)
1307{
1308 QFileSystemModel::Options previousOptions = options();
1309 setOptions(previousOptions.setFlag(flag: option, on));
1310}
1311
1312/*!
1313 \since 5.14
1314
1315 Returns \c true if the given \a option is enabled; otherwise, returns
1316 false.
1317
1318 \sa options, setOption()
1319*/
1320bool QFileSystemModel::testOption(Option option) const
1321{
1322 return options().testFlag(flag: option);
1323}
1324
1325/*!
1326 \property QFileSystemModel::options
1327 \brief the various options that affect the model
1328 \since 5.14
1329
1330 By default, all options are disabled.
1331
1332 Options should be set before changing properties.
1333
1334 \sa setOption(), testOption()
1335*/
1336void QFileSystemModel::setOptions(Options options)
1337{
1338 const Options changed = (options ^ QFileSystemModel::options());
1339
1340 if (changed.testFlag(flag: DontResolveSymlinks))
1341 setResolveSymlinks(!options.testFlag(flag: DontResolveSymlinks));
1342
1343#if QT_CONFIG(filesystemwatcher)
1344 Q_D(QFileSystemModel);
1345 if (changed.testFlag(flag: DontWatchForChanges))
1346 d->fileInfoGatherer.setWatching(!options.testFlag(flag: DontWatchForChanges));
1347#endif
1348
1349 if (changed.testFlag(flag: DontUseCustomDirectoryIcons)) {
1350 if (auto provider = iconProvider()) {
1351 QFileIconProvider::Options providerOptions = provider->options();
1352 providerOptions.setFlag(flag: QFileIconProvider::DontUseCustomDirectoryIcons,
1353 on: options.testFlag(flag: QFileSystemModel::DontUseCustomDirectoryIcons));
1354 provider->setOptions(providerOptions);
1355 } else {
1356 qWarning(msg: "Setting QFileSystemModel::DontUseCustomDirectoryIcons has no effect when no provider is used");
1357 }
1358 }
1359}
1360
1361QFileSystemModel::Options QFileSystemModel::options() const
1362{
1363 QFileSystemModel::Options result;
1364 result.setFlag(flag: DontResolveSymlinks, on: !resolveSymlinks());
1365#if QT_CONFIG(filesystemwatcher)
1366 Q_D(const QFileSystemModel);
1367 result.setFlag(flag: DontWatchForChanges, on: !d->fileInfoGatherer.isWatching());
1368#else
1369 result.setFlag(DontWatchForChanges);
1370#endif
1371 if (auto provider = iconProvider()) {
1372 result.setFlag(flag: DontUseCustomDirectoryIcons,
1373 on: provider->options().testFlag(flag: QFileIconProvider::DontUseCustomDirectoryIcons));
1374 }
1375 return result;
1376}
1377
1378/*!
1379 Returns the path of the item stored in the model under the
1380 \a index given.
1381*/
1382QString QFileSystemModel::filePath(const QModelIndex &index) const
1383{
1384 Q_D(const QFileSystemModel);
1385 QString fullPath = d->filePath(index);
1386 QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);
1387 if (dirNode->isSymLink()
1388#if QT_CONFIG(filesystemwatcher)
1389 && d->fileInfoGatherer.resolveSymlinks()
1390#endif
1391 && d->resolvedSymLinks.contains(key: fullPath)
1392 && dirNode->isDir()) {
1393 QFileInfo resolvedInfo(fullPath);
1394 resolvedInfo = resolvedInfo.canonicalFilePath();
1395 if (resolvedInfo.exists())
1396 return resolvedInfo.filePath();
1397 }
1398 return fullPath;
1399}
1400
1401QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const
1402{
1403 Q_Q(const QFileSystemModel);
1404 Q_UNUSED(q);
1405 if (!index.isValid())
1406 return QString();
1407 Q_ASSERT(index.model() == q);
1408
1409 QStringList path;
1410 QModelIndex idx = index;
1411 while (idx.isValid()) {
1412 QFileSystemModelPrivate::QFileSystemNode *dirNode = node(index: idx);
1413 if (dirNode)
1414 path.prepend(t: dirNode->fileName);
1415 idx = idx.parent();
1416 }
1417 QString fullPath = QDir::fromNativeSeparators(pathName: path.join(sep: QDir::separator()));
1418#if !defined(Q_OS_WIN)
1419 if ((fullPath.length() > 2) && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/'))
1420 fullPath = fullPath.mid(position: 1);
1421#else
1422 if (fullPath.length() == 2 && fullPath.endsWith(QLatin1Char(':')))
1423 fullPath.append(QLatin1Char('/'));
1424#endif
1425 return fullPath;
1426}
1427
1428/*!
1429 Create a directory with the \a name in the \a parent model index.
1430*/
1431QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent, const QString &name)
1432{
1433 Q_D(QFileSystemModel);
1434 if (!parent.isValid())
1435 return parent;
1436
1437 QDir dir(filePath(index: parent));
1438 if (!dir.mkdir(dirName: name))
1439 return QModelIndex();
1440 QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(index: parent);
1441 d->addNode(parentNode, fileName: name, info: QFileInfo());
1442 Q_ASSERT(parentNode->children.contains(name));
1443 QFileSystemModelPrivate::QFileSystemNode *node = parentNode->children[name];
1444#if QT_CONFIG(filesystemwatcher)
1445 node->populate(fileInfo: d->fileInfoGatherer.getInfo(info: QFileInfo(dir.absolutePath() + QDir::separator() + name)));
1446#endif
1447 d->addVisibleFiles(parentNode, newFiles: QStringList(name));
1448 return d->index(node);
1449}
1450
1451/*!
1452 Returns the complete OR-ed together combination of QFile::Permission for the \a index.
1453 */
1454QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index) const
1455{
1456 Q_D(const QFileSystemModel);
1457 return d->node(index)->permissions();
1458}
1459
1460/*!
1461 Sets the directory that is being watched by the model to \a newPath by
1462 installing a \l{QFileSystemWatcher}{file system watcher} on it. Any
1463 changes to files and directories within this directory will be
1464 reflected in the model.
1465
1466 If the path is changed, the rootPathChanged() signal will be emitted.
1467
1468 \note This function does not change the structure of the model or
1469 modify the data available to views. In other words, the "root" of
1470 the model is \e not changed to include only files and directories
1471 within the directory specified by \a newPath in the file system.
1472 */
1473QModelIndex QFileSystemModel::setRootPath(const QString &newPath)
1474{
1475 Q_D(QFileSystemModel);
1476#ifdef Q_OS_WIN
1477#ifdef Q_OS_WIN32
1478 QString longNewPath = qt_GetLongPathName(newPath);
1479#else
1480 QString longNewPath = QDir::fromNativeSeparators(newPath);
1481#endif
1482#else
1483 QString longNewPath = newPath;
1484#endif
1485 QDir newPathDir(longNewPath);
1486 //we remove .. and . from the given path if exist
1487 if (!newPath.isEmpty()) {
1488 longNewPath = QDir::cleanPath(path: longNewPath);
1489 newPathDir.setPath(longNewPath);
1490 }
1491
1492 d->setRootPath = true;
1493
1494 //user don't ask for the root path ("") but the conversion failed
1495 if (!newPath.isEmpty() && longNewPath.isEmpty())
1496 return d->index(path: rootPath());
1497
1498 if (d->rootDir.path() == longNewPath)
1499 return d->index(path: rootPath());
1500
1501 bool showDrives = (longNewPath.isEmpty() || longNewPath == QFileSystemModelPrivate::myComputer());
1502 if (!showDrives && !newPathDir.exists())
1503 return d->index(path: rootPath());
1504
1505 //We remove the watcher on the previous path
1506 if (!rootPath().isEmpty() && rootPath() != QLatin1String(".")) {
1507 //This remove the watcher for the old rootPath
1508#if QT_CONFIG(filesystemwatcher)
1509 d->fileInfoGatherer.removePath(path: rootPath());
1510#endif
1511 //This line "marks" the node as dirty, so the next fetchMore
1512 //call on the path will ask the gatherer to install a watcher again
1513 //But it doesn't re-fetch everything
1514 d->node(path: rootPath())->populatedChildren = false;
1515 }
1516
1517 // We have a new valid root path
1518 d->rootDir = newPathDir;
1519 QModelIndex newRootIndex;
1520 if (showDrives) {
1521 // otherwise dir will become '.'
1522 d->rootDir.setPath(QLatin1String(""));
1523 } else {
1524 newRootIndex = d->index(path: newPathDir.path());
1525 }
1526 fetchMore(parent: newRootIndex);
1527 emit rootPathChanged(newPath: longNewPath);
1528 d->forceSort = true;
1529 d->delayedSort();
1530 return newRootIndex;
1531}
1532
1533/*!
1534 The currently set root path
1535
1536 \sa rootDirectory()
1537*/
1538QString QFileSystemModel::rootPath() const
1539{
1540 Q_D(const QFileSystemModel);
1541 return d->rootDir.path();
1542}
1543
1544/*!
1545 The currently set directory
1546
1547 \sa rootPath()
1548*/
1549QDir QFileSystemModel::rootDirectory() const
1550{
1551 Q_D(const QFileSystemModel);
1552 QDir dir(d->rootDir);
1553 dir.setNameFilters(nameFilters());
1554 dir.setFilter(filter());
1555 return dir;
1556}
1557
1558/*!
1559 Sets the \a provider of file icons for the directory model.
1560*/
1561void QFileSystemModel::setIconProvider(QFileIconProvider *provider)
1562{
1563 Q_D(QFileSystemModel);
1564#if QT_CONFIG(filesystemwatcher)
1565 d->fileInfoGatherer.setIconProvider(provider);
1566#endif
1567 d->root.updateIcon(iconProvider: provider, path: QString());
1568}
1569
1570/*!
1571 Returns the file icon provider for this directory model.
1572*/
1573QFileIconProvider *QFileSystemModel::iconProvider() const
1574{
1575#if QT_CONFIG(filesystemwatcher)
1576 Q_D(const QFileSystemModel);
1577 return d->fileInfoGatherer.iconProvider();
1578#else
1579 return 0;
1580#endif
1581}
1582
1583/*!
1584 Sets the directory model's filter to that specified by \a filters.
1585
1586 Note that the filter you set should always include the QDir::AllDirs enum value,
1587 otherwise QFileSystemModel won't be able to read the directory structure.
1588
1589 \sa QDir::Filters
1590*/
1591void QFileSystemModel::setFilter(QDir::Filters filters)
1592{
1593 Q_D(QFileSystemModel);
1594 if (d->filters == filters)
1595 return;
1596 d->filters = filters;
1597 // CaseSensitivity might have changed
1598 setNameFilters(nameFilters());
1599 d->forceSort = true;
1600 d->delayedSort();
1601}
1602
1603/*!
1604 Returns the filter specified for the directory model.
1605
1606 If a filter has not been set, the default filter is QDir::AllEntries |
1607 QDir::NoDotAndDotDot | QDir::AllDirs.
1608
1609 \sa QDir::Filters
1610*/
1611QDir::Filters QFileSystemModel::filter() const
1612{
1613 Q_D(const QFileSystemModel);
1614 return d->filters;
1615}
1616
1617/*!
1618 \property QFileSystemModel::resolveSymlinks
1619 \brief Whether the directory model should resolve symbolic links
1620
1621 This is only relevant on Windows.
1622
1623 By default, this property is \c true.
1624
1625 \sa QFileSystemModel::Options
1626*/
1627void QFileSystemModel::setResolveSymlinks(bool enable)
1628{
1629#if QT_CONFIG(filesystemwatcher)
1630 Q_D(QFileSystemModel);
1631 d->fileInfoGatherer.setResolveSymlinks(enable);
1632#else
1633 Q_UNUSED(enable)
1634#endif
1635}
1636
1637bool QFileSystemModel::resolveSymlinks() const
1638{
1639#if QT_CONFIG(filesystemwatcher)
1640 Q_D(const QFileSystemModel);
1641 return d->fileInfoGatherer.resolveSymlinks();
1642#else
1643 return false;
1644#endif
1645}
1646
1647/*!
1648 \property QFileSystemModel::readOnly
1649 \brief Whether the directory model allows writing to the file system
1650
1651 If this property is set to false, the directory model will allow renaming, copying
1652 and deleting of files and directories.
1653
1654 This property is \c true by default
1655*/
1656void QFileSystemModel::setReadOnly(bool enable)
1657{
1658 Q_D(QFileSystemModel);
1659 d->readOnly = enable;
1660}
1661
1662bool QFileSystemModel::isReadOnly() const
1663{
1664 Q_D(const QFileSystemModel);
1665 return d->readOnly;
1666}
1667
1668/*!
1669 \property QFileSystemModel::nameFilterDisables
1670 \brief Whether files that don't pass the name filter are hidden or disabled
1671
1672 This property is \c true by default
1673*/
1674void QFileSystemModel::setNameFilterDisables(bool enable)
1675{
1676 Q_D(QFileSystemModel);
1677 if (d->nameFilterDisables == enable)
1678 return;
1679 d->nameFilterDisables = enable;
1680 d->forceSort = true;
1681 d->delayedSort();
1682}
1683
1684bool QFileSystemModel::nameFilterDisables() const
1685{
1686 Q_D(const QFileSystemModel);
1687 return d->nameFilterDisables;
1688}
1689
1690/*!
1691 Sets the name \a filters to apply against the existing files.
1692*/
1693void QFileSystemModel::setNameFilters(const QStringList &filters)
1694{
1695 // Prep the regexp's ahead of time
1696#if QT_CONFIG(regularexpression)
1697 Q_D(QFileSystemModel);
1698
1699 if (!d->bypassFilters.isEmpty()) {
1700 // update the bypass filter to only bypass the stuff that must be kept around
1701 d->bypassFilters.clear();
1702 // We guarantee that rootPath will stick around
1703 QPersistentModelIndex root(index(path: rootPath()));
1704 const QModelIndexList persistentList = persistentIndexList();
1705 for (const auto &persistentIndex : persistentList) {
1706 QFileSystemModelPrivate::QFileSystemNode *node = d->node(index: persistentIndex);
1707 while (node) {
1708 if (d->bypassFilters.contains(key: node))
1709 break;
1710 if (node->isDir())
1711 d->bypassFilters[node] = true;
1712 node = node->parent;
1713 }
1714 }
1715 }
1716
1717 d->nameFilters = filters;
1718 d->forceSort = true;
1719 d->delayedSort();
1720#endif
1721}
1722
1723/*!
1724 Returns a list of filters applied to the names in the model.
1725*/
1726QStringList QFileSystemModel::nameFilters() const
1727{
1728#if QT_CONFIG(regularexpression)
1729 Q_D(const QFileSystemModel);
1730 return d->nameFilters;
1731#else
1732 return QStringList();
1733#endif
1734}
1735
1736/*!
1737 \reimp
1738*/
1739bool QFileSystemModel::event(QEvent *event)
1740{
1741#if QT_CONFIG(filesystemwatcher)
1742 Q_D(QFileSystemModel);
1743 if (event->type() == QEvent::LanguageChange) {
1744 d->root.retranslateStrings(iconProvider: d->fileInfoGatherer.iconProvider(), path: QString());
1745 return true;
1746 }
1747#endif
1748 return QAbstractItemModel::event(event);
1749}
1750
1751bool QFileSystemModel::rmdir(const QModelIndex &aindex)
1752{
1753 QString path = filePath(index: aindex);
1754 const bool success = QDir().rmdir(dirName: path);
1755#if QT_CONFIG(filesystemwatcher)
1756 if (success) {
1757 QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func());
1758 d->fileInfoGatherer.removePath(path);
1759 }
1760#endif
1761 return success;
1762}
1763
1764/*!
1765 \internal
1766
1767 Performed quick listing and see if any files have been added or removed,
1768 then fetch more information on visible files.
1769 */
1770void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files)
1771{
1772 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path: directory, fetch: false);
1773 if (parentNode->children.count() == 0)
1774 return;
1775 QStringList toRemove;
1776 QStringList newFiles = files;
1777 std::sort(first: newFiles.begin(), last: newFiles.end());
1778 for (auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) {
1779 QStringList::iterator iterator = std::lower_bound(newFiles.begin(), newFiles.end(), i.value()->fileName);
1780 if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator))
1781 toRemove.append(t: i.value()->fileName);
1782 }
1783 for (int i = 0 ; i < toRemove.count() ; ++i )
1784 removeNode(parentNode, name: toRemove[i]);
1785}
1786
1787#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
1788static QString volumeName(const QString &path)
1789{
1790 IShellItem *item = nullptr;
1791 const QString native = QDir::toNativeSeparators(path);
1792 HRESULT hr = SHCreateItemFromParsingName(reinterpret_cast<const wchar_t *>(native.utf16()),
1793 nullptr, IID_IShellItem,
1794 reinterpret_cast<void **>(&item));
1795 if (FAILED(hr))
1796 return QString();
1797 LPWSTR name = nullptr;
1798 hr = item->GetDisplayName(SIGDN_NORMALDISPLAY, &name);
1799 if (FAILED(hr))
1800 return QString();
1801 QString result = QString::fromWCharArray(name);
1802 CoTaskMemFree(name);
1803 item->Release();
1804 return result;
1805}
1806#endif // Q_OS_WIN && !Q_OS_WINRT
1807
1808/*!
1809 \internal
1810
1811 Adds a new file to the children of parentNode
1812
1813 *WARNING* this will change the count of children
1814*/
1815QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo& info)
1816{
1817 // In the common case, itemLocation == count() so check there first
1818 QFileSystemModelPrivate::QFileSystemNode *node = new QFileSystemModelPrivate::QFileSystemNode(fileName, parentNode);
1819#if QT_CONFIG(filesystemwatcher)
1820 node->populate(fileInfo: info);
1821#else
1822 Q_UNUSED(info)
1823#endif
1824#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
1825 //The parentNode is "" so we are listing the drives
1826 if (parentNode->fileName.isEmpty())
1827 node->volumeName = volumeName(fileName);
1828#endif
1829 Q_ASSERT(!parentNode->children.contains(fileName));
1830 parentNode->children.insert(key: fileName, value: node);
1831 return node;
1832}
1833
1834/*!
1835 \internal
1836
1837 File at parentNode->children(itemLocation) has been removed, remove from the lists
1838 and emit signals if necessary
1839
1840 *WARNING* this will change the count of children and could change visibleChildren
1841 */
1842void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode, const QString& name)
1843{
1844 Q_Q(QFileSystemModel);
1845 QModelIndex parent = index(node: parentNode);
1846 bool indexHidden = isHiddenByFilter(indexNode: parentNode, index: parent);
1847
1848 int vLocation = parentNode->visibleLocation(childName: name);
1849 if (vLocation >= 0 && !indexHidden)
1850 q->beginRemoveRows(parent, first: translateVisibleLocation(parent: parentNode, row: vLocation),
1851 last: translateVisibleLocation(parent: parentNode, row: vLocation));
1852 QFileSystemNode * node = parentNode->children.take(key: name);
1853 delete node;
1854 // cleanup sort files after removing rather then re-sorting which is O(n)
1855 if (vLocation >= 0)
1856 parentNode->visibleChildren.removeAt(i: vLocation);
1857 if (vLocation >= 0 && !indexHidden)
1858 q->endRemoveRows();
1859}
1860
1861/*!
1862 \internal
1863
1864 File at parentNode->children(itemLocation) was not visible before, but now should be
1865 and emit signals if necessary.
1866
1867 *WARNING* this will change the visible count
1868 */
1869void QFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles)
1870{
1871 Q_Q(QFileSystemModel);
1872 QModelIndex parent = index(node: parentNode);
1873 bool indexHidden = isHiddenByFilter(indexNode: parentNode, index: parent);
1874 if (!indexHidden) {
1875 q->beginInsertRows(parent, first: parentNode->visibleChildren.count() , last: parentNode->visibleChildren.count() + newFiles.count() - 1);
1876 }
1877
1878 if (parentNode->dirtyChildrenIndex == -1)
1879 parentNode->dirtyChildrenIndex = parentNode->visibleChildren.count();
1880
1881 for (const auto &newFile : newFiles) {
1882 parentNode->visibleChildren.append(t: newFile);
1883 parentNode->children.value(key: newFile)->isVisible = true;
1884 }
1885 if (!indexHidden)
1886 q->endInsertRows();
1887}
1888
1889/*!
1890 \internal
1891
1892 File was visible before, but now should NOT be
1893
1894 *WARNING* this will change the visible count
1895 */
1896void QFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode, int vLocation)
1897{
1898 Q_Q(QFileSystemModel);
1899 if (vLocation == -1)
1900 return;
1901 QModelIndex parent = index(node: parentNode);
1902 bool indexHidden = isHiddenByFilter(indexNode: parentNode, index: parent);
1903 if (!indexHidden)
1904 q->beginRemoveRows(parent, first: translateVisibleLocation(parent: parentNode, row: vLocation),
1905 last: translateVisibleLocation(parent: parentNode, row: vLocation));
1906 parentNode->children.value(key: parentNode->visibleChildren.at(i: vLocation))->isVisible = false;
1907 parentNode->visibleChildren.removeAt(i: vLocation);
1908 if (!indexHidden)
1909 q->endRemoveRows();
1910}
1911
1912/*!
1913 \internal
1914
1915 The thread has received new information about files,
1916 update and emit dataChanged if it has actually changed.
1917 */
1918void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, const QVector<QPair<QString, QFileInfo> > &updates)
1919{
1920#if QT_CONFIG(filesystemwatcher)
1921 Q_Q(QFileSystemModel);
1922 QVector<QString> rowsToUpdate;
1923 QStringList newFiles;
1924 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path, fetch: false);
1925 QModelIndex parentIndex = index(node: parentNode);
1926 for (const auto &update : updates) {
1927 QString fileName = update.first;
1928 Q_ASSERT(!fileName.isEmpty());
1929 QExtendedInformation info = fileInfoGatherer.getInfo(info: update.second);
1930 bool previouslyHere = parentNode->children.contains(key: fileName);
1931 if (!previouslyHere) {
1932 addNode(parentNode, fileName, info: info.fileInfo());
1933 }
1934 QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(key: fileName);
1935 bool isCaseSensitive = parentNode->caseSensitive();
1936 if (isCaseSensitive) {
1937 if (node->fileName != fileName)
1938 continue;
1939 } else {
1940 if (QString::compare(s1: node->fileName,s2: fileName,cs: Qt::CaseInsensitive) != 0)
1941 continue;
1942 }
1943 if (isCaseSensitive) {
1944 Q_ASSERT(node->fileName == fileName);
1945 } else {
1946 node->fileName = fileName;
1947 }
1948
1949 if (*node != info ) {
1950 node->populate(fileInfo: info);
1951 bypassFilters.remove(key: node);
1952 // brand new information.
1953 if (filtersAcceptsNode(node)) {
1954 if (!node->isVisible) {
1955 newFiles.append(t: fileName);
1956 } else {
1957 rowsToUpdate.append(t: fileName);
1958 }
1959 } else {
1960 if (node->isVisible) {
1961 int visibleLocation = parentNode->visibleLocation(childName: fileName);
1962 removeVisibleFile(parentNode, vLocation: visibleLocation);
1963 } else {
1964 // The file is not visible, don't do anything
1965 }
1966 }
1967 }
1968 }
1969
1970 // bundle up all of the changed signals into as few as possible.
1971 std::sort(first: rowsToUpdate.begin(), last: rowsToUpdate.end());
1972 QString min;
1973 QString max;
1974 for (const QString &value : qAsConst(t&: rowsToUpdate)) {
1975 //##TODO is there a way to bundle signals with QString as the content of the list?
1976 /*if (min.isEmpty()) {
1977 min = value;
1978 if (i != rowsToUpdate.count() - 1)
1979 continue;
1980 }
1981 if (i != rowsToUpdate.count() - 1) {
1982 if ((value == min + 1 && max.isEmpty()) || value == max + 1) {
1983 max = value;
1984 continue;
1985 }
1986 }*/
1987 max = value;
1988 min = value;
1989 int visibleMin = parentNode->visibleLocation(childName: min);
1990 int visibleMax = parentNode->visibleLocation(childName: max);
1991 if (visibleMin >= 0
1992 && visibleMin < parentNode->visibleChildren.count()
1993 && parentNode->visibleChildren.at(i: visibleMin) == min
1994 && visibleMax >= 0) {
1995 QModelIndex bottom = q->index(row: translateVisibleLocation(parent: parentNode, row: visibleMin), column: 0, parent: parentIndex);
1996 QModelIndex top = q->index(row: translateVisibleLocation(parent: parentNode, row: visibleMax), column: 3, parent: parentIndex);
1997 emit q->dataChanged(topLeft: bottom, bottomRight: top);
1998 }
1999
2000 /*min = QString();
2001 max = QString();*/
2002 }
2003
2004 if (newFiles.count() > 0) {
2005 addVisibleFiles(parentNode, newFiles);
2006 }
2007
2008 if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) {
2009 forceSort = true;
2010 delayedSort();
2011 }
2012#else
2013 Q_UNUSED(path)
2014 Q_UNUSED(updates)
2015#endif // filesystemwatcher
2016}
2017
2018/*!
2019 \internal
2020*/
2021void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName)
2022{
2023 resolvedSymLinks[fileName] = resolvedName;
2024}
2025
2026#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
2027// Remove file system watchers at/below the index and return a list of previously
2028// watched files. This should be called prior to operations like rename/remove
2029// which might fail due to watchers on platforms like Windows. The watchers
2030// should be restored on failure.
2031QStringList QFileSystemModelPrivate::unwatchPathsAt(const QModelIndex &index)
2032{
2033 const QFileSystemModelPrivate::QFileSystemNode *indexNode = node(index);
2034 if (indexNode == nullptr)
2035 return QStringList();
2036 const Qt::CaseSensitivity caseSensitivity = indexNode->caseSensitive()
2037 ? Qt::CaseSensitive : Qt::CaseInsensitive;
2038 const QString path = indexNode->fileInfo().absoluteFilePath();
2039
2040 QStringList result;
2041 const auto filter = [path, caseSensitivity] (const QString &watchedPath)
2042 {
2043 const int pathSize = path.size();
2044 if (pathSize == watchedPath.size()) {
2045 return path.compare(watchedPath, caseSensitivity) == 0;
2046 } else if (watchedPath.size() > pathSize) {
2047 return watchedPath.at(pathSize) == QLatin1Char('/')
2048 && watchedPath.startsWith(path, caseSensitivity);
2049 }
2050 return false;
2051 };
2052
2053 const QStringList &watchedFiles = fileInfoGatherer.watchedFiles();
2054 std::copy_if(watchedFiles.cbegin(), watchedFiles.cend(),
2055 std::back_inserter(result), filter);
2056
2057 const QStringList &watchedDirectories = fileInfoGatherer.watchedDirectories();
2058 std::copy_if(watchedDirectories.cbegin(), watchedDirectories.cend(),
2059 std::back_inserter(result), filter);
2060
2061 fileInfoGatherer.unwatchPaths(result);
2062 return result;
2063}
2064#endif // filesystemwatcher && Q_OS_WIN
2065
2066/*!
2067 \internal
2068*/
2069void QFileSystemModelPrivate::init()
2070{
2071 Q_Q(QFileSystemModel);
2072
2073 delayedSortTimer.setSingleShot(true);
2074
2075 qRegisterMetaType<QVector<QPair<QString,QFileInfo> > >();
2076#if QT_CONFIG(filesystemwatcher)
2077 q->connect(sender: &fileInfoGatherer, SIGNAL(newListOfFiles(QString,QStringList)),
2078 receiver: q, SLOT(_q_directoryChanged(QString,QStringList)));
2079 q->connect(sender: &fileInfoGatherer, SIGNAL(updates(QString,QVector<QPair<QString,QFileInfo> >)),
2080 receiver: q, SLOT(_q_fileSystemChanged(QString,QVector<QPair<QString,QFileInfo> >)));
2081 q->connect(sender: &fileInfoGatherer, SIGNAL(nameResolved(QString,QString)),
2082 receiver: q, SLOT(_q_resolvedName(QString,QString)));
2083 q->connect(sender: &fileInfoGatherer, SIGNAL(directoryLoaded(QString)),
2084 receiver: q, SIGNAL(directoryLoaded(QString)));
2085#endif // filesystemwatcher
2086 q->connect(sender: &delayedSortTimer, SIGNAL(timeout()), receiver: q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection);
2087
2088 roleNames.insert(key: QFileSystemModel::FileIconRole,
2089 QByteArrayLiteral("fileIcon")); // == Qt::decoration
2090 roleNames.insert(key: QFileSystemModel::FilePathRole, QByteArrayLiteral("filePath"));
2091 roleNames.insert(key: QFileSystemModel::FileNameRole, QByteArrayLiteral("fileName"));
2092 roleNames.insert(key: QFileSystemModel::FilePermissions, QByteArrayLiteral("filePermissions"));
2093}
2094
2095/*!
2096 \internal
2097
2098 Returns \c false if node doesn't pass the filters otherwise true
2099
2100 QDir::Modified is not supported
2101 QDir::Drives is not supported
2102*/
2103bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) const
2104{
2105 // always accept drives
2106 if (node->parent == &root || bypassFilters.contains(key: node))
2107 return true;
2108
2109 // If we don't know anything yet don't accept it
2110 if (!node->hasInformation())
2111 return false;
2112
2113 const bool filterPermissions = ((filters & QDir::PermissionMask)
2114 && (filters & QDir::PermissionMask) != QDir::PermissionMask);
2115 const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
2116 const bool hideFiles = !(filters & QDir::Files);
2117 const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
2118 const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
2119 const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
2120 const bool hideHidden = !(filters & QDir::Hidden);
2121 const bool hideSystem = !(filters & QDir::System);
2122 const bool hideSymlinks = (filters & QDir::NoSymLinks);
2123 const bool hideDot = (filters & QDir::NoDot);
2124 const bool hideDotDot = (filters & QDir::NoDotDot);
2125
2126 // Note that we match the behavior of entryList and not QFileInfo on this.
2127 bool isDot = (node->fileName == QLatin1String("."));
2128 bool isDotDot = (node->fileName == QLatin1String(".."));
2129 if ( (hideHidden && !(isDot || isDotDot) && node->isHidden())
2130 || (hideSystem && node->isSystem())
2131 || (hideDirs && node->isDir())
2132 || (hideFiles && node->isFile())
2133 || (hideSymlinks && node->isSymLink())
2134 || (hideReadable && node->isReadable())
2135 || (hideWritable && node->isWritable())
2136 || (hideExecutable && node->isExecutable())
2137 || (hideDot && isDot)
2138 || (hideDotDot && isDotDot))
2139 return false;
2140
2141 return nameFilterDisables || passNameFilters(node);
2142}
2143
2144/*
2145 \internal
2146
2147 Returns \c true if node passes the name filters and should be visible.
2148 */
2149bool QFileSystemModelPrivate::passNameFilters(const QFileSystemNode *node) const
2150{
2151#if QT_CONFIG(regularexpression)
2152 if (nameFilters.isEmpty())
2153 return true;
2154
2155 // Check the name regularexpression filters
2156 if (!(node->isDir() && (filters & QDir::AllDirs))) {
2157 const QRegularExpression::PatternOptions options =
2158 (filters & QDir::CaseSensitive) ? QRegularExpression::NoPatternOption
2159 : QRegularExpression::CaseInsensitiveOption;
2160
2161 for (const auto &nameFilter : nameFilters) {
2162 QRegularExpression rx(QRegularExpression::wildcardToRegularExpression(str: nameFilter), options);
2163 QRegularExpressionMatch match = rx.match(subject: node->fileName);
2164 if (match.hasMatch())
2165 return true;
2166 }
2167 return false;
2168 }
2169#endif
2170 return true;
2171}
2172
2173QT_END_NAMESPACE
2174
2175#include "moc_qfilesystemmodel.cpp"
2176

source code of qtbase/src/widgets/dialogs/qfilesystemmodel.cpp