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