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