Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qfilesystemmodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "qfilesystemmodel.h"
7#include <qlocale.h>
8#include <qmimedata.h>
9#include <qurl.h>
10#include <qdebug.h>
11#include <QtCore/qcollator.h>
12#if QT_CONFIG(regularexpression)
13# include <QtCore/qregularexpression.h>
14#endif
15
16#include <algorithm>
17
18#ifdef Q_OS_WIN
19# include <QtCore/QVarLengthArray>
20# include <qt_windows.h>
21# include <shlobj.h>
22#endif
23
25
26using namespace Qt::StringLiterals;
27
132{
133 Q_D(const QFileSystemModel);
134 return d->node(index)->fileInfo();
135}
136
172{
173 Q_D(QFileSystemModel);
174
175 const QString path = d->filePath(aindex);
176 const QFileInfo fileInfo(path);
177#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
178 // QTBUG-65683: Remove file system watchers prior to deletion to prevent
179 // failure due to locked files on Windows.
180 const QStringList watchedPaths = d->unwatchPathsAt(aindex);
181#endif // filesystemwatcher && Q_OS_WIN
182 const bool success = (fileInfo.isFile() || fileInfo.isSymLink())
184#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
185 if (!success)
186 d->watchPaths(watchedPaths);
187#endif // filesystemwatcher && Q_OS_WIN
188 return success;
189}
190
198
203 : QAbstractItemModel(dd, parent)
204{
205 Q_D(QFileSystemModel);
206 d->init();
207}
208
213
218{
219 Q_D(const QFileSystemModel);
220 if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
221 return QModelIndex();
222
223 // get the parent node
224 QFileSystemModelPrivate::QFileSystemNode *parentNode = (d->indexValid(parent) ? d->node(parent) :
225 const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&d->root));
226 Q_ASSERT(parentNode);
227
228 // now get the internal pointer for the index
229 const int i = d->translateVisibleLocation(parentNode, row);
230 if (i >= parentNode->visibleChildren.size())
231 return QModelIndex();
232 const QString &childName = parentNode->visibleChildren.at(i);
233 const QFileSystemModelPrivate::QFileSystemNode *indexNode = parentNode->children.value(childName);
234 Q_ASSERT(indexNode);
235
236 return createIndex(row, column, const_cast<QFileSystemModelPrivate::QFileSystemNode*>(indexNode));
237}
238
243{
244 if (row == idx.row() && column < columnCount(idx.parent())) {
245 // cheap sibling operation: just adjust the column:
246 return createIndex(row, column, idx.internalPointer());
247 } else {
248 // for anything else: call the default implementation
249 // (this could probably be optimized, too):
251 }
252}
253
260{
261 Q_D(const QFileSystemModel);
262 QFileSystemModelPrivate::QFileSystemNode *node = d->node(path, false);
263 return d->index(node, column);
264}
265
272{
273 if (!index.isValid())
274 return const_cast<QFileSystemNode*>(&root);
276 Q_ASSERT(indexNode);
277 return indexNode;
278}
279
280#ifdef Q_OS_WIN32
281static QString qt_GetLongPathName(const QString &strShortPath)
282{
283 if (strShortPath.isEmpty()
284 || strShortPath == "."_L1 || strShortPath == ".."_L1)
285 return strShortPath;
286 if (strShortPath.length() == 2 && strShortPath.endsWith(u':'))
287 return strShortPath.toUpper();
288 const QString absPath = QDir(strShortPath).absolutePath();
289 if (absPath.startsWith("//"_L1)
290 || absPath.startsWith("\\\\"_L1)) // unc
291 return QDir::fromNativeSeparators(absPath);
292 if (absPath.startsWith(u'/'))
293 return QString();
294 const QString inputString = "\\\\?\\"_L1 + QDir::toNativeSeparators(absPath);
295 QVarLengthArray<TCHAR, MAX_PATH> buffer(MAX_PATH);
296 DWORD result = ::GetLongPathName((wchar_t*)inputString.utf16(),
297 buffer.data(),
298 buffer.size());
299 if (result > DWORD(buffer.size())) {
300 buffer.resize(result);
301 result = ::GetLongPathName((wchar_t*)inputString.utf16(),
302 buffer.data(),
303 buffer.size());
304 }
305 if (result > 4) {
306 QString longPath = QString::fromWCharArray(buffer.data() + 4); // ignoring prefix
307 longPath[0] = longPath.at(0).toUpper(); // capital drive letters
308 return QDir::fromNativeSeparators(longPath);
309 } else {
310 return QDir::fromNativeSeparators(strShortPath);
311 }
312}
313#endif
314
321{
322 Q_Q(const QFileSystemModel);
323 Q_UNUSED(q);
324 if (path.isEmpty() || path == myComputer() || path.startsWith(u':'))
326
327 // Construct the nodes up to the new root path if they need to be built
329#ifdef Q_OS_WIN32
330 QString longPath = qt_GetLongPathName(path);
331#else
332 QString longPath = path;
333#endif
334 if (longPath == rootDir.path())
336 else
337 absolutePath = QDir(longPath).absolutePath();
338
339 // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const?
340 QStringList pathElements = absolutePath.split(u'/', Qt::SkipEmptyParts);
341 if ((pathElements.isEmpty())
342#if !defined(Q_OS_WIN)
343 && QDir::fromNativeSeparators(longPath) != "/"_L1
344#endif
345 )
347 QModelIndex index = QModelIndex(); // start with "My Computer"
348 QString elementPath;
349 QChar separator = u'/';
350 QString trailingSeparator;
351#if defined(Q_OS_WIN)
352 if (absolutePath.startsWith("//"_L1)) { // UNC path
353 QString host = "\\\\"_L1 + pathElements.constFirst();
355 absolutePath.append(u'/');
356 if (longPath.endsWith(u'/') && !absolutePath.endsWith(u'/'))
357 absolutePath.append(u'/');
358 if (absolutePath.endsWith(u'/'))
359 trailingSeparator = "\\"_L1;
360 int r = 0;
361 auto rootNode = const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
362 auto it = root.children.constFind(host);
363 if (it != root.children.cend()) {
364 host = it.key(); // Normalize case for lookup in visibleLocation()
365 } else {
366 if (pathElements.count() == 1 && !absolutePath.endsWith(u'/'))
367 return rootNode;
368 QFileInfo info(host);
369 if (!info.exists())
370 return rootNode;
372 p->addNode(rootNode, host,info);
373 p->addVisibleFiles(rootNode, QStringList(host));
374 }
375 r = rootNode->visibleLocation(host);
376 r = translateVisibleLocation(rootNode, r);
377 index = q->index(r, 0, QModelIndex());
378 pathElements.pop_front();
379 separator = u'\\';
380 elementPath = host;
381 elementPath.append(separator);
382 } else {
383 if (!pathElements.at(0).contains(u':')) {
384 QString rootPath = QDir(longPath).rootPath();
385 pathElements.prepend(rootPath);
386 }
387 if (pathElements.at(0).endsWith(u'/'))
388 pathElements[0].chop(1);
389 }
390#else
391 // add the "/" item, since it is a valid path element on Unix
392 if (absolutePath[0] == u'/')
393 pathElements.prepend("/"_L1);
394#endif
395
397
398 for (int i = 0; i < pathElements.size(); ++i) {
399 QString element = pathElements.at(i);
400 if (i != 0)
401 elementPath.append(separator);
402 elementPath.append(element);
403 if (i == pathElements.size() - 1)
404 elementPath.append(trailingSeparator);
405#ifdef Q_OS_WIN
406 // On Windows, "filename " and "filename" are equivalent and
407 // "filename . " and "filename" are equivalent
408 // "filename......." and "filename" are equivalent Task #133928
409 // whereas "filename .txt" is still "filename .txt"
410 // If after stripping the characters there is nothing left then we
411 // just return the parent directory as it is assumed that the path
412 // is referring to the parent
413 while (element.endsWith(u'.') || element.endsWith(u' '))
414 element.chop(1);
415 // Only filenames that can't possibly exist will be end up being empty
416 if (element.isEmpty())
417 return parent;
418#endif
419 bool alreadyExisted = parent->children.contains(element);
420
421 // we couldn't find the path element, we create a new node since we
422 // _know_ that the path is valid
423 if (alreadyExisted) {
424 if ((parent->children.size() == 0)
425 || (parent->caseSensitive()
426 && parent->children.value(element)->fileName != element)
427 || (!parent->caseSensitive()
428 && parent->children.value(element)->fileName.toLower() != element.toLower()))
429 alreadyExisted = false;
430 }
431
433 if (!alreadyExisted) {
434 // Someone might call ::index("file://cookie/monster/doesn't/like/veggies"),
435 // a path that doesn't exists, I.E. don't blindly create directories.
436 QFileInfo info(elementPath);
437 if (!info.exists())
440 node = p->addNode(parent, element,info);
441#if QT_CONFIG(filesystemwatcher)
442 node->populate(fileInfoGatherer->getInfo(info));
443#endif
444 } else {
445 node = parent->children.value(element);
446 }
447
448 Q_ASSERT(node);
449 if (!node->isVisible) {
450 // It has been filtered out
451 if (alreadyExisted && node->hasInformation() && !fetch)
453
456 if (!p->bypassFilters.contains(node))
457 p->bypassFilters[node] = 1;
458 QString dir = q->filePath(this->index(parent));
459 if (!node->hasInformation() && fetch) {
460 Fetching f = { std::move(dir), std::move(element), node };
461 p->toFetch.append(std::move(f));
462 p->fetchingTimer.start(0, const_cast<QFileSystemModel*>(q));
463 }
464 }
465 parent = node;
466 }
467
468 return parent;
469}
470
475{
476 Q_D(QFileSystemModel);
477 if (event->timerId() == d->fetchingTimer.timerId()) {
478 d->fetchingTimer.stop();
479#if QT_CONFIG(filesystemwatcher)
480 for (int i = 0; i < d->toFetch.size(); ++i) {
481 const QFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node;
482 if (!node->hasInformation()) {
483 d->fileInfoGatherer->fetchExtendedInformation(d->toFetch.at(i).dir,
484 QStringList(d->toFetch.at(i).file));
485 } else {
486 // qDebug("yah!, you saved a little gerbil soul");
487 }
488 }
489#endif
490 d->toFetch.clear();
491 }
492}
493
499{
500 // This function is for public usage only because it could create a file info
501 Q_D(const QFileSystemModel);
502 if (!index.isValid())
503 return true;
505 if (n->hasInformation())
506 return n->isDir();
507 return fileInfo(index).isDir();
508}
509
514{
515 Q_D(const QFileSystemModel);
516 if (!index.isValid())
517 return 0;
518 return d->node(index)->size();
519}
520
525{
526 Q_D(const QFileSystemModel);
527 if (!index.isValid())
528 return QString();
529 return d->node(index)->type();
530}
531
546
560{
561 Q_D(const QFileSystemModel);
562 if (!index.isValid())
563 return QDateTime();
564 return d->node(index)->lastModified(tz);
565}
566
571{
572 Q_D(const QFileSystemModel);
573 if (!d->indexValid(index))
574 return QModelIndex();
575
577 Q_ASSERT(indexNode != nullptr);
578 QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
579 if (parentNode == nullptr || parentNode == &d->root)
580 return QModelIndex();
581
582 // get the parent's row
583 QFileSystemModelPrivate::QFileSystemNode *grandParentNode = parentNode->parent;
584 Q_ASSERT(grandParentNode->children.contains(parentNode->fileName));
585 int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName));
586 if (visualRow == -1)
587 return QModelIndex();
588 return createIndex(visualRow, 0, parentNode);
589}
590
591/*
592 \internal
593
594 return the index for node
595*/
597{
598 Q_Q(const QFileSystemModel);
599 QFileSystemModelPrivate::QFileSystemNode *parentNode = (node ? node->parent : nullptr);
600 if (node == &root || !parentNode)
601 return QModelIndex();
602
603 // get the parent's row
604 Q_ASSERT(node);
605 if (!node->isVisible)
606 return QModelIndex();
607
608 int visualRow = translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName));
609 return q->createIndex(visualRow, column, const_cast<QFileSystemNode*>(node));
610}
611
616{
617 Q_D(const QFileSystemModel);
618 if (parent.column() > 0)
619 return false;
620
621 if (!parent.isValid()) // drives
622 return true;
623
624 const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
625 Q_ASSERT(indexNode);
626 return (indexNode->isDir());
627}
628
633{
634 Q_D(const QFileSystemModel);
635 if (!d->setRootPath)
636 return false;
637 const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
638 return (!indexNode->populatedChildren);
639}
640
645{
646 Q_D(QFileSystemModel);
647 if (!d->setRootPath)
648 return;
650 if (indexNode->populatedChildren)
651 return;
652 indexNode->populatedChildren = true;
653#if QT_CONFIG(filesystemwatcher)
654 d->fileInfoGatherer->list(filePath(parent));
655#endif
656}
657
662{
663 Q_D(const QFileSystemModel);
664 if (parent.column() > 0)
665 return 0;
666
667 if (!parent.isValid())
668 return d->root.visibleChildren.size();
669
670 const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);
671 return parentNode->visibleChildren.size();
672}
673
678{
679 return (parent.column() > 0) ? 0 : QFileSystemModelPrivate::NumColumns;
680}
681
688{
689#if QT_CONFIG(filesystemwatcher)
690 Q_D(const QFileSystemModel);
691#endif
692 switch (role) {
693 case Qt::DisplayRole:
695#if QT_CONFIG(filesystemwatcher)
697 if (auto *provider = d->fileInfoGatherer->iconProvider())
698 return provider->icon(QAbstractFileIconProvider::Computer);
699 break;
700#endif
701 }
702 return QVariant();
703}
704
709{
710 Q_D(const QFileSystemModel);
711 if (!index.isValid() || index.model() != this)
712 return QVariant();
713
714 switch (role) {
715 case Qt::EditRole:
717 return d->name(index);
719 case Qt::DisplayRole:
720 switch (index.column()) {
721 case QFileSystemModelPrivate::NameColumn: return d->displayName(index);
722 case QFileSystemModelPrivate::SizeColumn: return d->size(index);
723 case QFileSystemModelPrivate::TypeColumn: return d->type(index);
724 case QFileSystemModelPrivate::TimeColumn: return d->time(index);
725 default:
726 qWarning("data: invalid display value column %d", index.column());
727 break;
728 }
729 break;
730 case FilePathRole:
731 return filePath(index);
732 case FileNameRole:
733 return d->name(index);
734 case FileInfoRole:
738 QIcon icon = d->icon(index);
739#if QT_CONFIG(filesystemwatcher)
740 if (icon.isNull()) {
742 if (auto *provider = d->fileInfoGatherer->iconProvider())
743 icon = provider->icon(d->node(index)->isDir() ? P::Folder: P::File);
744 }
745#endif // filesystemwatcher
746 return icon;
747 }
748 break;
752 break;
753 case FilePermissions:
754 int p = permissions(index);
755 return p;
756 }
757
758 return QVariant();
759}
760
765{
766 if (!index.isValid())
767 return QString();
768 const QFileSystemNode *n = node(index);
769 if (n->isDir()) {
770#ifdef Q_OS_MAC
771 return "--"_L1;
772#else
773 return ""_L1;
774#endif
775 // Windows - ""
776 // OS X - "--"
777 // Konqueror - "4 KB"
778 // Nautilus - "9 items" (the number of children)
779 }
780 return size(n->size());
781}
782
784{
785 return QLocale::system().formattedDataSize(bytes);
786}
787
792{
793 if (!index.isValid())
794 return QString();
795#if QT_CONFIG(datestring)
796 return QLocale::system().toString(node(index)->lastModified(QTimeZone::LocalTime), QLocale::ShortFormat);
797#else
799 return QString();
800#endif
801}
802
803/*
804 \internal
805*/
807{
808 if (!index.isValid())
809 return QString();
810 return node(index)->type();
811}
812
817{
818 if (!index.isValid())
819 return QString();
820 QFileSystemNode *dirNode = node(index);
821 if (
822#if QT_CONFIG(filesystemwatcher)
823 fileInfoGatherer->resolveSymlinks() &&
824#endif
825 !resolvedSymLinks.isEmpty() && dirNode->isSymLink(/* ignoreNtfsSymLinks = */ true)) {
827 return resolvedSymLinks.value(fullPath, dirNode->fileName);
828 }
829 return dirNode->fileName;
830}
831
836{
837#if defined(Q_OS_WIN)
838 QFileSystemNode *dirNode = node(index);
839 if (!dirNode->volumeName.isEmpty())
840 return dirNode->volumeName;
841#endif
842 return name(index);
843}
844
849{
850 if (!index.isValid())
851 return QIcon();
852 return node(index)->icon();
853}
854
858bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role)
859{
860 Q_D(QFileSystemModel);
861 if (!idx.isValid()
862 || idx.column() != 0
863 || role != Qt::EditRole
864 || (flags(idx) & Qt::ItemIsEditable) == 0) {
865 return false;
866 }
867
868 QString newName = value.toString();
869 QString oldName = idx.data().toString();
870 if (newName == oldName)
871 return true;
872
873 const QString parentPath = filePath(parent(idx));
874
875 if (newName.isEmpty() || QDir::toNativeSeparators(newName).contains(QDir::separator()))
876 return false;
877
878#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
879 // QTBUG-65683: Remove file system watchers prior to renaming to prevent
880 // failure due to locked files on Windows.
881 const QStringList watchedPaths = d->unwatchPathsAt(idx);
882#endif // filesystemwatcher && Q_OS_WIN
883 if (!QDir(parentPath).rename(oldName, newName)) {
884#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
885 d->watchPaths(watchedPaths);
886#endif
887 return false;
888 } else {
889 /*
890 *After re-naming something we don't want the selection to change*
891 - can't remove rows and later insert
892 - can't quickly remove and insert
893 - index pointer can't change because treeview doesn't use persistent index's
894
895 - if this get any more complicated think of changing it to just
896 use layoutChanged
897 */
898
899 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx);
900 QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
901 int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName);
902
903 parentNode->visibleChildren.removeAt(visibleLocation);
904 std::unique_ptr<QFileSystemModelPrivate::QFileSystemNode> nodeToRename(parentNode->children.take(oldName));
905 nodeToRename->fileName = newName;
906 nodeToRename->parent = parentNode;
907#if QT_CONFIG(filesystemwatcher)
908 nodeToRename->populate(d->fileInfoGatherer->getInfo(QFileInfo(parentPath, newName)));
909#endif
910 nodeToRename->isVisible = true;
911 parentNode->children[newName] = nodeToRename.release();
912 parentNode->visibleChildren.insert(visibleLocation, newName);
913
914 d->delayedSort();
915 emit fileRenamed(parentPath, oldName, newName);
916 }
917 return true;
918}
919
923QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
924{
925 switch (role) {
927 if (section == 0) {
928 // ### TODO oh man this is ugly and doesn't even work all the way!
929 // it is still 2 pixels off
932 return pixmap;
933 }
934 break;
936 return Qt::AlignLeft;
937 }
938
939 if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
940 return QAbstractItemModel::headerData(section, orientation, role);
941
942 QString returnValue;
943 switch (section) {
945 returnValue = tr("Name");
946 break;
948 returnValue = tr("Size");
949 break;
951 returnValue =
952#ifdef Q_OS_MAC
953 tr("Kind", "Match OS X Finder");
954#else
955 tr("Type", "All other platforms");
956#endif
957 break;
958 // Windows - Type
959 // OS X - Kind
960 // Konqueror - File Type
961 // Nautilus - Type
963 returnValue = tr("Date Modified");
964 break;
965 default: return QVariant();
966 }
967 return returnValue;
968}
969
973Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const
974{
975 Q_D(const QFileSystemModel);
976 Qt::ItemFlags flags = QAbstractItemModel::flags(index);
977 if (!index.isValid())
978 return flags;
979
981 if (d->nameFilterDisables && !d->passNameFilters(indexNode)) {
982 flags &= ~Qt::ItemIsEnabled;
983 // ### TODO you shouldn't be able to set this as the current item, task 119433
984 return flags;
985 }
986
988
989 if (!indexNode->isDir())
991 if (d->readOnly)
992 return flags;
993 if ((index.column() == 0) && indexNode->permissions() & QFile::WriteUser) {
995 if (indexNode->isDir())
997 }
998 return flags;
999}
1000
1009
1010
1011/*
1012 \internal
1013 Helper functor used by sort()
1014*/
1016{
1017public:
1018 inline QFileSystemModelSorter(int column) : sortColumn(column)
1019 {
1020 naturalCompare.setNumericMode(true);
1022 }
1023
1026 {
1027 switch (sortColumn) {
1029#ifndef Q_OS_MAC
1030 // place directories before files
1031 bool left = l->isDir();
1032 bool right = r->isDir();
1033 if (left ^ right)
1034 return left;
1035#endif
1036 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1037 }
1039 {
1040 // Directories go first
1041 bool left = l->isDir();
1042 bool right = r->isDir();
1043 if (left ^ right)
1044 return left;
1045
1046 qint64 sizeDifference = l->size() - r->size();
1047 if (sizeDifference == 0)
1048 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1049
1050 return sizeDifference < 0;
1051 }
1053 {
1054 int compare = naturalCompare.compare(l->type(), r->type());
1055 if (compare == 0)
1056 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1057
1058 return compare < 0;
1059 }
1061 {
1063 const QDateTime right = r->lastModified(QTimeZone::UTC);
1064 if (left == right)
1065 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1066
1067 return left < right;
1068 }
1069 }
1070 Q_ASSERT(false);
1071 return false;
1072 }
1073
1079
1080
1081private:
1082 QCollator naturalCompare;
1083 int sortColumn;
1084};
1085
1086/*
1087 \internal
1088
1089 Sort all of the children of parent
1090*/
1092{
1093 Q_Q(QFileSystemModel);
1095 if (indexNode->children.size() == 0)
1096 return;
1097
1098 QList<QFileSystemModelPrivate::QFileSystemNode *> values;
1099
1100 for (auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) {
1101 if (filtersAcceptsNode(iterator.value())) {
1102 values.append(iterator.value());
1103 } else {
1104 iterator.value()->isVisible = false;
1105 }
1106 }
1108 std::sort(values.begin(), values.end(), ms);
1109 // First update the new visible list
1110 indexNode->visibleChildren.clear();
1111 //No more dirty item we reset our internal dirty index
1112 indexNode->dirtyChildrenIndex = -1;
1113 indexNode->visibleChildren.reserve(values.size());
1114 for (QFileSystemNode *node : std::as_const(values)) {
1115 indexNode->visibleChildren.append(node->fileName);
1116 node->isVisible = true;
1117 }
1118
1119 if (!disableRecursiveSort) {
1120 for (int i = 0; i < q->rowCount(parent); ++i) {
1121 const QModelIndex childIndex = q->index(i, 0, parent);
1122 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(childIndex);
1123 //Only do a recursive sort on visible nodes
1124 if (indexNode->isVisible)
1125 sortChildren(column, childIndex);
1126 }
1127 }
1128}
1129
1134{
1135 Q_D(QFileSystemModel);
1136 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
1137 return;
1138
1141 QList<QPair<QFileSystemModelPrivate::QFileSystemNode *, int>> oldNodes;
1142 oldNodes.reserve(oldList.size());
1143 for (const QModelIndex &oldNode : oldList) {
1144 QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldNode), oldNode.column());
1145 oldNodes.append(pair);
1146 }
1147
1148 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
1149 //we sort only from where we are, don't need to sort all the model
1150 d->sortChildren(column, index(rootPath()));
1151 d->sortColumn = column;
1152 d->forceSort = false;
1153 }
1154 d->sortOrder = order;
1155
1156 QModelIndexList newList;
1157 newList.reserve(oldNodes.size());
1158 for (const auto &[node, col]: std::as_const(oldNodes))
1159 newList.append(d->index(node, col));
1160
1161 changePersistentIndexList(oldList, newList);
1163}
1164
1170{
1171 return QStringList("text/uri-list"_L1);
1172}
1173
1183{
1184 QList<QUrl> urls;
1186 for (; it != indexes.end(); ++it)
1187 if ((*it).column() == QFileSystemModelPrivate::NameColumn)
1188 urls << QUrl::fromLocalFile(filePath(*it));
1189 QMimeData *data = new QMimeData();
1190 data->setUrls(urls);
1191 return data;
1192}
1193
1203 int row, int column, const QModelIndex &parent)
1204{
1205 Q_UNUSED(row);
1207 if (!parent.isValid() || isReadOnly())
1208 return false;
1209
1210 bool success = true;
1212
1213 QList<QUrl> urls = data->urls();
1214 QList<QUrl>::const_iterator it = urls.constBegin();
1215
1216 switch (action) {
1217 case Qt::CopyAction:
1218 for (; it != urls.constEnd(); ++it) {
1219 QString path = (*it).toLocalFile();
1220 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
1221 }
1222 break;
1223 case Qt::LinkAction:
1224 for (; it != urls.constEnd(); ++it) {
1225 QString path = (*it).toLocalFile();
1226 success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
1227 }
1228 break;
1229 case Qt::MoveAction:
1230 for (; it != urls.constEnd(); ++it) {
1231 QString path = (*it).toLocalFile();
1232 success = QFile::rename(path, to + QFileInfo(path).fileName()) && success;
1233 }
1234 break;
1235 default:
1236 return false;
1237 }
1238
1239 return success;
1240}
1241
1246{
1248}
1249
1253QHash<int, QByteArray> QFileSystemModel::roleNames() const
1254{
1257 QByteArrayLiteral("fileIcon")); // == Qt::decoration
1260 ret.insert(QFileSystemModel::FilePermissions, QByteArrayLiteral("filePermissions"));
1262 return ret;
1263}
1264
1295{
1296 QFileSystemModel::Options previousOptions = options();
1297 setOptions(previousOptions.setFlag(option, on));
1298}
1299
1309{
1310 return options().testFlag(option);
1311}
1312
1325{
1326 const Options changed = (options ^ QFileSystemModel::options());
1327
1328 if (changed.testFlag(DontResolveSymlinks))
1330
1331#if QT_CONFIG(filesystemwatcher)
1332 Q_D(QFileSystemModel);
1333 if (changed.testFlag(DontWatchForChanges))
1334 d->fileInfoGatherer->setWatching(!options.testFlag(DontWatchForChanges));
1335#endif
1336
1337 if (changed.testFlag(DontUseCustomDirectoryIcons)) {
1338 if (auto provider = iconProvider()) {
1339 QAbstractFileIconProvider::Options providerOptions = provider->options();
1342 provider->setOptions(providerOptions);
1343 } else {
1344 qWarning("Setting QFileSystemModel::DontUseCustomDirectoryIcons has no effect when no provider is used");
1345 }
1346 }
1347}
1348
1349QFileSystemModel::Options QFileSystemModel::options() const
1350{
1351 QFileSystemModel::Options result;
1353#if QT_CONFIG(filesystemwatcher)
1354 Q_D(const QFileSystemModel);
1355 result.setFlag(DontWatchForChanges, !d->fileInfoGatherer->isWatching());
1356#else
1357 result.setFlag(DontWatchForChanges);
1358#endif
1359 if (auto provider = iconProvider()) {
1361 provider->options().testFlag(QAbstractFileIconProvider::DontUseCustomDirectoryIcons));
1362 }
1363 return result;
1364}
1365
1371{
1372 Q_D(const QFileSystemModel);
1373 QString fullPath = d->filePath(index);
1375 if (dirNode->isSymLink()
1376#if QT_CONFIG(filesystemwatcher)
1377 && d->fileInfoGatherer->resolveSymlinks()
1378#endif
1379 && d->resolvedSymLinks.contains(fullPath)
1380 && dirNode->isDir()) {
1381 QFileInfo fullPathInfo(dirNode->fileInfo());
1382 if (!dirNode->hasInformation())
1383 fullPathInfo = QFileInfo(fullPath);
1384 QString canonicalPath = fullPathInfo.canonicalFilePath();
1385 auto *canonicalNode = d->node(fullPathInfo.canonicalFilePath(), false);
1386 QFileInfo resolvedInfo = canonicalNode->fileInfo();
1387 if (!canonicalNode->hasInformation())
1388 resolvedInfo = QFileInfo(canonicalPath);
1389 if (resolvedInfo.exists())
1390 return resolvedInfo.filePath();
1391 }
1392 return fullPath;
1393}
1394
1396{
1397 Q_Q(const QFileSystemModel);
1398 Q_UNUSED(q);
1399 if (!index.isValid())
1400 return QString();
1401 Q_ASSERT(index.model() == q);
1402
1404 QModelIndex idx = index;
1405 while (idx.isValid()) {
1407 if (dirNode)
1408 path.prepend(dirNode->fileName);
1409 idx = idx.parent();
1410 }
1412#if !defined(Q_OS_WIN)
1413 if ((fullPath.size() > 2) && fullPath[0] == u'/' && fullPath[1] == u'/')
1414 fullPath = fullPath.mid(1);
1415#else
1416 if (fullPath.length() == 2 && fullPath.endsWith(u':'))
1417 fullPath.append(u'/');
1418#endif
1419 return fullPath;
1420}
1421
1426{
1427 Q_D(QFileSystemModel);
1428 if (!parent.isValid())
1429 return parent;
1430
1432 if (!dir.mkdir(name))
1433 return QModelIndex();
1435 d->addNode(parentNode, name, QFileInfo());
1436 Q_ASSERT(parentNode->children.contains(name));
1438#if QT_CONFIG(filesystemwatcher)
1439 node->populate(d->fileInfoGatherer->getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name)));
1440#endif
1441 d->addVisibleFiles(parentNode, QStringList(name));
1442 return d->index(node);
1443}
1444
1448QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index) const
1449{
1450 Q_D(const QFileSystemModel);
1451 return d->node(index)->permissions();
1452}
1453
1468{
1469 Q_D(QFileSystemModel);
1470#ifdef Q_OS_WIN
1471#ifdef Q_OS_WIN32
1472 QString longNewPath = qt_GetLongPathName(newPath);
1473#else
1474 QString longNewPath = QDir::fromNativeSeparators(newPath);
1475#endif
1476#else
1477 QString longNewPath = newPath;
1478#endif
1479 //we remove .. and . from the given path if exist
1480 if (!newPath.isEmpty())
1481 longNewPath = QDir::cleanPath(longNewPath);
1482
1483 d->setRootPath = true;
1484
1485 //user don't ask for the root path ("") but the conversion failed
1486 if (!newPath.isEmpty() && longNewPath.isEmpty())
1487 return d->index(rootPath());
1488
1489 if (d->rootDir.path() == longNewPath)
1490 return d->index(rootPath());
1491
1492 auto node = d->node(longNewPath);
1493 QFileInfo newPathInfo;
1494 if (node && node->hasInformation())
1495 newPathInfo = node->fileInfo();
1496 else
1497 newPathInfo = QFileInfo(longNewPath);
1498
1499 bool showDrives = (longNewPath.isEmpty() || longNewPath == QFileSystemModelPrivate::myComputer());
1500 if (!showDrives && !newPathInfo.exists())
1501 return d->index(rootPath());
1502
1503 //We remove the watcher on the previous path
1504 if (!rootPath().isEmpty() && rootPath() != "."_L1) {
1505 //This remove the watcher for the old rootPath
1506#if QT_CONFIG(filesystemwatcher)
1507 d->fileInfoGatherer->removePath(rootPath());
1508#endif
1509 //This line "marks" the node as dirty, so the next fetchMore
1510 //call on the path will ask the gatherer to install a watcher again
1511 //But it doesn't re-fetch everything
1512 d->node(rootPath())->populatedChildren = false;
1513 }
1514
1515 // We have a new valid root path
1516 d->rootDir = QDir(longNewPath);
1517 QModelIndex newRootIndex;
1518 if (showDrives) {
1519 // otherwise dir will become '.'
1520 d->rootDir.setPath(""_L1);
1521 } else {
1522 newRootIndex = d->index(d->rootDir.path());
1523 }
1524 fetchMore(newRootIndex);
1525 emit rootPathChanged(longNewPath);
1526 d->forceSort = true;
1527 d->delayedSort();
1528 return newRootIndex;
1529}
1530
1537{
1538 Q_D(const QFileSystemModel);
1539 return d->rootDir.path();
1540}
1541
1548{
1549 Q_D(const QFileSystemModel);
1550 QDir dir(d->rootDir);
1551 dir.setNameFilters(nameFilters());
1552 dir.setFilter(filter());
1553 return dir;
1554}
1555
1560{
1561 Q_D(QFileSystemModel);
1562#if QT_CONFIG(filesystemwatcher)
1563 d->fileInfoGatherer->setIconProvider(provider);
1564#endif
1565 d->root.updateIcon(provider, QString());
1566}
1567
1572{
1573#if QT_CONFIG(filesystemwatcher)
1574 Q_D(const QFileSystemModel);
1575 return d->fileInfoGatherer->iconProvider();
1576#else
1577 return nullptr;
1578#endif
1579}
1580
1590{
1591 Q_D(QFileSystemModel);
1592 if (d->filters == filters)
1593 return;
1594 const bool changingCaseSensitivity =
1595 filters.testFlag(QDir::CaseSensitive) != d->filters.testFlag(QDir::CaseSensitive);
1596 d->filters = filters;
1597 if (changingCaseSensitivity)
1598 d->rebuildNameFilterRegexps();
1599 d->forceSort = true;
1600 d->delayedSort();
1601}
1602
1611QDir::Filters QFileSystemModel::filter() const
1612{
1613 Q_D(const QFileSystemModel);
1614 return d->filters;
1615}
1616
1628{
1629#if QT_CONFIG(filesystemwatcher)
1630 Q_D(QFileSystemModel);
1631 d->fileInfoGatherer->setResolveSymlinks(enable);
1632#else
1634#endif
1635}
1636
1638{
1639#if QT_CONFIG(filesystemwatcher)
1640 Q_D(const QFileSystemModel);
1641 return d->fileInfoGatherer->resolveSymlinks();
1642#else
1643 return false;
1644#endif
1645}
1646
1657{
1658 Q_D(QFileSystemModel);
1659 d->readOnly = enable;
1660}
1661
1663{
1664 Q_D(const QFileSystemModel);
1665 return d->readOnly;
1666}
1667
1675{
1676 Q_D(QFileSystemModel);
1677 if (d->nameFilterDisables == enable)
1678 return;
1679 d->nameFilterDisables = enable;
1680 d->forceSort = true;
1681 d->delayedSort();
1682}
1683
1685{
1686 Q_D(const QFileSystemModel);
1687 return d->nameFilterDisables;
1688}
1689
1694{
1695#if QT_CONFIG(regularexpression)
1696 Q_D(QFileSystemModel);
1697
1698 if (!d->bypassFilters.isEmpty()) {
1699 // update the bypass filter to only bypass the stuff that must be kept around
1700 d->bypassFilters.clear();
1701 // We guarantee that rootPath will stick around
1703 const QModelIndexList persistentList = persistentIndexList();
1704 for (const auto &persistentIndex : persistentList) {
1705 QFileSystemModelPrivate::QFileSystemNode *node = d->node(persistentIndex);
1706 while (node) {
1707 if (d->bypassFilters.contains(node))
1708 break;
1709 if (node->isDir())
1710 d->bypassFilters[node] = true;
1711 node = node->parent;
1712 }
1713 }
1714 }
1715
1716 d->nameFilters = filters;
1717 d->rebuildNameFilterRegexps();
1718 d->forceSort = true;
1719 d->delayedSort();
1720#else
1722#endif
1723}
1724
1729{
1730#if QT_CONFIG(regularexpression)
1731 Q_D(const QFileSystemModel);
1732 return d->nameFilters;
1733#else
1734 return QStringList();
1735#endif
1736}
1737
1742{
1743#if QT_CONFIG(filesystemwatcher)
1744 Q_D(QFileSystemModel);
1745 if (event->type() == QEvent::LanguageChange) {
1746 d->root.retranslateStrings(d->fileInfoGatherer->iconProvider(), QString());
1747 return true;
1748 }
1749#endif
1751}
1752
1754{
1755 QString path = filePath(aindex);
1756 const bool success = QDir().rmdir(path);
1757#if QT_CONFIG(filesystemwatcher)
1758 if (success) {
1759 QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func());
1760 d->fileInfoGatherer->removePath(path);
1761 }
1762#endif
1763 return success;
1764}
1765
1773{
1775 if (parentNode->children.size() == 0)
1776 return;
1777 QStringList toRemove;
1778 QStringList newFiles = files;
1779 std::sort(newFiles.begin(), newFiles.end());
1780 for (auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) {
1781 QStringList::iterator iterator = std::lower_bound(newFiles.begin(), newFiles.end(), i.value()->fileName);
1782 if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator))
1783 toRemove.append(i.value()->fileName);
1784 }
1785 for (int i = 0 ; i < toRemove.size() ; ++i )
1786 removeNode(parentNode, toRemove[i]);
1787}
1788
1789#if defined(Q_OS_WIN)
1790static QString volumeName(const QString &path)
1791{
1792 IShellItem *item = nullptr;
1793 const QString native = QDir::toNativeSeparators(path);
1794 HRESULT hr = SHCreateItemFromParsingName(reinterpret_cast<const wchar_t *>(native.utf16()),
1795 nullptr, IID_IShellItem,
1796 reinterpret_cast<void **>(&item));
1797 if (FAILED(hr))
1798 return QString();
1799 LPWSTR name = nullptr;
1800 hr = item->GetDisplayName(SIGDN_NORMALDISPLAY, &name);
1801 if (FAILED(hr))
1802 return QString();
1804 CoTaskMemFree(name);
1805 item->Release();
1806 return result;
1807}
1808#endif // Q_OS_WIN
1809
1818{
1819 // In the common case, itemLocation == count() so check there first
1821#if QT_CONFIG(filesystemwatcher)
1822 node->populate(info);
1823#else
1824 Q_UNUSED(info);
1825#endif
1826#if defined(Q_OS_WIN)
1827 //The parentNode is "" so we are listing the drives
1828 if (parentNode->fileName.isEmpty())
1829 node->volumeName = volumeName(fileName);
1830#endif
1831 Q_ASSERT(!parentNode->children.contains(fileName));
1832 parentNode->children.insert(fileName, node);
1833 return node;
1834}
1835
1845{
1846 Q_Q(QFileSystemModel);
1847 QModelIndex parent = index(parentNode);
1848 bool indexHidden = isHiddenByFilter(parentNode, parent);
1849
1850 int vLocation = parentNode->visibleLocation(name);
1851 if (vLocation >= 0 && !indexHidden)
1852 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1853 translateVisibleLocation(parentNode, vLocation));
1854 QFileSystemNode * node = parentNode->children.take(name);
1855 delete node;
1856 // cleanup sort files after removing rather then re-sorting which is O(n)
1857 if (vLocation >= 0)
1858 parentNode->visibleChildren.removeAt(vLocation);
1859 if (vLocation >= 0 && !indexHidden)
1860 q->endRemoveRows();
1861}
1862
1872{
1873 Q_Q(QFileSystemModel);
1874 QModelIndex parent = index(parentNode);
1875 bool indexHidden = isHiddenByFilter(parentNode, parent);
1876 if (!indexHidden) {
1877 q->beginInsertRows(parent, parentNode->visibleChildren.size() , parentNode->visibleChildren.size() + newFiles.size() - 1);
1878 }
1879
1880 if (parentNode->dirtyChildrenIndex == -1)
1881 parentNode->dirtyChildrenIndex = parentNode->visibleChildren.size();
1882
1883 for (const auto &newFile : newFiles) {
1884 parentNode->visibleChildren.append(newFile);
1885 parentNode->children.value(newFile)->isVisible = true;
1886 }
1887 if (!indexHidden)
1888 q->endInsertRows();
1889}
1890
1899{
1900 Q_Q(QFileSystemModel);
1901 if (vLocation == -1)
1902 return;
1903 QModelIndex parent = index(parentNode);
1904 bool indexHidden = isHiddenByFilter(parentNode, parent);
1905 if (!indexHidden)
1906 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1907 translateVisibleLocation(parentNode, vLocation));
1908 parentNode->children.value(parentNode->visibleChildren.at(vLocation))->isVisible = false;
1909 parentNode->visibleChildren.removeAt(vLocation);
1910 if (!indexHidden)
1911 q->endRemoveRows();
1912}
1913
1921 const QList<std::pair<QString, QFileInfo>> &updates)
1922{
1923#if QT_CONFIG(filesystemwatcher)
1924 Q_Q(QFileSystemModel);
1925 QList<QString> rowsToUpdate;
1926 QStringList newFiles;
1928 QModelIndex parentIndex = index(parentNode);
1929 for (const auto &update : updates) {
1930 QString fileName = update.first;
1931 Q_ASSERT(!fileName.isEmpty());
1932 QExtendedInformation info = fileInfoGatherer->getInfo(update.second);
1933 bool previouslyHere = parentNode->children.contains(fileName);
1934 if (!previouslyHere) {
1935 addNode(parentNode, fileName, info.fileInfo());
1936 }
1938 bool isCaseSensitive = parentNode->caseSensitive();
1939 if (isCaseSensitive) {
1940 if (node->fileName != fileName)
1941 continue;
1942 } else {
1944 continue;
1945 }
1946 if (isCaseSensitive) {
1948 } else {
1950 }
1951
1952 if (*node != info ) {
1953 node->populate(info);
1954 bypassFilters.remove(node);
1955 // brand new information.
1956 if (filtersAcceptsNode(node)) {
1957 if (!node->isVisible) {
1958 newFiles.append(fileName);
1959 } else {
1960 rowsToUpdate.append(fileName);
1961 }
1962 } else {
1963 if (node->isVisible) {
1964 int visibleLocation = parentNode->visibleLocation(fileName);
1965 removeVisibleFile(parentNode, visibleLocation);
1966 } else {
1967 // The file is not visible, don't do anything
1968 }
1969 }
1970 }
1971 }
1972
1973 // bundle up all of the changed signals into as few as possible.
1974 std::sort(rowsToUpdate.begin(), rowsToUpdate.end());
1975 QString min;
1976 QString max;
1977 for (const QString &value : std::as_const(rowsToUpdate)) {
1978 //##TODO is there a way to bundle signals with QString as the content of the list?
1979 /*if (min.isEmpty()) {
1980 min = value;
1981 if (i != rowsToUpdate.count() - 1)
1982 continue;
1983 }
1984 if (i != rowsToUpdate.count() - 1) {
1985 if ((value == min + 1 && max.isEmpty()) || value == max + 1) {
1986 max = value;
1987 continue;
1988 }
1989 }*/
1990 max = value;
1991 min = value;
1992 int visibleMin = parentNode->visibleLocation(min);
1993 int visibleMax = parentNode->visibleLocation(max);
1994 if (visibleMin >= 0
1995 && visibleMin < parentNode->visibleChildren.size()
1996 && parentNode->visibleChildren.at(visibleMin) == min
1997 && visibleMax >= 0) {
1998 // don't use NumColumns here, a subclass might override columnCount
1999 const int lastColumn = q->columnCount(parentIndex) - 1;
2000 const QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMin),
2002 const QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMax),
2003 lastColumn, parentIndex);
2004 // We document that emitting dataChanged with indexes that don't have the
2005 // same parent is undefined behavior.
2006 Q_ASSERT(bottom.parent() == top.parent());
2007 emit q->dataChanged(top, bottom);
2008 }
2009
2010 /*min = QString();
2011 max = QString();*/
2012 }
2013
2014 if (newFiles.size() > 0) {
2015 addVisibleFiles(parentNode, newFiles);
2016 }
2017
2018 if (newFiles.size() > 0 || (sortColumn != 0 && rowsToUpdate.size() > 0)) {
2019 forceSort = true;
2020 delayedSort();
2021 }
2022#else
2023 Q_UNUSED(path);
2024 Q_UNUSED(updates);
2025#endif // filesystemwatcher
2026}
2027
2035
2036#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
2037// Remove file system watchers at/below the index and return a list of previously
2038// watched files. This should be called prior to operations like rename/remove
2039// which might fail due to watchers on platforms like Windows. The watchers
2040// should be restored on failure.
2041QStringList QFileSystemModelPrivate::unwatchPathsAt(const QModelIndex &index)
2042{
2044 if (indexNode == nullptr)
2045 return QStringList();
2046 const Qt::CaseSensitivity caseSensitivity = indexNode->caseSensitive()
2048 const QString path = indexNode->fileInfo().absoluteFilePath();
2049
2051 const auto filter = [path, caseSensitivity] (const QString &watchedPath)
2052 {
2053 const int pathSize = path.size();
2054 if (pathSize == watchedPath.size()) {
2055 return path.compare(watchedPath, caseSensitivity) == 0;
2056 } else if (watchedPath.size() > pathSize) {
2057 return watchedPath.at(pathSize) == u'/'
2058 && watchedPath.startsWith(path, caseSensitivity);
2059 }
2060 return false;
2061 };
2062
2063 const QStringList &watchedFiles = fileInfoGatherer->watchedFiles();
2064 std::copy_if(watchedFiles.cbegin(), watchedFiles.cend(),
2065 std::back_inserter(result), filter);
2066
2067 const QStringList &watchedDirectories = fileInfoGatherer->watchedDirectories();
2068 std::copy_if(watchedDirectories.cbegin(), watchedDirectories.cend(),
2069 std::back_inserter(result), filter);
2070
2071 fileInfoGatherer->unwatchPaths(result);
2072 return result;
2073}
2074#endif // filesystemwatcher && Q_OS_WIN
2075
2077#if QT_CONFIG(filesystemwatcher)
2078 : fileInfoGatherer(new QFileInfoGatherer)
2079#endif // filesystemwatcher
2080{
2081}
2082
2084{
2085#if QT_CONFIG(filesystemwatcher)
2086 fileInfoGatherer->requestAbort();
2087 if (!fileInfoGatherer->wait(1000)) {
2088 // If the thread hangs, perhaps because the network was disconnected
2089 // while the gatherer was stat'ing a remote file, then don't block
2090 // shutting down the model (which might block a file dialog and the
2091 // main thread). Schedule the gatherer for later deletion; it's
2092 // destructor will wait for the thread to finish.
2093 auto *rawGatherer = fileInfoGatherer.release();
2094 rawGatherer->deleteLater();
2095 }
2096#endif // filesystemwatcher
2097}
2098
2103{
2104 Q_Q(QFileSystemModel);
2105
2107
2108 qRegisterMetaType<QList<std::pair<QString, QFileInfo>>>();
2109#if QT_CONFIG(filesystemwatcher)
2112 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::updates,
2116 q->connect(fileInfoGatherer.get(), &QFileInfoGatherer::directoryLoaded,
2118#endif // filesystemwatcher
2122}
2123
2133{
2134 // When the model is set to only show files, then a node representing a dir
2135 // should be hidden regardless of bypassFilters.
2136 // QTBUG-74471
2137 const bool hideDirs = (filters & (QDir::Dirs | QDir::AllDirs)) == 0;
2138 const bool shouldHideDirNode = hideDirs && node->isDir();
2139
2140 // always accept drives
2141 if (node->parent == &root || (!shouldHideDirNode && bypassFilters.contains(node)))
2142 return true;
2143
2144 // If we don't know anything yet don't accept it
2145 if (!node->hasInformation())
2146 return false;
2147
2148 const bool filterPermissions = ((filters & QDir::PermissionMask)
2150 const bool hideFiles = !(filters & QDir::Files);
2151 const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
2152 const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
2153 const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
2154 const bool hideHidden = !(filters & QDir::Hidden);
2155 const bool hideSystem = !(filters & QDir::System);
2156 const bool hideSymlinks = (filters & QDir::NoSymLinks);
2157 const bool hideDot = (filters & QDir::NoDot);
2158 const bool hideDotDot = (filters & QDir::NoDotDot);
2159
2160 // Note that we match the behavior of entryList and not QFileInfo on this.
2161 bool isDot = (node->fileName == "."_L1);
2162 bool isDotDot = (node->fileName == ".."_L1);
2163 if ( (hideHidden && !(isDot || isDotDot) && node->isHidden())
2164 || (hideSystem && node->isSystem())
2165 || (hideDirs && node->isDir())
2166 || (hideFiles && node->isFile())
2167 || (hideSymlinks && node->isSymLink())
2168 || (hideReadable && node->isReadable())
2169 || (hideWritable && node->isWritable())
2170 || (hideExecutable && node->isExecutable())
2171 || (hideDot && isDot)
2172 || (hideDotDot && isDotDot))
2173 return false;
2174
2176}
2177
2178/*
2179 \internal
2180
2181 Returns \c true if node passes the name filters and should be visible.
2182 */
2184{
2185#if QT_CONFIG(regularexpression)
2186 if (nameFilters.isEmpty())
2187 return true;
2188
2189 // Check the name regularexpression filters
2190 if (!(node->isDir() && (filters & QDir::AllDirs))) {
2191 const auto matchesNodeFileName = [node](const QRegularExpression &re)
2192 {
2193 return node->fileName.contains(re);
2194 };
2195 return std::any_of(nameFiltersRegexps.begin(),
2196 nameFiltersRegexps.end(),
2197 matchesNodeFileName);
2198 }
2199#else
2200 Q_UNUSED(node);
2201#endif
2202 return true;
2203}
2204
2205#if QT_CONFIG(regularexpression)
2206void QFileSystemModelPrivate::rebuildNameFilterRegexps()
2207{
2208 nameFiltersRegexps.clear();
2209 nameFiltersRegexps.reserve(nameFilters.size());
2210 const auto cs = (filters & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
2211 const auto convertWildcardToRegexp = [cs](const QString &nameFilter)
2212 {
2213 return QRegularExpression::fromWildcard(nameFilter, cs);
2214 };
2215 std::transform(nameFilters.constBegin(),
2216 nameFilters.constEnd(),
2217 std::back_inserter(nameFiltersRegexps),
2218 convertWildcardToRegexp);
2219}
2220#endif
2221
2223
2224#include "moc_qfilesystemmodel.cpp"
virtual Q_INVOKABLE QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
Returns the data for the given role and section in the header with the specified orientation.
virtual Q_INVOKABLE Qt::ItemFlags flags(const QModelIndex &index) const
Returns the item flags for the given index.
QModelIndexList persistentIndexList() const
Q_INVOKABLE Qt::SortOrder order
void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to)
void layoutAboutToBeChanged(const QList< QPersistentModelIndex > &parents=QList< QPersistentModelIndex >(), QAbstractItemModel::LayoutChangeHint hint=QAbstractItemModel::NoLayoutChangeHint)
void layoutChanged(const QList< QPersistentModelIndex > &parents=QList< QPersistentModelIndex >(), QAbstractItemModel::LayoutChangeHint hint=QAbstractItemModel::NoLayoutChangeHint)
virtual Q_INVOKABLE QModelIndex sibling(int row, int column, const QModelIndex &idx) const
Returns the sibling at row and column for the item at index, or an invalid QModelIndex if there is no...
virtual QHash< int, QByteArray > roleNames() const
QModelIndex createIndex(int row, int column, const void *data=nullptr) const
Creates a model index for the given row and column with the internal pointer ptr.
\inmodule QtCore
\inmodule QtCore
Definition qcollator.h:44
void setNumericMode(bool on)
Enables numeric sorting mode when on is true.
void setCaseSensitivity(Qt::CaseSensitivity cs)
Sets the case-sensitivity of the collator to cs.
int compare(const QString &s1, const QString &s2) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qcollator.h:70
\inmodule QtCore\reentrant
Definition qdatetime.h:283
\inmodule QtCore
Definition qdir.h:20
bool removeRecursively()
Definition qdir.cpp:1640
static QString fromNativeSeparators(const QString &pathName)
Definition qdir.cpp:962
QString path() const
Returns the path.
Definition qdir.cpp:653
static QChar separator()
Returns the native directory separator: "/" under Unix and "\\" under Windows.
Definition qdir.h:209
QString absolutePath() const
Returns the absolute path (a path that starts with "/" or with a drive specification),...
Definition qdir.cpp:667
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2398
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
static QString rootPath()
Returns the absolute path of the root directory.
Definition qdir.cpp:2159
bool rmdir(const QString &dirName) const
Removes the directory specified by dirName.
Definition qdir.cpp:1550
@ Executable
Definition qdir.h:31
@ CaseSensitive
Definition qdir.h:41
@ Files
Definition qdir.h:23
@ PermissionMask
Definition qdir.h:32
@ Hidden
Definition qdir.h:35
@ AllDirs
Definition qdir.h:40
@ NoSymLinks
Definition qdir.h:25
@ NoDotDot
Definition qdir.h:43
@ Readable
Definition qdir.h:29
@ Writable
Definition qdir.h:30
@ System
Definition qdir.h:36
@ NoDot
Definition qdir.h:42
@ Dirs
Definition qdir.h:22
\inmodule QtCore
Definition qcoreevent.h:45
@ LanguageChange
Definition qcoreevent.h:123
void newListOfFiles(const QString &directory, const QStringList &listOfFiles) const
void updates(const QString &directory, const QList< std::pair< QString, QFileInfo > > &updates)
void nameResolved(const QString &fileName, const QString &resolvedName) const
void directoryLoaded(const QString &path)
bool isSymLink() const
bool isFile() const
Returns true if this object points to a file or to a symbolic link to a file.
bool isDir() const
Returns true if this object points to a directory or to a symbolic link to a directory.
void populate(const QExtendedInformation &fileInfo)
QDateTime lastModified(const QTimeZone &tz) const
QHash< QFileSystemModelNodePathKey, QFileSystemNode * > children
bool isSymLink(bool ignoreNtfsSymLinks=false) const
int visibleLocation(const QString &childName)
bool passNameFilters(const QFileSystemNode *node) const
QHash< const QFileSystemNode *, bool > bypassFilters
QHash< QString, QString > resolvedSymLinks
QString name(const QModelIndex &index) const
QString filePath(const QModelIndex &index) const
QModelIndex index(const QString &path, int column=0)
QFileSystemNode * addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo &info)
QString type(const QModelIndex &index) const
QString time(const QModelIndex &index) const
QString size(const QModelIndex &index) const
bool isHiddenByFilter(QFileSystemNode *indexNode, const QModelIndex &index) const
void resolvedName(const QString &fileName, const QString &resolvedName)
QIcon icon(const QModelIndex &index) const
void directoryChanged(const QString &directory, const QStringList &list)
bool filtersAcceptsNode(const QFileSystemNode *node) const
QFileSystemNode * node(const QModelIndex &index) const
void sortChildren(int column, const QModelIndex &parent)
void removeVisibleFile(QFileSystemNode *parentNode, int visibleLocation)
void removeNode(QFileSystemNode *parentNode, const QString &name)
int translateVisibleLocation(QFileSystemNode *parent, int row) const
void fileSystemChanged(const QString &path, const QList< std::pair< QString, QFileInfo > > &)
void addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles)
QString displayName(const QModelIndex &index) const
bool compareNodes(const QFileSystemModelPrivate::QFileSystemNode *l, const QFileSystemModelPrivate::QFileSystemNode *r) const
bool operator()(const QFileSystemModelPrivate::QFileSystemNode *l, const QFileSystemModelPrivate::QFileSystemNode *r) const
The QFileSystemModel class provides a data model for the local filesystem.
QModelIndex mkdir(const QModelIndex &parent, const QString &name)
Create a directory with the name in the parent model index.
QModelIndex setRootPath(const QString &path)
Sets the directory that is being watched by the model to newPath by installing a \l{QFileSystemWatche...
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
\reimp
QDir rootDirectory() const
The currently set directory.
void setOptions(Options options)
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
Handles the data supplied by a drag and drop operation that ended with the given action over the row ...
bool nameFilterDisables
Whether files that don't pass the name filter are hidden or disabled.
void setNameFilterDisables(bool enable)
void fileRenamed(const QString &path, const QString &oldName, const QString &newName)
This signal is emitted whenever a file with the oldName is successfully renamed to newName.
bool canFetchMore(const QModelIndex &parent) const override
\reimp
bool rmdir(const QModelIndex &index)
Removes the directory corresponding to the model item index in the file system model and {deletes the...
bool testOption(Option option) const
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
\reimp
void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override
\reimp
void rootPathChanged(const QString &newPath)
This signal is emitted whenever the root path has been changed to a newPath.
QString rootPath() const
The currently set root path.
void setOption(Option option, bool on=true)
QString type(const QModelIndex &index) const
Returns the type of file index such as "Directory" or "JPEG file".
QFileInfo fileInfo(const QModelIndex &index) const
Returns the QFileInfo for the item stored in the model under the given index.
QFile::Permissions permissions(const QModelIndex &index) const
Returns the complete OR-ed together combination of QFile::Permission for the index.
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
~QFileSystemModel()
Destroys this file system model.
QString filePath(const QModelIndex &index) const
Returns the path of the item stored in the model under the index given.
QModelIndex sibling(int row, int column, const QModelIndex &idx) const override
\reimp
bool event(QEvent *event) override
\reimp
qint64 size(const QModelIndex &index) const
Returns the size in bytes of index.
QFileSystemModel(QObject *parent=nullptr)
Constructs a file system model with the given parent.
QStringList nameFilters() const
Returns a list of filters applied to the names in the model.
Qt::DropActions supportedDropActions() const override
\reimp
void setIconProvider(QAbstractFileIconProvider *provider)
Sets the provider of file icons for the directory model.
QVariant myComputer(int role=Qt::DisplayRole) const
Returns the data stored under the given role for the item "My Computer".
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
\reimp
void fetchMore(const QModelIndex &parent) override
\reimp
bool resolveSymlinks
Whether the directory model should resolve symbolic links.
void directoryLoaded(const QString &path)
void setReadOnly(bool enable)
QDateTime lastModified(const QModelIndex &index) const
Returns the date and time (in local time) when index was last modified.
void setNameFilters(const QStringList &filters)
Sets the name filters to apply against the existing files.
QStringList mimeTypes() const override
Returns a list of MIME types that can be used to describe a list of items in the model.
QHash< int, QByteArray > roleNames() const override
\reimp
bool hasChildren(const QModelIndex &parent=QModelIndex()) const override
\reimp
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
\reimp
QAbstractFileIconProvider * iconProvider() const
Returns the file icon provider for this directory model.
QDir::Filters filter() const
Returns the filter specified for the directory model.
void setResolveSymlinks(bool enable)
void setFilter(QDir::Filters filters)
Sets the directory model's filter to that specified by filters.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
\reimp
QMimeData * mimeData(const QModelIndexList &indexes) const override
Returns an object that contains a serialized description of the specified indexes.
void timerEvent(QTimerEvent *event) override
\reimp
Options options
the various options that affect the model
bool remove(const QModelIndex &index)
Removes the model item index from the file system model and {deletes the corresponding file from the ...
Qt::ItemFlags flags(const QModelIndex &index) const override
\reimp
int rowCount(const QModelIndex &parent=QModelIndex()) const override
\reimp
bool isDir(const QModelIndex &index) const
Returns true if the model item index represents a directory; otherwise returns false.
bool link(const QString &newName)
Creates a link named linkName that points to the file currently specified by fileName().
Definition qfile.cpp:720
bool copy(const QString &newName)
Copies the file named fileName() to newName.
Definition qfile.cpp:765
bool remove()
Removes the file specified by fileName().
Definition qfile.cpp:419
bool rename(const QString &newName)
Renames the file currently specified by fileName() to newName.
Definition qfile.cpp:554
T value(const Key &key) const noexcept
Definition qhash.h:1054
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
Definition qhash.h:928
The QIcon class provides scalable icons in different modes and states.
Definition qicon.h:20
bool isNull() const
Returns true if the icon is empty; otherwise returns false.
Definition qicon.cpp:1019
\inmodule QtGui
Definition qimage.h:37
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
qsizetype size() const noexcept
Definition qlist.h:397
void removeAt(qsizetype i)
Definition qlist.h:590
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:488
iterator end()
Definition qlist.h:626
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
T value(qsizetype i) const
Definition qlist.h:664
iterator begin()
Definition qlist.h:625
void reserve(qsizetype size)
Definition qlist.h:753
void append(parameter_type t)
Definition qlist.h:458
@ ShortFormat
Definition qlocale.h:875
static QLocale system()
Returns a QLocale object initialized to the system locale.
Definition qlocale.cpp:2862
\inmodule QtCore
Definition qmimedata.h:16
\inmodule QtCore
QVariant data(int role=Qt::DisplayRole) const
Returns the data for the given role for the item referred to by the index.
constexpr int row() const noexcept
Returns the row this model index refers to.
QModelIndex parent() const
Returns the parent of the model index, or QModelIndex() if it has no parent.
constexpr int column() const noexcept
Returns the column this model index refers to.
void * internalPointer() const noexcept
Returns a {void} {*} pointer used by the model to associate the index with the internal data structur...
constexpr bool isValid() const noexcept
Returns {true} if this model index is valid; otherwise returns {false}.
QObject * parent
Definition qobject.h:73
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
\inmodule QtCore
Definition qobject.h:103
const QObjectList & children() const
Returns a list of child objects.
Definition qobject.h:201
virtual bool event(QEvent *event)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition qobject.cpp:1389
\inmodule QtCore \reentrant
static QRegularExpression fromWildcard(QStringView pattern, Qt::CaseSensitivity cs=Qt::CaseInsensitive, WildcardConversionOptions options=DefaultWildcardConversion)
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
QString first(qsizetype n) const &
Definition qstring.h:390
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5506
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6664
QString toLower() const &
Definition qstring.h:435
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1369
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
QString & append(QChar c)
Definition qstring.cpp:3252
QString toUpper() const &
Definition qstring.h:439
\inmodule QtCore
Definition qtimezone.h:26
\inmodule QtCore
Definition qcoreevent.h:366
void setSingleShot(bool singleShot)
Definition qtimer.cpp:552
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3368
\inmodule QtCore
Definition qvariant.h:65
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
QSet< QString >::iterator it
Combined button and popup list for selecting options.
Definition qcompare.h:63
@ AlignVCenter
Definition qnamespace.h:155
@ AlignTrailing
Definition qnamespace.h:147
@ AlignLeft
Definition qnamespace.h:144
Orientation
Definition qnamespace.h:98
@ Horizontal
Definition qnamespace.h:99
@ transparent
Definition qnamespace.h:47
@ TextAlignmentRole
@ DecorationRole
@ EditRole
@ DisplayRole
SortOrder
Definition qnamespace.h:121
CaseSensitivity
@ CaseInsensitive
@ CaseSensitive
DropAction
@ CopyAction
@ MoveAction
@ LinkAction
@ QueuedConnection
@ SkipEmptyParts
Definition qnamespace.h:128
@ ItemNeverHasChildren
@ ItemIsEditable
@ ItemIsDragEnabled
@ ItemIsDropEnabled
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
#define Q_FALLTHROUGH()
QList< QString > QStringList
Constructs a string list that contains the given string, str.
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
return ret
GLenum GLsizei GLsizei GLint * values
[15]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLdouble GLdouble GLdouble GLdouble top
GLdouble GLdouble right
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum GLuint buffer
GLint left
GLint GLint bottom
GLbitfield flags
GLboolean enable
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLuint name
GLfloat n
GLenum GLenum GLsizei void GLsizei void * column
GLbyte GLbyte tz
struct _cl_event * event
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLuint GLenum option
GLfixed GLfixed GLint GLint order
static QString absolutePath(const QString &path)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define MAX_PATH
static QString canonicalPath(const QString &rootPath)
#define QT_CONFIG(feature)
#define tr(X)
#define emit
#define Q_UNUSED(x)
static int compare(quint64 a, quint64 b)
long long qint64
Definition qtypes.h:60
long HRESULT
QString dir
[11]
QStringList files
[8]
const QStringList filters({"Image files (*.png *.xpm *.jpg)", "Text files (*.txt)", "Any files (*)" })
[6]
QGraphicsItem * item
widget render & pixmap
QHostInfo info
[0]
bool contains(const AT &t) const noexcept
Definition qlist.h:45