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

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