File indexing completed on 2024-05-19 04:56:03

0001 /**
0002  * \file filesystemmodel.cpp
0003  * \cond
0004  * Taken from Qt Git, revision e73bd4a
0005  * qtbase/src/widgets/dialogs/qfilesystemmodel.cpp
0006  * Adapted for Kid3 with the following changes:
0007  * - Remove Q prefix from class names
0008  * - Remove QT_..._CONFIG, QT_..._NAMESPACE, Q_..._EXPORT...
0009  * - Allow compilation without Qt private headers (USE_QT_PRIVATE_HEADERS)
0010  * - Remove moc includes
0011  * - Remove dependencies to Qt5::Widgets
0012  * - Do not display a message box from setData(), this will crash without GUI
0013  */
0014 /****************************************************************************
0015 **
0016 ** Copyright (C) 2016 The Qt Company Ltd.
0017 ** Contact: https://www.qt.io/licensing/
0018 **
0019 ** This file is part of the QtWidgets module of the Qt Toolkit.
0020 **
0021 ** $QT_BEGIN_LICENSE:LGPL$
0022 ** Commercial License Usage
0023 ** Licensees holding valid commercial Qt licenses may use this file in
0024 ** accordance with the commercial license agreement provided with the
0025 ** Software or, alternatively, in accordance with the terms contained in
0026 ** a written agreement between you and The Qt Company. For licensing terms
0027 ** and conditions see https://www.qt.io/terms-conditions. For further
0028 ** information use the contact form at https://www.qt.io/contact-us.
0029 **
0030 ** GNU Lesser General Public License Usage
0031 ** Alternatively, this file may be used under the terms of the GNU Lesser
0032 ** General Public License version 3 as published by the Free Software
0033 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
0034 ** packaging of this file. Please review the following information to
0035 ** ensure the GNU Lesser General Public License version 3 requirements
0036 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
0037 **
0038 ** GNU General Public License Usage
0039 ** Alternatively, this file may be used under the terms of the GNU
0040 ** General Public License version 2.0 or (at your option) the GNU General
0041 ** Public license version 3 or any later version approved by the KDE Free
0042 ** Qt Foundation. The licenses are as published by the Free Software
0043 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
0044 ** included in the packaging of this file. Please review the following
0045 ** information to ensure the GNU General Public License requirements will
0046 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
0047 ** https://www.gnu.org/licenses/gpl-3.0.html.
0048 **
0049 ** $QT_END_LICENSE$
0050 **
0051 ****************************************************************************/
0052 
0053 #include "filesystemmodel.h"
0054 #include <qlocale.h>
0055 #include <qmimedata.h>
0056 #include <qurl.h>
0057 #include <qdebug.h>
0058 #include <qcoreevent.h>
0059 #include <QtCore/qcollator.h>
0060 #include <QRegularExpression>
0061 
0062 #include <algorithm>
0063 #include <memory>
0064 
0065 #ifdef Q_OS_WIN
0066 #  include <QtCore/QVarLengthArray>
0067 #  include <qt_windows.h>
0068 #endif
0069 
0070 #include "abstractfiledecorationprovider.h"
0071 
0072 /*!
0073     \enum FileSystemModel::Roles
0074     \value FileIconRole
0075     \value FilePathRole
0076     \value FileNameRole
0077     \value FilePermissions
0078 */
0079 
0080 /*!
0081     \class FileSystemModel
0082     \since 4.4
0083 
0084     \brief The QFileSystemModel class provides a data model for the local filesystem.
0085 
0086     \ingroup model-view
0087     \inmodule QtWidgets
0088 
0089     This class provides access to the local filesystem, providing functions
0090     for renaming and removing files and directories, and for creating new
0091     directories. In the simplest case, it can be used with a suitable display
0092     widget as part of a browser or filter.
0093 
0094     QFileSystemModel can be accessed using the standard interface provided by
0095     QAbstractItemModel, but it also provides some convenience functions that are
0096     specific to a directory model.
0097     The fileInfo(), isDir(), fileName() and filePath() functions provide information
0098     about the underlying files and directories related to items in the model.
0099     Directories can be created and removed using mkdir(), rmdir().
0100 
0101     \note QFileSystemModel requires an instance of \l QApplication.
0102 
0103     \section1 Example Usage
0104 
0105     A directory model that displays the contents of a default directory
0106     is usually constructed with a parent object:
0107 
0108     \snippet shareddirmodel/main.cpp 2
0109 
0110     A tree view can be used to display the contents of the model
0111 
0112     \snippet shareddirmodel/main.cpp 4
0113 
0114     and the contents of a particular directory can be displayed by
0115     setting the tree view's root index:
0116 
0117     \snippet shareddirmodel/main.cpp 7
0118 
0119     The view's root index can be used to control how much of a
0120     hierarchical model is displayed. QFileSystemModel provides a convenience
0121     function that returns a suitable model index for a path to a
0122     directory within the model.
0123 
0124     \section1 Caching and Performance
0125 
0126     QFileSystemModel will not fetch any files or directories until setRootPath()
0127     is called.  This will prevent any unnecessary querying on the file system
0128     until that point such as listing the drives on Windows.
0129 
0130     Unlike QDirModel, QFileSystemModel uses a separate thread to populate
0131     itself so it will not cause the main thread to hang as the file system
0132     is being queried.  Calls to rowCount() will return 0 until the model
0133     populates a directory.
0134 
0135     QFileSystemModel keeps a cache with file information. The cache is
0136     automatically kept up to date using the QFileSystemWatcher.
0137 
0138     \sa {Model Classes}
0139 */
0140 
0141 /*!
0142     \fn bool QFileSystemModel::rmdir(const QModelIndex &index)
0143 
0144     Removes the directory corresponding to the model item \a index in the
0145     file system model and \b{deletes the corresponding directory from the
0146     file system}, returning true if successful. If the directory cannot be
0147     removed, false is returned.
0148 
0149     \warning This function deletes directories from the file system; it does
0150     \b{not} move them to a location where they can be recovered.
0151 
0152     \sa remove()
0153 */
0154 
0155 /*!
0156     \fn QIcon QFileSystemModel::fileName(const QModelIndex &index) const
0157 
0158     Returns the file name for the item stored in the model under the given
0159     \a index.
0160 */
0161 
0162 /*!
0163     \fn QIcon QFileSystemModel::fileIcon(const QModelIndex &index) const
0164 
0165     Returns the icon for the item stored in the model under the given
0166     \a index.
0167 */
0168 
0169 /*!
0170     \fn QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const
0171 
0172     Returns the QFileInfo for the item stored in the model under the given
0173     \a index.
0174 */
0175 QFileInfo FileSystemModel::fileInfo(const QModelIndex &index) const
0176 {
0177     Q_D(const FileSystemModel);
0178     return d->node(index)->fileInfo();
0179 }
0180 
0181 /*!
0182     \fn void QFileSystemModel::rootPathChanged(const QString &newPath);
0183 
0184     This signal is emitted whenever the root path has been changed to a \a newPath.
0185 */
0186 
0187 /*!
0188     \fn void QFileSystemModel::fileRenamed(const QString &path, const QString &oldName, const QString &newName)
0189 
0190     This signal is emitted whenever a file with the \a oldName is successfully
0191     renamed to \a newName.  The file is located in the directory \a path.
0192 */
0193 
0194 /*!
0195     \since 4.7
0196     \fn void QFileSystemModel::directoryLoaded(const QString &path)
0197 
0198     This signal is emitted when the gatherer thread has finished to load the \a path.
0199 
0200 */
0201 
0202 /*!
0203     \fn bool QFileSystemModel::remove(const QModelIndex &index)
0204 
0205     Removes the model item \a index from the file system model and \b{deletes the
0206     corresponding file from the file system}, returning true if successful. If the
0207     item cannot be removed, false is returned.
0208 
0209     \warning This function deletes files from the file system; it does \b{not}
0210     move them to a location where they can be recovered.
0211 
0212     \sa rmdir()
0213 */
0214 
0215 bool FileSystemModel::remove(const QModelIndex &aindex)
0216 {
0217     Q_D(FileSystemModel);
0218 
0219     const QString path = d->filePath(aindex);
0220     const QFileInfo fileInfo(path);
0221     const bool success = fileInfo.isFile() || fileInfo.isSymLink()
0222             ? QFile::remove(path) : QDir(path).removeRecursively();
0223 #ifndef QT_NO_FILESYSTEMWATCHER
0224     if (success) {
0225         auto dptr = const_cast<FileSystemModelPrivate*>(d_func());
0226         dptr->fileInfoGatherer.removePath(path);
0227     }
0228 #endif
0229     return success;
0230 }
0231 
0232 /*!
0233   Constructs a file system model with the given \a parent.
0234 */
0235 FileSystemModel::FileSystemModel(QObject *parent)
0236 #ifdef USE_QT_PRIVATE_HEADERS
0237     : QAbstractItemModel(*new FileSystemModelPrivate, parent)
0238 #else
0239     : QAbstractItemModel(parent),
0240       d_ptr(new FileSystemModelPrivate(this))
0241 #endif
0242 {
0243     Q_D(FileSystemModel);
0244     d->init();
0245 }
0246 
0247 /*!
0248     \internal
0249 */
0250 FileSystemModel::FileSystemModel(FileSystemModelPrivate &dd, QObject *parent)
0251 #if defined(USE_QT_PRIVATE_HEADERS)
0252     : QAbstractItemModel(dd, parent)
0253 #else
0254     : QAbstractItemModel(parent),
0255       d_ptr(&dd)
0256 #endif
0257 {
0258     Q_D(FileSystemModel);
0259     d->init();
0260 }
0261 
0262 /*!
0263   Destroys this file system model.
0264 */
0265 FileSystemModel::~FileSystemModel()
0266 {
0267 }
0268 
0269 /*!
0270     \reimp
0271 */
0272 QModelIndex FileSystemModel::index(int row, int column, const QModelIndex &parent) const
0273 {
0274     Q_D(const FileSystemModel);
0275     if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
0276         return QModelIndex();
0277 
0278     // get the parent node
0279     FileSystemModelPrivate::FileSystemNode *parentNode = d->indexValid(parent) ? d->node(parent) :
0280                                                    const_cast<FileSystemModelPrivate::FileSystemNode*>(&d->root);
0281     Q_ASSERT(parentNode);
0282 
0283     // now get the internal pointer for the index
0284     const int i = d->translateVisibleLocation(parentNode, row);
0285     if (i >= parentNode->visibleChildren.size())
0286         return QModelIndex();
0287     const QString &childName = parentNode->visibleChildren.at(i);
0288     const FileSystemModelPrivate::FileSystemNode *indexNode = parentNode->children.value(childName);
0289     Q_ASSERT(indexNode);
0290 
0291     return createIndex(row, column, const_cast<FileSystemModelPrivate::FileSystemNode*>(indexNode));
0292 }
0293 
0294 /*!
0295     \reimp
0296 */
0297 QModelIndex FileSystemModel::sibling(int row, int column, const QModelIndex &idx) const
0298 {
0299     if (row == idx.row() && column < FileSystemModelPrivate::NumColumns) {
0300         // cheap sibling operation: just adjust the column:
0301         return createIndex(row, column, idx.internalPointer());
0302     }
0303     // for anything else: call the default implementation
0304     // (this could probably be optimized, too):
0305     return QAbstractItemModel::sibling(row, column, idx);
0306 }
0307 
0308 /*!
0309     \overload
0310 
0311     Returns the model item index for the given \a path and \a column.
0312 */
0313 QModelIndex FileSystemModel::index(const QString &path, int column) const
0314 {
0315     Q_D(const FileSystemModel);
0316     FileSystemModelPrivate::FileSystemNode *node = d->node(path, false);
0317     return d->index(node, column);
0318 }
0319 
0320 /*!
0321     \internal
0322 
0323     Return the QFileSystemNode that goes to index.
0324   */
0325 FileSystemModelPrivate::FileSystemNode *FileSystemModelPrivate::node(const QModelIndex &index) const
0326 {
0327     if (!index.isValid())
0328         return const_cast<FileSystemNode*>(&root);
0329     auto indexNode = static_cast<FileSystemModelPrivate::FileSystemNode*>(index.internalPointer());
0330     Q_ASSERT(indexNode);
0331     return indexNode;
0332 }
0333 
0334 #ifdef Q_OS_WIN32
0335 static QString qt_GetLongPathName(const QString &strShortPath)
0336 {
0337     if (strShortPath.isEmpty()
0338         || strShortPath == QLatin1String(".") || strShortPath == QLatin1String(".."))
0339         return strShortPath;
0340     if (strShortPath.length() == 2 && strShortPath.endsWith(QLatin1Char(':')))
0341         return strShortPath.toUpper();
0342     const QString absPath = QDir(strShortPath).absolutePath();
0343     if (absPath.startsWith(QLatin1String("//"))
0344         || absPath.startsWith(QLatin1String("\\\\"))) // unc
0345         return QDir::fromNativeSeparators(absPath);
0346     if (absPath.startsWith(QLatin1Char('/')))
0347         return QString();
0348     const QString inputString = QLatin1String("\\\\?\\") + QDir::toNativeSeparators(absPath);
0349     // Kid3 WCHAR instead of TCHAR, GetLongPathNameW instead of GetLongPathName
0350     QVarLengthArray<WCHAR, MAX_PATH> buffer(MAX_PATH);
0351     DWORD result = ::GetLongPathNameW((wchar_t*)inputString.utf16(),
0352                                      buffer.data(),
0353                                      buffer.size());
0354     if (result > DWORD(buffer.size())) {
0355         buffer.resize(result);
0356         result = ::GetLongPathNameW((wchar_t*)inputString.utf16(),
0357                                    buffer.data(),
0358                                    buffer.size());
0359     }
0360     if (result > 4) {
0361         QString longPath = QString::fromWCharArray(buffer.data() + 4); // ignoring prefix
0362         longPath[0] = longPath.at(0).toUpper(); // capital drive letters
0363         return QDir::fromNativeSeparators(longPath);
0364     } else {
0365         return QDir::fromNativeSeparators(strShortPath);
0366     }
0367 }
0368 #endif
0369 
0370 /*!
0371     \internal
0372 
0373     Given a path return the matching QFileSystemNode or &root if invalid
0374 */
0375 FileSystemModelPrivate::FileSystemNode *FileSystemModelPrivate::node(const QString &path, bool fetch) const
0376 {
0377     Q_Q(const FileSystemModel);
0378     Q_UNUSED(q);
0379     if (path.isEmpty() || path == myComputer() || path.startsWith(QLatin1Char(':')))
0380         return const_cast<FileSystemModelPrivate::FileSystemNode*>(&root);
0381 
0382     // Construct the nodes up to the new root path if they need to be built
0383     QString absolutePath;
0384 #ifdef Q_OS_WIN32
0385     QString longPath = qt_GetLongPathName(path);
0386 #else
0387     QString longPath = path;
0388 #endif
0389     if (longPath == rootDir.path())
0390         absolutePath = rootDir.absolutePath();
0391     else
0392         absolutePath = QDir(longPath).absolutePath();
0393 
0394     // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const?
0395 #if QT_VERSION >= 0x050e00
0396     QStringList pathElements = absolutePath.split(QLatin1Char('/'), Qt::SkipEmptyParts);
0397 #else
0398     QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts);
0399 #endif
0400     if (pathElements.isEmpty()
0401 #if !defined(Q_OS_WIN)
0402         && QDir::fromNativeSeparators(longPath) != QLatin1String("/")
0403 #endif
0404         )
0405         return const_cast<FileSystemModelPrivate::FileSystemNode*>(&root);
0406     auto index = QModelIndex(); // start with "My Computer"
0407     QString elementPath;
0408     QChar separator = QLatin1Char('/');
0409     QString trailingSeparator;
0410 #if defined(Q_OS_WIN)
0411     if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path
0412         QString host = QLatin1String("\\\\") + pathElements.constFirst();
0413         if (absolutePath == QDir::fromNativeSeparators(host))
0414             absolutePath.append(QLatin1Char('/'));
0415         if (longPath.endsWith(QLatin1Char('/')) && !absolutePath.endsWith(QLatin1Char('/')))
0416             absolutePath.append(QLatin1Char('/'));
0417         if (absolutePath.endsWith(QLatin1Char('/')))
0418             trailingSeparator = QLatin1String("\\");
0419         int r = 0;
0420         FileSystemModelPrivate::FileSystemNode *rootNode = const_cast<FileSystemModelPrivate::FileSystemNode*>(&root);
0421         if (!root.children.contains(host.toLower())) {
0422             if (pathElements.count() == 1 && !absolutePath.endsWith(QLatin1Char('/')))
0423                 return rootNode;
0424             QFileInfo info(host);
0425             if (!info.exists())
0426                 return rootNode;
0427             FileSystemModelPrivate *p = const_cast<FileSystemModelPrivate*>(this);
0428             p->addNode(rootNode, host,info);
0429             p->addVisibleFiles(rootNode, QStringList(host));
0430         }
0431         r = rootNode->visibleLocation(host);
0432         r = translateVisibleLocation(rootNode, r);
0433         index = q->index(r, 0, QModelIndex());
0434         pathElements.pop_front();
0435         separator = QLatin1Char('\\');
0436         elementPath = host;
0437         elementPath.append(separator);
0438     } else {
0439         if (!pathElements.at(0).contains(QLatin1Char(':'))) {
0440             QString rootPath = QDir(longPath).rootPath();
0441             pathElements.prepend(rootPath);
0442         }
0443         if (pathElements.at(0).endsWith(QLatin1Char('/')))
0444             pathElements[0].chop(1);
0445     }
0446 #else
0447     // add the "/" item, since it is a valid path element on Unix
0448     if (absolutePath[0] == QLatin1Char('/'))
0449         pathElements.prepend(QLatin1String("/"));
0450 #endif
0451 
0452     FileSystemModelPrivate::FileSystemNode *parent = node(index);
0453 
0454     for (int i = 0; i < pathElements.count(); ++i) {
0455         QString element = pathElements.at(i);
0456         if (i != 0)
0457             elementPath.append(separator);
0458         elementPath.append(element);
0459         if (i == pathElements.count() - 1)
0460             elementPath.append(trailingSeparator);
0461 #ifdef Q_OS_WIN
0462         // On Windows, "filename    " and "filename" are equivalent and
0463         // "filename  .  " and "filename" are equivalent
0464         // "filename......." and "filename" are equivalent Task #133928
0465         // whereas "filename  .txt" is still "filename  .txt"
0466         // If after stripping the characters there is nothing left then we
0467         // just return the parent directory as it is assumed that the path
0468         // is referring to the parent
0469         while (element.endsWith(QLatin1Char('.')) || element.endsWith(QLatin1Char(' ')))
0470             element.chop(1);
0471         // Only filenames that can't possibly exist will be end up being empty
0472         if (element.isEmpty())
0473             return parent;
0474 #endif
0475         bool alreadyExisted = parent->children.contains(element);
0476 
0477         // we couldn't find the path element, we create a new node since we
0478         // _know_ that the path is valid
0479         if (alreadyExisted) {
0480             if (parent->children.count() == 0
0481                 || (parent->caseSensitive()
0482                     && parent->children.value(element)->fileName != element)
0483                 || (!parent->caseSensitive()
0484                     && parent->children.value(element)->fileName.toLower() != element.toLower()))
0485                 alreadyExisted = false;
0486         }
0487 
0488         FileSystemModelPrivate::FileSystemNode *node;
0489         if (!alreadyExisted) {
0490             // Someone might call ::index("file://cookie/monster/doesn't/like/veggies"),
0491             // a path that doesn't exists, I.E. don't blindly create directories.
0492             QFileInfo info(elementPath);
0493             if (!info.exists())
0494                 return const_cast<FileSystemModelPrivate::FileSystemNode*>(&root);
0495             auto p = const_cast<FileSystemModelPrivate*>(this);
0496             node = p->addNode(parent, element,info);
0497 #ifndef QT_NO_FILESYSTEMWATCHER
0498             node->populate(fileInfoGatherer.getInfo(info));
0499 #endif
0500         } else {
0501             node = parent->children.value(element);
0502         }
0503 
0504         Q_ASSERT(node);
0505         if (!node->isVisible) {
0506             // It has been filtered out
0507             if (alreadyExisted && node->hasInformation() && !fetch)
0508                 return const_cast<FileSystemModelPrivate::FileSystemNode*>(&root);
0509 
0510             auto p = const_cast<FileSystemModelPrivate*>(this);
0511             p->addVisibleFiles(parent, QStringList(element));
0512             if (!p->bypassFilters.contains(node))
0513                 p->bypassFilters[node] = true;
0514             QString dir = q->filePath(this->index(parent));
0515             if (!node->hasInformation() && fetch) {
0516                 Fetching f = { std::move(dir), std::move(element), node };
0517                 p->toFetch.append(std::move(f));
0518                 p->fetchingTimer.start(0, const_cast<FileSystemModel*>(q));
0519             }
0520         }
0521         parent = node;
0522     }
0523 
0524     return parent;
0525 }
0526 
0527 /*!
0528     \reimp
0529 */
0530 void FileSystemModel::timerEvent(QTimerEvent *event)
0531 {
0532     Q_D(FileSystemModel);
0533     if (event->timerId() == d->fetchingTimer.timerId()) {
0534         d->fetchingTimer.stop();
0535 #ifndef QT_NO_FILESYSTEMWATCHER
0536         for (int i = 0; i < d->toFetch.count(); ++i) {
0537             if (const FileSystemModelPrivate::FileSystemNode *node = d->toFetch.at(i).node;
0538                 !node->hasInformation()) {
0539                 d->fileInfoGatherer.fetchExtendedInformation(d->toFetch.at(i).dir,
0540                                                  QStringList(d->toFetch.at(i).file));
0541             } else {
0542                 // qDebug("yah!, you saved a little gerbil soul");
0543             }
0544         }
0545 #endif
0546         d->toFetch.clear();
0547     }
0548 }
0549 
0550 /*!
0551     Returns \c true if the model item \a index represents a directory;
0552     otherwise returns \c false.
0553 */
0554 bool FileSystemModel::isDir(const QModelIndex &index) const
0555 {
0556     // This function is for public usage only because it could create a file info
0557     Q_D(const FileSystemModel);
0558     if (!index.isValid())
0559         return true;
0560     if (FileSystemModelPrivate::FileSystemNode *n = d->node(index);
0561         n->hasInformation())
0562         return n->isDir();
0563     return fileInfo(index).isDir();
0564 }
0565 
0566 /*!
0567     Returns the size in bytes of \a index. If the file does not exist, 0 is returned.
0568   */
0569 qint64 FileSystemModel::size(const QModelIndex &index) const
0570 {
0571     Q_D(const FileSystemModel);
0572     if (!index.isValid())
0573         return 0;
0574     return d->node(index)->size();
0575 }
0576 
0577 /*!
0578     Returns the type of file \a index such as "Directory" or "JPEG file".
0579   */
0580 QString FileSystemModel::type(const QModelIndex &index) const
0581 {
0582     Q_D(const FileSystemModel);
0583     if (!index.isValid())
0584         return QString();
0585     return d->node(index)->type();
0586 }
0587 
0588 /*!
0589     Returns the date and time when \a index was last modified.
0590  */
0591 QDateTime FileSystemModel::lastModified(const QModelIndex &index) const
0592 {
0593     Q_D(const FileSystemModel);
0594     if (!index.isValid())
0595         return QDateTime();
0596     return d->node(index)->lastModified();
0597 }
0598 
0599 /*!
0600     \reimp
0601 */
0602 QModelIndex FileSystemModel::parent(const QModelIndex &index) const
0603 {
0604     Q_D(const FileSystemModel);
0605     if (!d->indexValid(index))
0606         return QModelIndex();
0607 
0608     FileSystemModelPrivate::FileSystemNode *indexNode = d->node(index);
0609     Q_ASSERT(indexNode != nullptr);
0610     FileSystemModelPrivate::FileSystemNode *parentNode = indexNode->parent;
0611     if (parentNode == nullptr || parentNode == &d->root)
0612         return QModelIndex();
0613 
0614     // get the parent's row
0615     FileSystemModelPrivate::FileSystemNode *grandParentNode = parentNode->parent;
0616     Q_ASSERT(grandParentNode->children.contains(parentNode->fileName));
0617     int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName));
0618     if (visualRow == -1)
0619         return QModelIndex();
0620     return createIndex(visualRow, 0, parentNode);
0621 }
0622 
0623 /*
0624     \internal
0625 
0626     return the index for node
0627 */
0628 QModelIndex FileSystemModelPrivate::index(const FileSystemModelPrivate::FileSystemNode *node, int column) const
0629 {
0630     Q_Q(const FileSystemModel);
0631     FileSystemModelPrivate::FileSystemNode *parentNode = node ? node->parent : nullptr;
0632     if (node == &root || !parentNode)
0633         return QModelIndex();
0634 
0635     // get the parent's row
0636     Q_ASSERT(node);
0637     if (!node->isVisible)
0638         return QModelIndex();
0639 
0640     int visualRow = translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName));
0641     return q->createIndex(visualRow, column, const_cast<FileSystemNode*>(node));
0642 }
0643 
0644 /*!
0645     \reimp
0646 */
0647 bool FileSystemModel::hasChildren(const QModelIndex &parent) const
0648 {
0649     Q_D(const FileSystemModel);
0650     if (parent.column() > 0)
0651         return false;
0652 
0653     if (!parent.isValid()) // drives
0654         return true;
0655 
0656     const FileSystemModelPrivate::FileSystemNode *indexNode = d->node(parent);
0657     Q_ASSERT(indexNode);
0658     return indexNode->isDir();
0659 }
0660 
0661 /*!
0662     \reimp
0663  */
0664 bool FileSystemModel::canFetchMore(const QModelIndex &parent) const
0665 {
0666     Q_D(const FileSystemModel);
0667     const FileSystemModelPrivate::FileSystemNode *indexNode = d->node(parent);
0668     return !indexNode->populatedChildren;
0669 }
0670 
0671 /*!
0672     \reimp
0673  */
0674 void FileSystemModel::fetchMore(const QModelIndex &parent)
0675 {
0676     Q_D(FileSystemModel);
0677     if (!d->setRootPath)
0678         return;
0679     FileSystemModelPrivate::FileSystemNode *indexNode = d->node(parent);
0680     if (indexNode->populatedChildren)
0681         return;
0682     indexNode->populatedChildren = true;
0683 #ifndef QT_NO_FILESYSTEMWATCHER
0684     d->fileInfoGatherer.list(filePath(parent));
0685 #endif
0686 }
0687 
0688 /*!
0689     \reimp
0690 */
0691 int FileSystemModel::rowCount(const QModelIndex &parent) const
0692 {
0693     Q_D(const FileSystemModel);
0694     if (parent.column() > 0)
0695         return 0;
0696 
0697     if (!parent.isValid())
0698         return d->root.visibleChildren.count();
0699 
0700     const FileSystemModelPrivate::FileSystemNode *parentNode = d->node(parent);
0701     return parentNode->visibleChildren.count();
0702 }
0703 
0704 /*!
0705     \reimp
0706 */
0707 int FileSystemModel::columnCount(const QModelIndex &parent) const
0708 {
0709     return parent.column() > 0 ? 0 : FileSystemModelPrivate::NumColumns;
0710 }
0711 
0712 /*!
0713     Returns the data stored under the given \a role for the item "My Computer".
0714 
0715     \sa Qt::ItemDataRole
0716  */
0717 QVariant FileSystemModel::myComputer(int role) const
0718 {
0719 #ifndef QT_NO_FILESYSTEMWATCHER
0720     Q_D(const FileSystemModel);
0721 #endif
0722     switch (role) {
0723     case Qt::DisplayRole:
0724         return FileSystemModelPrivate::myComputer();
0725 #ifndef QT_NO_FILESYSTEMWATCHER
0726     case Qt::DecorationRole:
0727         return d->fileInfoGatherer.decorationProvider()
0728                 ? d->fileInfoGatherer.decorationProvider()->computerDecoration()
0729                 : QVariant();
0730 #endif
0731     }
0732     return QVariant();
0733 }
0734 
0735 /*!
0736     \reimp
0737 */
0738 QVariant FileSystemModel::data(const QModelIndex &index, int role) const
0739 {
0740     Q_D(const FileSystemModel);
0741     if (!index.isValid() || index.model() != this)
0742         return QVariant();
0743 
0744     switch (role) {
0745     case Qt::EditRole:
0746     case Qt::DisplayRole:
0747         switch (index.column()) {
0748         case 0: return d->displayName(index);
0749         case 1: return d->size(index);
0750         case 2: return d->type(index);
0751         case 3: return d->time(index);
0752         default:
0753             qWarning("data: invalid display value column %d", index.column());
0754             break;
0755         }
0756         break;
0757     case FilePathRole:
0758         return filePath(index);
0759     case FileNameRole:
0760         return d->name(index);
0761     case Qt::DecorationRole:
0762         if (index.column() == 0) {
0763             QVariant icon = d->icon(index);
0764 #ifndef QT_NO_FILESYSTEMWATCHER
0765             if (icon.isNull()) {
0766                 if (d->node(index)->isDir())
0767                     icon = d->fileInfoGatherer.decorationProvider()
0768                             ? d->fileInfoGatherer.decorationProvider()->folderDecoration()
0769                             : QVariant();
0770                 else
0771                     icon = d->fileInfoGatherer.decorationProvider()
0772                             ? d->fileInfoGatherer.decorationProvider()->fileDecoration()
0773                             : QVariant();
0774             }
0775 #endif // QT_NO_FILESYSTEMWATCHER
0776             return icon;
0777         }
0778         break;
0779     case Qt::TextAlignmentRole:
0780         if (index.column() == 1)
0781             return QVariant(Qt::AlignTrailing | Qt::AlignVCenter);
0782         break;
0783     case FilePermissions:
0784         int p = permissions(index);
0785         return p;
0786     }
0787 
0788     return QVariant();
0789 }
0790 
0791 /*!
0792     \internal
0793 */
0794 QString FileSystemModelPrivate::size(const QModelIndex &index) const
0795 {
0796     if (!index.isValid())
0797         return QString();
0798     const FileSystemNode *n = node(index);
0799     if (n->isDir()) {
0800 #ifdef Q_OS_MAC
0801         return QLatin1String("--");
0802 #else
0803         return QLatin1String("");
0804 #endif
0805     // Windows   - ""
0806     // OS X      - "--"
0807     // Konqueror - "4 KB"
0808     // Nautilus  - "9 items" (the number of children)
0809     }
0810     return size(n->size());
0811 }
0812 
0813 QString FileSystemModelPrivate::size(qint64 bytes)
0814 {
0815     // According to the Si standard KB is 1000 bytes, KiB is 1024
0816     // but on windows sizes are calculated by dividing by 1024 so we do what they do.
0817     constexpr qint64 kb = 1024;
0818     constexpr qint64 mb = 1024 * kb;
0819     constexpr qint64 gb = 1024 * mb;
0820     constexpr qint64 tb = 1024 * gb;
0821     if (bytes >= tb)
0822         return FileSystemModel::tr("%1 TB").arg(QLocale().toString(static_cast<qreal>(bytes) / tb, 'f', 3));
0823     if (bytes >= gb)
0824         return FileSystemModel::tr("%1 GB").arg(QLocale().toString(static_cast<qreal>(bytes) / gb, 'f', 2));
0825     if (bytes >= mb)
0826         return FileSystemModel::tr("%1 MB").arg(QLocale().toString(static_cast<qreal>(bytes) / mb, 'f', 1));
0827     if (bytes >= kb)
0828         return FileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb));
0829     return FileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes));
0830 }
0831 
0832 /*!
0833     \internal
0834 */
0835 QString FileSystemModelPrivate::time(const QModelIndex &index) const
0836 {
0837     if (!index.isValid())
0838         return QString();
0839 #ifndef QT_NO_DATESTRING
0840     return QLocale::system().toString(node(index)->lastModified(), QLocale::ShortFormat);
0841 #else
0842     Q_UNUSED(index);
0843     return QString();
0844 #endif
0845 }
0846 
0847 /*
0848     \internal
0849 */
0850 QString FileSystemModelPrivate::type(const QModelIndex &index) const
0851 {
0852     if (!index.isValid())
0853         return QString();
0854     return node(index)->type();
0855 }
0856 
0857 /*!
0858     \internal
0859 */
0860 QString FileSystemModelPrivate::name(const QModelIndex &index) const
0861 {
0862     if (!index.isValid())
0863         return QString();
0864     FileSystemNode *dirNode = node(index);
0865     if (
0866 #ifndef QT_NO_FILESYSTEMWATCHER
0867         fileInfoGatherer.resolveSymlinks() &&
0868 #endif
0869         !resolvedSymLinks.isEmpty() && dirNode->isSymLink(/* ignoreNtfsSymLinks = */ true)) {
0870         QString fullPath = QDir::fromNativeSeparators(filePath(index));
0871         return resolvedSymLinks.value(fullPath, dirNode->fileName);
0872     }
0873     return dirNode->fileName;
0874 }
0875 
0876 /*!
0877     \internal
0878 */
0879 QString FileSystemModelPrivate::displayName(const QModelIndex &index) const
0880 {
0881 #if defined(Q_OS_WIN)
0882     FileSystemNode *dirNode = node(index);
0883     if (!dirNode->volumeName.isNull())
0884         return dirNode->volumeName + QLatin1String(" (") + name(index) + QLatin1Char(')');
0885 #endif
0886     return name(index);
0887 }
0888 
0889 /*!
0890     \internal
0891 */
0892 QVariant FileSystemModelPrivate::icon(const QModelIndex &index) const
0893 {
0894     if (!index.isValid())
0895         return QVariant();
0896     return node(index)->icon();
0897 }
0898 
0899 /*!
0900     \reimp
0901 */
0902 bool FileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role)
0903 {
0904     Q_D(FileSystemModel);
0905     if (!idx.isValid()
0906         || idx.column() != 0
0907         || role != Qt::EditRole
0908         || (flags(idx) & Qt::ItemIsEditable) == 0) {
0909         return false;
0910     }
0911 
0912     QString newName = value.toString();
0913     QString oldName = idx.data().toString();
0914     if (newName == oldName)
0915         return true;
0916 
0917     if (const QString parentPath = filePath(parent(idx));
0918         newName.isEmpty()
0919         || QDir::toNativeSeparators(newName).contains(QDir::separator())
0920         || !QDir(parentPath).rename(oldName, newName)) {
0921         emit fileRenameFailed(parentPath, oldName, newName);
0922         return false;
0923     } else {
0924         /*
0925             *After re-naming something we don't want the selection to change*
0926             - can't remove rows and later insert
0927             - can't quickly remove and insert
0928             - index pointer can't change because treeview doesn't use persistant index's
0929 
0930             - if this get any more complicated think of changing it to just
0931               use layoutChanged
0932          */
0933 
0934         FileSystemModelPrivate::FileSystemNode *indexNode = d->node(idx);
0935         FileSystemModelPrivate::FileSystemNode *parentNode = indexNode->parent;
0936         int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName);
0937 
0938         parentNode->visibleChildren.removeAt(visibleLocation);
0939         std::unique_ptr<FileSystemModelPrivate::FileSystemNode> nodeToRename(parentNode->children.take(oldName));
0940         nodeToRename->fileName = newName;
0941         nodeToRename->parent = parentNode;
0942 #ifndef QT_NO_FILESYSTEMWATCHER
0943         nodeToRename->populate(d->fileInfoGatherer.getInfo(QFileInfo(parentPath, newName)));
0944         if (nodeToRename->isDir()) {
0945             // Update watcher when directory is renamed
0946             d->fileInfoGatherer.removePath(parentPath + QLatin1Char('/') + oldName);
0947             d->fileInfoGatherer.addPath(parentPath + QLatin1Char('/') + newName);
0948         }
0949 #endif
0950         nodeToRename->isVisible = true;
0951         parentNode->children[newName] = nodeToRename.release();
0952         parentNode->visibleChildren.insert(visibleLocation, newName);
0953 
0954         d->delayedSort();
0955         emit fileRenamed(parentPath, oldName, newName);
0956     }
0957     return true;
0958 }
0959 
0960 /*!
0961     \reimp
0962 */
0963 QVariant FileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
0964 {
0965     switch (role) {
0966     case Qt::DecorationRole:
0967         if (section == 0) {
0968             Q_D(const FileSystemModel);
0969             return d->fileInfoGatherer.decorationProvider()
0970                     ? d->fileInfoGatherer.decorationProvider()->headerDecoration()
0971                     : QVariant();
0972         }
0973         break;
0974     case Qt::TextAlignmentRole:
0975         return Qt::AlignLeft;
0976     }
0977 
0978     if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
0979         return QAbstractItemModel::headerData(section, orientation, role);
0980 
0981     QString returnValue;
0982     switch (section) {
0983     case 0: returnValue = tr("Name");
0984             break;
0985     case 1: returnValue = tr("Size");
0986             break;
0987     case 2: returnValue =
0988 #ifdef Q_OS_MAC
0989                    tr("Kind", "Match OS X Finder");
0990 #else
0991                    tr("Type", "All other platforms");
0992 #endif
0993            break;
0994     // Windows   - Type
0995     // OS X      - Kind
0996     // Konqueror - File Type
0997     // Nautilus  - Type
0998     case 3: returnValue = tr("Date Modified");
0999             break;
1000     default: return QVariant();
1001     }
1002     return returnValue;
1003 }
1004 
1005 /*!
1006     \reimp
1007 */
1008 Qt::ItemFlags FileSystemModel::flags(const QModelIndex &index) const
1009 {
1010     Q_D(const FileSystemModel);
1011     Qt::ItemFlags flags = QAbstractItemModel::flags(index);
1012     if (!index.isValid())
1013         return flags;
1014 
1015     FileSystemModelPrivate::FileSystemNode *indexNode = d->node(index);
1016     if (d->nameFilterDisables && !d->passNameFilters(indexNode)) {
1017         flags &= ~Qt::ItemIsEnabled;
1018         // ### TODO you shouldn't be able to set this as the current item, task 119433
1019         return flags;
1020     }
1021 
1022     flags |= Qt::ItemIsDragEnabled;
1023     if (d->readOnly)
1024         return flags;
1025     if (index.column() == 0 && indexNode->permissions() & QFile::WriteUser) {
1026         flags |= Qt::ItemIsEditable;
1027         if (indexNode->isDir())
1028             flags |= Qt::ItemIsDropEnabled;
1029         else
1030             flags |= Qt::ItemNeverHasChildren;
1031     }
1032     return flags;
1033 }
1034 
1035 /*!
1036     \internal
1037 */
1038 void FileSystemModelPrivate::_q_performDelayedSort()
1039 {
1040     Q_Q(FileSystemModel);
1041     q->sort(sortColumn, sortOrder);
1042 }
1043 
1044 
1045 /*
1046     \internal
1047     Helper functor used by sort()
1048 */
1049 class FileSystemModelSorter
1050 {
1051 public:
1052     explicit FileSystemModelSorter(int column, bool ignorePunctuation = false) : sortColumn(column)
1053     {
1054         naturalCompare.setIgnorePunctuation(ignorePunctuation);
1055         naturalCompare.setNumericMode(true);
1056         naturalCompare.setCaseSensitivity(Qt::CaseInsensitive);
1057     }
1058 
1059     bool compareNodes(const FileSystemModelPrivate::FileSystemNode *l,
1060                     const FileSystemModelPrivate::FileSystemNode *r) const
1061     {
1062         switch (sortColumn) {
1063         case 0: {
1064 #ifndef Q_OS_MAC
1065             // place directories before files
1066             bool left = l->isDir();
1067             bool right = r->isDir();
1068             if (left ^ right)
1069                 return left;
1070 #endif
1071             return naturalCompare.compare(l->fileName, r->fileName) < 0;
1072                 }
1073         case 1:
1074         {
1075             // Directories go first
1076             bool left = l->isDir();
1077             bool right = r->isDir();
1078             if (left ^ right)
1079                 return left;
1080 
1081             qint64 sizeDifference = l->size() - r->size();
1082             if (sizeDifference == 0)
1083                 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1084 
1085             return sizeDifference < 0;
1086         }
1087         case 2:
1088         {
1089             int compare = naturalCompare.compare(l->type(), r->type());
1090             if (compare == 0)
1091                 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1092 
1093             return compare < 0;
1094         }
1095         case 3:
1096         {
1097             if (l->lastModified() == r->lastModified())
1098                 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1099 
1100             return l->lastModified() < r->lastModified();
1101         }
1102         }
1103         Q_ASSERT(false);
1104         return false;
1105     }
1106 
1107     bool operator()(const FileSystemModelPrivate::FileSystemNode *l,
1108                     const FileSystemModelPrivate::FileSystemNode *r) const
1109     {
1110         return compareNodes(l, r);
1111     }
1112 
1113 
1114 private:
1115     QCollator naturalCompare;
1116     int sortColumn;
1117 };
1118 
1119 /*
1120     \internal
1121 
1122     Sort all of the children of parent
1123 */
1124 void FileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent)
1125 {
1126     Q_Q(FileSystemModel);
1127     FileSystemModelPrivate::FileSystemNode *indexNode = node(parent);
1128     if (indexNode->children.count() == 0)
1129         return;
1130 
1131     QVector<FileSystemModelPrivate::FileSystemNode*> values;
1132 
1133     for (auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) {
1134         if (filtersAcceptsNode(iterator.value())) {
1135             values.append(iterator.value());
1136         } else {
1137             iterator.value()->isVisible = false;
1138         }
1139     }
1140     FileSystemModelSorter ms(column, sortIgnoringPunctuation);
1141     std::sort(values.begin(), values.end(), ms);
1142     // First update the new visible list
1143     indexNode->visibleChildren.clear();
1144     //No more dirty item we reset our internal dirty index
1145     indexNode->dirtyChildrenIndex = -1;
1146     const int numValues = values.count();
1147     indexNode->visibleChildren.reserve(numValues);
1148     for (int i = 0; i < numValues; ++i) {
1149         indexNode->visibleChildren.append(values.at(i)->fileName);
1150         values.at(i)->isVisible = true;
1151     }
1152 
1153     if (!disableRecursiveSort) {
1154         for (int i = 0; i < q->rowCount(parent); ++i) {
1155             const QModelIndex childIndex = q->index(i, 0, parent);
1156             //Only do a recursive sort on visible nodes
1157             if (FileSystemModelPrivate::FileSystemNode *childIndexNode = node(childIndex);
1158                 childIndexNode->isVisible)
1159                 sortChildren(column, childIndex);
1160         }
1161     }
1162 }
1163 
1164 /*!
1165     \reimp
1166 */
1167 void FileSystemModel::sort(int column, Qt::SortOrder order)
1168 {
1169     Q_D(FileSystemModel);
1170     if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
1171         return;
1172 
1173     emit layoutAboutToBeChanged();
1174     QModelIndexList oldList = persistentIndexList();
1175     QVector<QPair<FileSystemModelPrivate::FileSystemNode*, int> > oldNodes;
1176     const int nodeCount = oldList.count();
1177     oldNodes.reserve(nodeCount);
1178     for (int i = 0; i < nodeCount; ++i) {
1179         const QModelIndex &oldNode = oldList.at(i);
1180         QPair<FileSystemModelPrivate::FileSystemNode*, int> pair(d->node(oldNode), oldNode.column());
1181         oldNodes.append(pair);
1182     }
1183 
1184     if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
1185         //we sort only from where we are, don't need to sort all the model
1186         d->sortChildren(column, index(rootPath()));
1187         d->sortColumn = column;
1188         d->forceSort = false;
1189     }
1190     d->sortOrder = order;
1191 
1192     QModelIndexList newList;
1193     const int numOldNodes = oldNodes.size();
1194     newList.reserve(numOldNodes);
1195     for (int i = 0; i < numOldNodes; ++i) {
1196         const QPair<FileSystemModelPrivate::FileSystemNode*, int> &oldNode = oldNodes.at(i);
1197         newList.append(d->index(oldNode.first, oldNode.second));
1198     }
1199     changePersistentIndexList(oldList, newList);
1200     emit layoutChanged();
1201 }
1202 
1203 /*!
1204     Returns a list of MIME types that can be used to describe a list of items
1205     in the model.
1206 */
1207 QStringList FileSystemModel::mimeTypes() const
1208 {
1209     return QStringList(QLatin1String("text/uri-list"));
1210 }
1211 
1212 /*!
1213     Returns an object that contains a serialized description of the specified
1214     \a indexes. The format used to describe the items corresponding to the
1215     indexes is obtained from the mimeTypes() function.
1216 
1217     If the list of indexes is empty, 0 is returned rather than a serialized
1218     empty list.
1219 */
1220 QMimeData *FileSystemModel::mimeData(const QModelIndexList &indexes) const
1221 {
1222     QList<QUrl> urls;
1223     for (QList<QModelIndex>::const_iterator it = indexes.begin();
1224          it != indexes.end(); ++it)
1225         if (it->column() == 0)
1226             urls << QUrl::fromLocalFile(filePath(*it));
1227     auto data = new QMimeData();
1228     data->setUrls(urls);
1229     return data;
1230 }
1231 
1232 /*!
1233     Handles the \a data supplied by a drag and drop operation that ended with
1234     the given \a action over the row in the model specified by the \a row and
1235     \a column and by the \a parent index.
1236 
1237     \sa supportedDropActions()
1238 */
1239 bool FileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
1240                              int row, int column, const QModelIndex &parent)
1241 {
1242     Q_UNUSED(row);
1243     Q_UNUSED(column);
1244     if (!parent.isValid() || isReadOnly())
1245         return false;
1246 
1247     bool success = true;
1248     QString to = filePath(parent) + QDir::separator();
1249 
1250     QList<QUrl> urls = data->urls();
1251     QList<QUrl>::const_iterator it = urls.constBegin();
1252 
1253     switch (action) {
1254     case Qt::CopyAction:
1255         for (; it != urls.constEnd(); ++it) {
1256             QString path = it->toLocalFile();
1257             success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
1258         }
1259         break;
1260     case Qt::LinkAction:
1261         for (; it != urls.constEnd(); ++it) {
1262             QString path = it->toLocalFile();
1263             success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
1264         }
1265         break;
1266     case Qt::MoveAction:
1267         for (; it != urls.constEnd(); ++it) {
1268             QString path = it->toLocalFile();
1269             success = QFile::rename(path, to + QFileInfo(path).fileName()) && success;
1270         }
1271         break;
1272     default:
1273         return false;
1274     }
1275 
1276     return success;
1277 }
1278 
1279 /*!
1280     \reimp
1281 */
1282 Qt::DropActions FileSystemModel::supportedDropActions() const
1283 {
1284     return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
1285 }
1286 
1287 /*!
1288     Returns the path of the item stored in the model under the
1289     \a index given.
1290 */
1291 QString FileSystemModel::filePath(const QModelIndex &index) const
1292 {
1293     Q_D(const FileSystemModel);
1294     QString fullPath = d->filePath(index);
1295     if (FileSystemModelPrivate::FileSystemNode *dirNode = d->node(index);
1296         dirNode->isSymLink()
1297 #ifndef QT_NO_FILESYSTEMWATCHER
1298         && d->fileInfoGatherer.resolveSymlinks()
1299 #endif
1300         && d->resolvedSymLinks.contains(fullPath)
1301         && dirNode->isDir()) {
1302         QFileInfo resolvedInfo(fullPath);
1303         resolvedInfo = QFileInfo(resolvedInfo.canonicalFilePath());
1304         if (resolvedInfo.exists())
1305             return resolvedInfo.filePath();
1306     }
1307     return fullPath;
1308 }
1309 
1310 QString FileSystemModelPrivate::filePath(const QModelIndex &index) const
1311 {
1312     Q_Q(const FileSystemModel);
1313     Q_UNUSED(q);
1314     if (!index.isValid())
1315         return QString();
1316     Q_ASSERT(index.model() == q);
1317 
1318     QStringList path;
1319     QModelIndex idx = index;
1320     while (idx.isValid()) {
1321         if (FileSystemModelPrivate::FileSystemNode *dirNode = node(idx))
1322             path.prepend(dirNode->fileName);
1323         idx = idx.parent();
1324     }
1325     QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator()));
1326 #if !defined(Q_OS_WIN)
1327     if (fullPath.length() > 2 && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/'))
1328         fullPath = fullPath.mid(1);
1329 #else
1330     if (fullPath.length() == 2 && fullPath.endsWith(QLatin1Char(':')))
1331         fullPath.append(QLatin1Char('/'));
1332 #endif
1333     return fullPath;
1334 }
1335 
1336 /*!
1337     Create a directory with the \a name in the \a parent model index.
1338 */
1339 QModelIndex FileSystemModel::mkdir(const QModelIndex &parent, const QString &name)
1340 {
1341     Q_D(FileSystemModel);
1342     if (!parent.isValid())
1343         return parent;
1344 
1345     QDir dir(filePath(parent));
1346     if (!dir.mkdir(name))
1347         return QModelIndex();
1348     FileSystemModelPrivate::FileSystemNode *parentNode = d->node(parent);
1349     d->addNode(parentNode, name, QFileInfo());
1350     Q_ASSERT(parentNode->children.contains(name));
1351     FileSystemModelPrivate::FileSystemNode *node = parentNode->children[name];
1352 #ifndef QT_NO_FILESYSTEMWATCHER
1353     node->populate(d->fileInfoGatherer.getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name)));
1354 #endif
1355     d->addVisibleFiles(parentNode, QStringList(name));
1356     return d->index(node);
1357 }
1358 
1359 /*!
1360     Returns the complete OR-ed together combination of QFile::Permission for the \a index.
1361  */
1362 QFile::Permissions FileSystemModel::permissions(const QModelIndex &index) const
1363 {
1364     Q_D(const FileSystemModel);
1365     return d->node(index)->permissions();
1366 }
1367 
1368 /*!
1369     Free resources used by file system model and reset root path to default.
1370     The model state will be as after creation, but all properties except the
1371     root path remain the same.
1372  */
1373 void FileSystemModel::clear()
1374 {
1375     Q_D(FileSystemModel);
1376     beginResetModel();
1377     d->clear();
1378     endResetModel();
1379 }
1380 
1381 /*!
1382     Sets the directory that is being watched by the model to \a newPath by
1383     installing a \l{QFileSystemWatcher}{file system watcher} on it. Any
1384     changes to files and directories within this directory will be
1385     reflected in the model.
1386 
1387     If the path is changed, the rootPathChanged() signal will be emitted.
1388 
1389     \note This function does not change the structure of the model or
1390     modify the data available to views. In other words, the "root" of
1391     the model is \e not changed to include only files and directories
1392     within the directory specified by \a newPath in the file system.
1393   */
1394 QModelIndex FileSystemModel::setRootPath(const QString &newPath)
1395 {
1396     Q_D(FileSystemModel);
1397 #ifdef Q_OS_WIN
1398 #ifdef Q_OS_WIN32
1399     QString longNewPath = qt_GetLongPathName(newPath);
1400 #else
1401     QString longNewPath = QDir::fromNativeSeparators(newPath);
1402 #endif
1403 #else
1404     QString longNewPath = newPath;
1405 #endif
1406     QDir newPathDir(longNewPath);
1407     //we remove .. and . from the given path if exist
1408     if (!newPath.isEmpty()) {
1409         longNewPath = QDir::cleanPath(longNewPath);
1410         newPathDir.setPath(longNewPath);
1411     }
1412 
1413     d->setRootPath = true;
1414 
1415     //user don't ask for the root path ("") but the conversion failed
1416     if (!newPath.isEmpty() && longNewPath.isEmpty())
1417         return d->index(rootPath());
1418 
1419     if (d->rootDir.path() == longNewPath)
1420         return d->index(rootPath());
1421 
1422     bool showDrives = longNewPath.isEmpty() || longNewPath == d->myComputer();
1423     if (!showDrives && !newPathDir.exists())
1424         return d->index(rootPath());
1425 
1426     //We remove the watcher on the previous path
1427     if (!rootPath().isEmpty() && rootPath() != QLatin1String(".")) {
1428         //This remove the watcher for the old rootPath
1429 #ifndef QT_NO_FILESYSTEMWATCHER
1430         d->fileInfoGatherer.removePath(rootPath());
1431 #endif
1432         //This line "marks" the node as dirty, so the next fetchMore
1433         //call on the path will ask the gatherer to install a watcher again
1434         //But it doesn't re-fetch everything
1435         d->node(rootPath())->populatedChildren = false;
1436     }
1437 
1438     // We have a new valid root path
1439     d->rootDir = newPathDir;
1440     QModelIndex newRootIndex;
1441     if (showDrives) {
1442         // otherwise dir will become '.'
1443         d->rootDir.setPath(QLatin1String(""));
1444     } else {
1445         newRootIndex = d->index(newPathDir.path());
1446     }
1447     fetchMore(newRootIndex);
1448     emit rootPathChanged(longNewPath);
1449     d->forceSort = true;
1450     d->delayedSort();
1451     return newRootIndex;
1452 }
1453 
1454 /*!
1455     The currently set root path
1456 
1457     \sa rootDirectory()
1458 */
1459 QString FileSystemModel::rootPath() const
1460 {
1461     Q_D(const FileSystemModel);
1462     return d->rootDir.path();
1463 }
1464 
1465 /*!
1466     The currently set directory
1467 
1468     \sa rootPath()
1469 */
1470 QDir FileSystemModel::rootDirectory() const
1471 {
1472     Q_D(const FileSystemModel);
1473     QDir dir(d->rootDir);
1474     dir.setNameFilters(nameFilters());
1475     dir.setFilter(filter());
1476     return dir;
1477 }
1478 
1479 /*!
1480     Sets the \a provider of file icons for the directory model.
1481 */
1482 void FileSystemModel::setDecorationProvider(AbstractFileDecorationProvider *provider)
1483 {
1484     Q_D(FileSystemModel);
1485 #ifndef QT_NO_FILESYSTEMWATCHER
1486     d->fileInfoGatherer.setDecorationProvider(provider);
1487 #endif
1488     d->root.updateIcon(provider, QString());
1489 }
1490 
1491 /*!
1492     Returns the file icon provider for this directory model.
1493 */
1494 AbstractFileDecorationProvider *FileSystemModel::decorationProvider() const
1495 {
1496 #ifndef QT_NO_FILESYSTEMWATCHER
1497     Q_D(const FileSystemModel);
1498     return d->fileInfoGatherer.decorationProvider();
1499 #else
1500     return 0;
1501 #endif
1502 }
1503 
1504 /*!
1505     Sets the directory model's filter to that specified by \a filters.
1506 
1507     Note that the filter you set should always include the QDir::AllDirs enum value,
1508     otherwise QFileSystemModel won't be able to read the directory structure.
1509 
1510     \sa QDir::Filters
1511 */
1512 void FileSystemModel::setFilter(QDir::Filters filters)
1513 {
1514     Q_D(FileSystemModel);
1515     if (d->filters == filters)
1516         return;
1517     d->filters = filters;
1518     // CaseSensitivity might have changed
1519     setNameFilters(nameFilters());
1520     d->forceSort = true;
1521     d->delayedSort();
1522 }
1523 
1524 /*!
1525     Returns the filter specified for the directory model.
1526 
1527     If a filter has not been set, the default filter is QDir::AllEntries |
1528     QDir::NoDotAndDotDot | QDir::AllDirs.
1529 
1530     \sa QDir::Filters
1531 */
1532 QDir::Filters FileSystemModel::filter() const
1533 {
1534     Q_D(const FileSystemModel);
1535     return d->filters;
1536 }
1537 
1538 /*!
1539     \property QFileSystemModel::resolveSymlinks
1540     \brief Whether the directory model should resolve symbolic links
1541 
1542     This is only relevant on Windows.
1543 
1544     By default, this property is \c true.
1545 */
1546 void FileSystemModel::setResolveSymlinks(bool enable)
1547 {
1548 #ifndef QT_NO_FILESYSTEMWATCHER
1549     Q_D(FileSystemModel);
1550     d->fileInfoGatherer.setResolveSymlinks(enable);
1551 #else
1552     Q_UNUSED(enable)
1553 #endif
1554 }
1555 
1556 bool FileSystemModel::resolveSymlinks() const
1557 {
1558 #ifndef QT_NO_FILESYSTEMWATCHER
1559     Q_D(const FileSystemModel);
1560     return d->fileInfoGatherer.resolveSymlinks();
1561 #else
1562     return false;
1563 #endif
1564 }
1565 
1566 /*!
1567     \property QFileSystemModel::readOnly
1568     \brief Whether the directory model allows writing to the file system
1569 
1570     If this property is set to false, the directory model will allow renaming, copying
1571     and deleting of files and directories.
1572 
1573     This property is \c true by default
1574 */
1575 void FileSystemModel::setReadOnly(bool enable)
1576 {
1577     Q_D(FileSystemModel);
1578     d->readOnly = enable;
1579 }
1580 
1581 bool FileSystemModel::isReadOnly() const
1582 {
1583     Q_D(const FileSystemModel);
1584     return d->readOnly;
1585 }
1586 
1587 /*!
1588     \property QFileSystemModel::nameFilterDisables
1589     \brief Whether files that don't pass the name filter are hidden or disabled
1590 
1591     This property is \c true by default
1592 */
1593 void FileSystemModel::setNameFilterDisables(bool enable)
1594 {
1595     Q_D(FileSystemModel);
1596     if (d->nameFilterDisables == enable)
1597         return;
1598     d->nameFilterDisables = enable;
1599     d->forceSort = true;
1600     d->delayedSort();
1601 }
1602 
1603 bool FileSystemModel::nameFilterDisables() const
1604 {
1605     Q_D(const FileSystemModel);
1606     return d->nameFilterDisables;
1607 }
1608 
1609 /*!
1610     Sets the name \a filters to apply against the existing files.
1611 */
1612 void FileSystemModel::setNameFilters(const QStringList &filters)
1613 {
1614     // Prep the regexp's ahead of time
1615 #ifndef QT_NO_REGEXP
1616     Q_D(FileSystemModel);
1617 
1618     if (!d->bypassFilters.isEmpty()) {
1619         // update the bypass filter to only bypass the stuff that must be kept around
1620         d->bypassFilters.clear();
1621         // We guarantee that rootPath will stick around
1622         QPersistentModelIndex root(index(rootPath()));
1623         const QModelIndexList persistentList = persistentIndexList();
1624         for (const auto &persistentIndex : persistentList) {
1625             FileSystemModelPrivate::FileSystemNode *node = d->node(persistentIndex);
1626             while (node) {
1627                 if (d->bypassFilters.contains(node))
1628                     break;
1629                 if (node->isDir())
1630                     d->bypassFilters[node] = true;
1631                 node = node->parent;
1632             }
1633         }
1634     }
1635 
1636     d->nameFilters = filters;
1637     d->forceSort = true;
1638     d->delayedSort();
1639 #endif
1640 }
1641 
1642 /*!
1643     Returns a list of filters applied to the names in the model.
1644 */
1645 QStringList FileSystemModel::nameFilters() const
1646 {
1647 #ifndef QT_NO_REGEXP
1648     Q_D(const FileSystemModel);
1649     return d->nameFilters;
1650 #else
1651     return QStringList();
1652 #endif
1653 }
1654 
1655 /*!
1656     \brief Whether punctuation characters and symbols are ignored when sorting,
1657     \c false by default.
1658     \sa QCollator::setIgnorePunctuation
1659 */
1660 void FileSystemModel::setSortIgnoringPunctuation(bool ignore)
1661 {
1662     Q_D(FileSystemModel);
1663     d->sortIgnoringPunctuation = ignore;
1664 }
1665 
1666 bool FileSystemModel::sortIgnoringPunctuation() const
1667 {
1668     Q_D(const FileSystemModel);
1669     return d->sortIgnoringPunctuation;
1670 }
1671 
1672 /*!
1673     \reimp
1674 */
1675 bool FileSystemModel::event(QEvent *event)
1676 {
1677 #ifndef QT_NO_FILESYSTEMWATCHER
1678     Q_D(FileSystemModel);
1679     if (event->type() == QEvent::LanguageChange) {
1680         d->root.retranslateStrings(d->fileInfoGatherer.decorationProvider(), QString());
1681         return true;
1682     }
1683 #endif
1684     return QAbstractItemModel::event(event);
1685 }
1686 
1687 bool FileSystemModel::rmdir(const QModelIndex &aindex)
1688 {
1689     QString path = filePath(aindex);
1690     const bool success = QDir().rmdir(path);
1691 #ifndef QT_NO_FILESYSTEMWATCHER
1692     if (success) {
1693         auto d = const_cast<FileSystemModelPrivate*>(d_func());
1694         d->fileInfoGatherer.removePath(path);
1695     }
1696 #endif
1697     return success;
1698 }
1699 
1700 /*!
1701      \internal
1702 
1703     Performed quick listing and see if any files have been added or removed,
1704     then fetch more information on visible files.
1705  */
1706 void FileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files)
1707 {
1708     FileSystemModelPrivate::FileSystemNode *parentNode = node(directory, false);
1709     if (parentNode->children.count() == 0)
1710         return;
1711     QStringList toRemove;
1712     QStringList newFiles = files;
1713     std::sort(newFiles.begin(), newFiles.end());
1714     for (auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) {
1715         if (QStringList::iterator iterator = std::lower_bound(
1716               newFiles.begin(), newFiles.end(), i.value()->fileName);
1717             iterator == newFiles.end() || i.value()->fileName < *iterator)
1718             toRemove.append(i.value()->fileName);
1719     }
1720     for (int i = 0 ; i < toRemove.count() ; ++i )
1721         removeNode(parentNode, toRemove[i]);
1722 }
1723 
1724 /*!
1725     \internal
1726 
1727     Adds a new file to the children of parentNode
1728 
1729     *WARNING* this will change the count of children
1730 */
1731 FileSystemModelPrivate::FileSystemNode* FileSystemModelPrivate::addNode(FileSystemNode *parentNode, const QString &fileName, const QFileInfo& info)
1732 {
1733     // In the common case, itemLocation == count() so check there first
1734     auto node = new FileSystemModelPrivate::FileSystemNode(fileName, parentNode);
1735 #ifndef QT_NO_FILESYSTEMWATCHER
1736     node->populate(ExtendedInformation(info));
1737 #else
1738     Q_UNUSED(info)
1739 #endif
1740 #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
1741     //The parentNode is "" so we are listing the drives
1742     if (parentNode->fileName.isEmpty()) {
1743         wchar_t name[MAX_PATH + 1];
1744         //GetVolumeInformation requires to add trailing backslash
1745         const QString nodeName = fileName + QLatin1String("\\");
1746         // Kid3 GetVolumeInformationW instead of GetVolumeInformation
1747         BOOL success = ::GetVolumeInformationW((wchar_t *)(nodeName.utf16()),
1748                 name, MAX_PATH + 1, NULL, 0, NULL, NULL, 0);
1749         if (success && name[0])
1750             node->volumeName = QString::fromWCharArray(name);
1751     }
1752 #endif
1753     Q_ASSERT(!parentNode->children.contains(fileName));
1754     parentNode->children.insert(fileName, node);
1755     return node;
1756 }
1757 
1758 /*!
1759     \internal
1760 
1761     File at parentNode->children(itemLocation) has been removed, remove from the lists
1762     and emit signals if necessary
1763 
1764     *WARNING* this will change the count of children and could change visibleChildren
1765  */
1766 void FileSystemModelPrivate::removeNode(FileSystemModelPrivate::FileSystemNode *parentNode, const QString& name)
1767 {
1768     Q_Q(FileSystemModel);
1769     QModelIndex parent = index(parentNode);
1770     bool indexHidden = isHiddenByFilter(parentNode, parent);
1771 
1772     int vLocation = parentNode->visibleLocation(name);
1773     if (vLocation >= 0 && !indexHidden)
1774         q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1775                                        translateVisibleLocation(parentNode, vLocation));
1776     FileSystemNode * node = parentNode->children.take(name);
1777 #ifndef QT_NO_FILESYSTEMWATCHER
1778     if (node->info && node->info->isDir()) {
1779         // Remove watched path when directory is removed or renamed
1780         fileInfoGatherer.removePath(node->info->fileInfo().filePath());
1781     }
1782 #endif
1783     delete node;
1784     // cleanup sort files after removing rather then re-sorting which is O(n)
1785     if (vLocation >= 0)
1786         parentNode->visibleChildren.removeAt(vLocation);
1787     if (vLocation >= 0 && !indexHidden)
1788         q->endRemoveRows();
1789 }
1790 
1791 /*!
1792     \internal
1793 
1794     File at parentNode->children(itemLocation) was not visible before, but now should be
1795     and emit signals if necessary.
1796 
1797     *WARNING* this will change the visible count
1798  */
1799 void FileSystemModelPrivate::addVisibleFiles(FileSystemNode *parentNode, const QStringList &newFiles)
1800 {
1801     Q_Q(FileSystemModel);
1802     QModelIndex parent = index(parentNode);
1803     bool indexHidden = isHiddenByFilter(parentNode, parent);
1804     if (!indexHidden) {
1805         q->beginInsertRows(parent, parentNode->visibleChildren.count() , parentNode->visibleChildren.count() + newFiles.count() - 1);
1806     }
1807 
1808     if (parentNode->dirtyChildrenIndex == -1)
1809         parentNode->dirtyChildrenIndex = parentNode->visibleChildren.count();
1810 
1811     for (const auto &newFile : newFiles) {
1812         parentNode->visibleChildren.append(newFile);
1813         parentNode->children.value(newFile)->isVisible = true;
1814     }
1815     if (!indexHidden)
1816       q->endInsertRows();
1817 }
1818 
1819 /*!
1820     \internal
1821 
1822     File was visible before, but now should NOT be
1823 
1824     *WARNING* this will change the visible count
1825  */
1826 void FileSystemModelPrivate::removeVisibleFile(FileSystemNode *parentNode, int vLocation)
1827 {
1828     Q_Q(FileSystemModel);
1829     if (vLocation == -1)
1830         return;
1831     QModelIndex parent = index(parentNode);
1832     bool indexHidden = isHiddenByFilter(parentNode, parent);
1833     if (!indexHidden)
1834         q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1835                                        translateVisibleLocation(parentNode, vLocation));
1836     parentNode->children.value(parentNode->visibleChildren.at(vLocation))->isVisible = false;
1837     parentNode->visibleChildren.removeAt(vLocation);
1838     if (!indexHidden)
1839         q->endRemoveRows();
1840 }
1841 
1842 /*!
1843     \internal
1844 
1845     The thread has received new information about files,
1846     update and emit dataChanged if it has actually changed.
1847  */
1848 void FileSystemModelPrivate::_q_fileSystemChanged(const QString &path, const QVector<QPair<QString, QFileInfo> > &updates)
1849 {
1850 #ifndef QT_NO_FILESYSTEMWATCHER
1851     Q_Q(FileSystemModel);
1852     QVector<QString> rowsToUpdate;
1853     QStringList newFiles;
1854     FileSystemModelPrivate::FileSystemNode *parentNode = node(path, false);
1855     QModelIndex parentIndex = index(parentNode);
1856     for (const auto &update : updates) {
1857         QString fileName = update.first;
1858         Q_ASSERT(!fileName.isEmpty());
1859         ExtendedInformation info = fileInfoGatherer.getInfo(update.second);
1860         if (bool previouslyHere = parentNode->children.contains(fileName);
1861             !previouslyHere) {
1862             addNode(parentNode, fileName, info.fileInfo());
1863         }
1864         FileSystemModelPrivate::FileSystemNode * node = parentNode->children.value(fileName);
1865         bool isCaseSensitive = parentNode->caseSensitive();
1866         if (isCaseSensitive) {
1867             if (node->fileName != fileName)
1868                 continue;
1869         } else {
1870             if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0)
1871                 continue;
1872         }
1873         if (isCaseSensitive) {
1874             Q_ASSERT(node->fileName == fileName);
1875         } else {
1876             node->fileName = fileName;
1877         }
1878 
1879         if (*node != info ) {
1880             node->populate(info);
1881             bypassFilters.remove(node);
1882             // brand new information.
1883             if (filtersAcceptsNode(node)) {
1884                 if (!node->isVisible) {
1885                     newFiles.append(fileName);
1886                 } else {
1887                     rowsToUpdate.append(fileName);
1888                 }
1889             } else {
1890                 if (node->isVisible) {
1891                     int visibleLocation = parentNode->visibleLocation(fileName);
1892                     removeVisibleFile(parentNode, visibleLocation);
1893                 } else {
1894                     // The file is not visible, don't do anything
1895                 }
1896             }
1897         }
1898     }
1899 
1900     // bundle up all of the changed signals into as few as possible.
1901     std::sort(rowsToUpdate.begin(), rowsToUpdate.end());
1902     QString min;
1903     QString max;
1904     for (int i = 0; i < rowsToUpdate.count(); ++i) {
1905         QString value = rowsToUpdate.at(i);
1906         //##TODO is there a way to bundle signals with QString as the content of the list?
1907         /*if (min.isEmpty()) {
1908             min = value;
1909             if (i != rowsToUpdate.count() - 1)
1910                 continue;
1911         }
1912         if (i != rowsToUpdate.count() - 1) {
1913             if ((value == min + 1 && max.isEmpty()) || value == max + 1) {
1914                 max = value;
1915                 continue;
1916             }
1917         }*/
1918         max = value;
1919         min = value;
1920         int visibleMin = parentNode->visibleLocation(min);
1921         int visibleMax = parentNode->visibleLocation(max);
1922         if (visibleMin >= 0
1923             && visibleMin < parentNode->visibleChildren.count()
1924             && parentNode->visibleChildren.at(visibleMin) == min
1925             && visibleMax >= 0) {
1926             QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMin), 0, parentIndex);
1927             QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMax), 3, parentIndex);
1928             emit q->dataChanged(bottom, top);
1929         }
1930 
1931         /*min = QString();
1932         max = QString();*/
1933     }
1934 
1935     if (newFiles.count() > 0) {
1936         addVisibleFiles(parentNode, newFiles);
1937     }
1938 
1939     if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) {
1940         forceSort = true;
1941         delayedSort();
1942     }
1943 #else
1944     Q_UNUSED(path)
1945     Q_UNUSED(updates)
1946 #endif // !QT_NO_FILESYSTEMWATCHER
1947 }
1948 
1949 /*!
1950     \internal
1951 */
1952 void FileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName)
1953 {
1954     resolvedSymLinks[fileName] = resolvedName;
1955 }
1956 
1957 void FileSystemModelPrivate::clear()
1958 {
1959     forceSort = true;
1960     setRootPath = false;
1961     rootDir.setPath(QString::fromLatin1("."));
1962 #ifndef QT_NO_FILESYSTEMWATCHER
1963     fileInfoGatherer.clear();
1964 #endif
1965     delayedSortTimer.stop();
1966     bypassFilters.clear();
1967     resolvedSymLinks.clear();
1968     root.clear();
1969     fetchingTimer.stop();
1970     toFetch.clear();
1971 }
1972 
1973 /*!
1974     \internal
1975 */
1976 void FileSystemModelPrivate::init()
1977 {
1978     Q_Q(FileSystemModel);
1979     qRegisterMetaType<QVector<QPair<QString,QFileInfo> > >();
1980 #ifndef QT_NO_FILESYSTEMWATCHER
1981     q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(QString,QStringList)),
1982                q, SLOT(_q_directoryChanged(QString,QStringList)));
1983     q->connect(&fileInfoGatherer, SIGNAL(updates(QString,QVector<QPair<QString,QFileInfo> >)),
1984             q, SLOT(_q_fileSystemChanged(QString,QVector<QPair<QString,QFileInfo> >)));
1985     q->connect(&fileInfoGatherer, SIGNAL(nameResolved(QString,QString)),
1986             q, SLOT(_q_resolvedName(QString,QString)));
1987     q->connect(&fileInfoGatherer, SIGNAL(directoryLoaded(QString)),
1988                q, SIGNAL(directoryLoaded(QString)));
1989 #endif // !QT_NO_FILESYSTEMWATCHER
1990     q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection);
1991 
1992 #if QT_VERSION >= 0x050f00
1993     roleNames.insert(FileSystemModel::FileIconRole, QByteArrayLiteral("fileIcon")); // == Qt::decoration
1994 #else
1995     roleNames.insertMulti(FileSystemModel::FileIconRole, QByteArrayLiteral("fileIcon")); // == Qt::decoration
1996 #endif
1997     roleNames.insert(FileSystemModel::FilePathRole, QByteArrayLiteral("filePath"));
1998     roleNames.insert(FileSystemModel::FileNameRole, QByteArrayLiteral("fileName"));
1999     roleNames.insert(FileSystemModel::FilePermissions, QByteArrayLiteral("filePermissions"));
2000 }
2001 
2002 #ifndef USE_QT_PRIVATE_HEADERS
2003 QHash<int, QByteArray> FileSystemModel::roleNames() const
2004 {
2005     Q_D(const FileSystemModel);
2006     return d->roleNames;
2007 }
2008 #endif
2009 
2010 #if QT_VERSION < 0x050f00
2011 QString FileSystemModel::wildcardToRegularExpression(const QString &pattern)
2012 {
2013     const int wclen = pattern.size();
2014     QString rx;
2015     rx.reserve(wclen + wclen / 16);
2016     int i = 0;
2017     const QChar *wc = pattern.data();
2018 
2019 #ifdef Q_OS_WIN
2020     const QLatin1Char nativePathSeparator('\\');
2021     const QLatin1String starEscape("[^/\\\\]*");
2022     const QLatin1String questionMarkEscape("[^/\\\\]");
2023 #else
2024     const QLatin1Char nativePathSeparator('/');
2025     const QLatin1String starEscape("[^/]*");
2026     const QLatin1String questionMarkEscape("[^/]");
2027 #endif
2028 
2029     while (i < wclen) {
2030         const QChar c = wc[i++];
2031         switch (c.unicode()) {
2032         case '*':
2033             rx += starEscape;
2034             break;
2035         case '?':
2036             rx += questionMarkEscape;
2037             break;
2038         case '\\':
2039 #ifdef Q_OS_WIN
2040         case '/':
2041             rx += QLatin1String("[/\\\\]");
2042             break;
2043 #endif
2044         case '$':
2045         case '(':
2046         case ')':
2047         case '+':
2048         case '.':
2049         case '^':
2050         case '{':
2051         case '|':
2052         case '}':
2053             rx += QLatin1Char('\\');
2054             rx += c;
2055             break;
2056         case '[':
2057             rx += c;
2058             // Support for the [!abc] or [!a-c] syntax
2059             if (i < wclen) {
2060                 if (wc[i] == QLatin1Char('!')) {
2061                     rx += QLatin1Char('^');
2062                     ++i;
2063                 }
2064 
2065                 if (i < wclen && wc[i] == QLatin1Char(']'))
2066                     rx += wc[i++];
2067 
2068                 while (i < wclen && wc[i] != QLatin1Char(']')) {
2069                     // The '/' appearing in a character class invalidates the
2070                     // regular expression parsing. It also concerns '\\' on
2071                     // Windows OS types.
2072                     if (wc[i] == QLatin1Char('/') || wc[i] == nativePathSeparator)
2073                         return rx;
2074                     if (wc[i] == QLatin1Char('\\'))
2075                         rx += QLatin1Char('\\');
2076                     rx += wc[i++];
2077                 }
2078             }
2079             break;
2080         default:
2081             rx += c;
2082             break;
2083         }
2084     }
2085 
2086     return rx;
2087 }
2088 #endif
2089 
2090 /*!
2091     \internal
2092 
2093     Returns \c false if node doesn't pass the filters otherwise true
2094 
2095     QDir::Modified is not supported
2096     QDir::Drives is not supported
2097 */
2098 bool FileSystemModelPrivate::filtersAcceptsNode(const FileSystemNode *node) const
2099 {
2100     // always accept drives
2101     if (node->parent == &root || bypassFilters.contains(node))
2102         return true;
2103 
2104     // If we don't know anything yet don't accept it
2105     if (!node->hasInformation())
2106         return false;
2107 
2108     const bool filterPermissions = ((filters & QDir::PermissionMask)
2109                                    && (filters & QDir::PermissionMask) != QDir::PermissionMask);
2110     const bool hideDirs          = !(filters & (QDir::Dirs | QDir::AllDirs));
2111     const bool hideFiles         = !(filters & QDir::Files);
2112     const bool hideReadable      = !(!filterPermissions || (filters & QDir::Readable));
2113     const bool hideWritable      = !(!filterPermissions || (filters & QDir::Writable));
2114     const bool hideExecutable    = !(!filterPermissions || (filters & QDir::Executable));
2115     const bool hideHidden        = !(filters & QDir::Hidden);
2116     const bool hideSystem        = !(filters & QDir::System);
2117     const bool hideSymlinks      = (filters & QDir::NoSymLinks);
2118     const bool hideDot           = (filters & QDir::NoDot);
2119     const bool hideDotDot        = (filters & QDir::NoDotDot);
2120 
2121     // Note that we match the behavior of entryList and not QFileInfo on this.
2122     bool isDot    = node->fileName == QLatin1String(".");
2123     bool isDotDot = node->fileName == QLatin1String("..");
2124     if (   (hideHidden && !(isDot || isDotDot) && node->isHidden())
2125         || (hideSystem && node->isSystem())
2126         || (hideDirs && node->isDir())
2127         || (hideFiles && node->isFile())
2128         || (hideSymlinks && node->isSymLink())
2129         || (hideReadable && node->isReadable())
2130         || (hideWritable && node->isWritable())
2131         || (hideExecutable && node->isExecutable())
2132         || (hideDot && isDot)
2133         || (hideDotDot && isDotDot))
2134         return false;
2135 
2136     return nameFilterDisables || passNameFilters(node);
2137 }
2138 
2139 /*
2140     \internal
2141 
2142     Returns \c true if node passes the name filters and should be visible.
2143  */
2144 bool FileSystemModelPrivate::passNameFilters(const FileSystemNode *node) const
2145 {
2146 #ifndef QT_NO_REGEXP
2147     if (nameFilters.isEmpty())
2148         return true;
2149 
2150     // Check the name regularexpression filters
2151     if (!(node->isDir() && (filters & QDir::AllDirs))) {
2152         auto reOptions = filters & QDir::CaseSensitive
2153             ? QRegularExpression::NoPatternOption
2154             : QRegularExpression::CaseInsensitiveOption;
2155         for (const auto &nameFilter : nameFilters) {
2156 #if QT_VERSION >= 0x050f00
2157             auto rx = QRegularExpression(
2158                   QRegularExpression::wildcardToRegularExpression(nameFilter),
2159                   reOptions);
2160 #else
2161             auto rx = QRegularExpression(
2162                   FileSystemModel::wildcardToRegularExpression(nameFilter),
2163                   reOptions);
2164 #endif
2165             if (auto match = rx.match(node->fileName); match.hasMatch())
2166                 return true;
2167         }
2168         return false;
2169     }
2170 #endif
2171     return true;
2172 }
2173 /** \endcond */