Qt 6.x
The Qt SDK
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
131{
132 Q_D(const QFileSystemModel);
133 return d->node(index)->fileInfo();
134}
135
171{
172 Q_D(QFileSystemModel);
173
174 const QString path = d->filePath(aindex);
175 const QFileInfo fileInfo(path);
176#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
177 // QTBUG-65683: Remove file system watchers prior to deletion to prevent
178 // failure due to locked files on Windows.
179 const QStringList watchedPaths = d->unwatchPathsAt(aindex);
180#endif // filesystemwatcher && Q_OS_WIN
181 const bool success = (fileInfo.isFile() || fileInfo.isSymLink())
183#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
184 if (!success)
185 d->watchPaths(watchedPaths);
186#endif // filesystemwatcher && Q_OS_WIN
187 return success;
188}
189
195{
196}
197
203{
204 Q_D(QFileSystemModel);
205 d->init();
206}
207
212
217{
218 Q_D(const QFileSystemModel);
219 if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
220 return QModelIndex();
221
222 // get the parent node
223 QFileSystemModelPrivate::QFileSystemNode *parentNode = (d->indexValid(parent) ? d->node(parent) :
224 const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&d->root));
225 Q_ASSERT(parentNode);
226
227 // now get the internal pointer for the index
228 const int i = d->translateVisibleLocation(parentNode, row);
229 if (i >= parentNode->visibleChildren.size())
230 return QModelIndex();
231 const QString &childName = parentNode->visibleChildren.at(i);
232 const QFileSystemModelPrivate::QFileSystemNode *indexNode = parentNode->children.value(childName);
233 Q_ASSERT(indexNode);
234
235 return createIndex(row, column, const_cast<QFileSystemModelPrivate::QFileSystemNode*>(indexNode));
236}
237
242{
243 if (row == idx.row() && column < columnCount(idx.parent())) {
244 // cheap sibling operation: just adjust the column:
245 return createIndex(row, column, idx.internalPointer());
246 } else {
247 // for anything else: call the default implementation
248 // (this could probably be optimized, too):
250 }
251}
252
259{
260 Q_D(const QFileSystemModel);
261 QFileSystemModelPrivate::QFileSystemNode *node = d->node(path, false);
262 return d->index(node, column);
263}
264
271{
272 if (!index.isValid())
273 return const_cast<QFileSystemNode*>(&root);
275 Q_ASSERT(indexNode);
276 return indexNode;
277}
278
279#ifdef Q_OS_WIN32
280static QString qt_GetLongPathName(const QString &strShortPath)
281{
282 if (strShortPath.isEmpty()
283 || strShortPath == "."_L1 || strShortPath == ".."_L1)
284 return strShortPath;
285 if (strShortPath.length() == 2 && strShortPath.endsWith(u':'))
286 return strShortPath.toUpper();
287 const QString absPath = QDir(strShortPath).absolutePath();
288 if (absPath.startsWith("//"_L1)
289 || absPath.startsWith("\\\\"_L1)) // unc
290 return QDir::fromNativeSeparators(absPath);
291 if (absPath.startsWith(u'/'))
292 return QString();
293 const QString inputString = "\\\\?\\"_L1 + QDir::toNativeSeparators(absPath);
295 DWORD result = ::GetLongPathName((wchar_t*)inputString.utf16(),
296 buffer.data(),
297 buffer.size());
298 if (result > DWORD(buffer.size())) {
299 buffer.resize(result);
300 result = ::GetLongPathName((wchar_t*)inputString.utf16(),
301 buffer.data(),
302 buffer.size());
303 }
304 if (result > 4) {
305 QString longPath = QString::fromWCharArray(buffer.data() + 4); // ignoring prefix
306 longPath[0] = longPath.at(0).toUpper(); // capital drive letters
307 return QDir::fromNativeSeparators(longPath);
308 } else {
309 return QDir::fromNativeSeparators(strShortPath);
310 }
311}
312#endif
313
320{
321 Q_Q(const QFileSystemModel);
322 Q_UNUSED(q);
323 if (path.isEmpty() || path == myComputer() || path.startsWith(u':'))
325
326 // Construct the nodes up to the new root path if they need to be built
328#ifdef Q_OS_WIN32
329 QString longPath = qt_GetLongPathName(path);
330#else
331 QString longPath = path;
332#endif
333 if (longPath == rootDir.path())
335 else
336 absolutePath = QDir(longPath).absolutePath();
337
338 // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const?
339 QStringList pathElements = absolutePath.split(u'/', Qt::SkipEmptyParts);
340 if ((pathElements.isEmpty())
341#if !defined(Q_OS_WIN)
342 && QDir::fromNativeSeparators(longPath) != "/"_L1
343#endif
344 )
346 QModelIndex index = QModelIndex(); // start with "My Computer"
347 QString elementPath;
348 QChar separator = u'/';
349 QString trailingSeparator;
350#if defined(Q_OS_WIN)
351 if (absolutePath.startsWith("//"_L1)) { // UNC path
352 QString host = "\\\\"_L1 + pathElements.constFirst();
354 absolutePath.append(u'/');
355 if (longPath.endsWith(u'/') && !absolutePath.endsWith(u'/'))
356 absolutePath.append(u'/');
357 if (absolutePath.endsWith(u'/'))
358 trailingSeparator = "\\"_L1;
359 int r = 0;
360 auto rootNode = const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
361 auto it = root.children.constFind(host);
362 if (it != root.children.cend()) {
363 host = it.key(); // Normalize case for lookup in visibleLocation()
364 } else {
365 if (pathElements.count() == 1 && !absolutePath.endsWith(u'/'))
366 return rootNode;
367 QFileInfo info(host);
368 if (!info.exists())
369 return rootNode;
371 p->addNode(rootNode, host,info);
372 p->addVisibleFiles(rootNode, QStringList(host));
373 }
374 r = rootNode->visibleLocation(host);
375 r = translateVisibleLocation(rootNode, r);
376 index = q->index(r, 0, QModelIndex());
377 pathElements.pop_front();
378 separator = u'\\';
379 elementPath = host;
380 elementPath.append(separator);
381 } else {
382 if (!pathElements.at(0).contains(u':')) {
383 QString rootPath = QDir(longPath).rootPath();
384 pathElements.prepend(rootPath);
385 }
386 if (pathElements.at(0).endsWith(u'/'))
387 pathElements[0].chop(1);
388 }
389#else
390 // add the "/" item, since it is a valid path element on Unix
391 if (absolutePath[0] == u'/')
392 pathElements.prepend("/"_L1);
393#endif
394
396
397 for (int i = 0; i < pathElements.size(); ++i) {
398 QString element = pathElements.at(i);
399 if (i != 0)
400 elementPath.append(separator);
401 elementPath.append(element);
402 if (i == pathElements.size() - 1)
403 elementPath.append(trailingSeparator);
404#ifdef Q_OS_WIN
405 // On Windows, "filename " and "filename" are equivalent and
406 // "filename . " and "filename" are equivalent
407 // "filename......." and "filename" are equivalent Task #133928
408 // whereas "filename .txt" is still "filename .txt"
409 // If after stripping the characters there is nothing left then we
410 // just return the parent directory as it is assumed that the path
411 // is referring to the parent
412 while (element.endsWith(u'.') || element.endsWith(u' '))
413 element.chop(1);
414 // Only filenames that can't possibly exist will be end up being empty
415 if (element.isEmpty())
416 return parent;
417#endif
418 bool alreadyExisted = parent->children.contains(element);
419
420 // we couldn't find the path element, we create a new node since we
421 // _know_ that the path is valid
422 if (alreadyExisted) {
423 if ((parent->children.size() == 0)
424 || (parent->caseSensitive()
425 && parent->children.value(element)->fileName != element)
426 || (!parent->caseSensitive()
427 && parent->children.value(element)->fileName.toLower() != element.toLower()))
428 alreadyExisted = false;
429 }
430
432 if (!alreadyExisted) {
433 // Someone might call ::index("file://cookie/monster/doesn't/like/veggies"),
434 // a path that doesn't exists, I.E. don't blindly create directories.
435 QFileInfo info(elementPath);
436 if (!info.exists())
439 node = p->addNode(parent, element,info);
440#if QT_CONFIG(filesystemwatcher)
441 node->populate(fileInfoGatherer.getInfo(info));
442#endif
443 } else {
444 node = parent->children.value(element);
445 }
446
447 Q_ASSERT(node);
448 if (!node->isVisible) {
449 // It has been filtered out
450 if (alreadyExisted && node->hasInformation() && !fetch)
452
455 if (!p->bypassFilters.contains(node))
456 p->bypassFilters[node] = 1;
457 QString dir = q->filePath(this->index(parent));
458 if (!node->hasInformation() && fetch) {
459 Fetching f = { std::move(dir), std::move(element), node };
460 p->toFetch.append(std::move(f));
461 p->fetchingTimer.start(0, const_cast<QFileSystemModel*>(q));
462 }
463 }
464 parent = node;
465 }
466
467 return parent;
468}
469
474{
475 Q_D(QFileSystemModel);
476 if (event->timerId() == d->fetchingTimer.timerId()) {
477 d->fetchingTimer.stop();
478#if QT_CONFIG(filesystemwatcher)
479 for (int i = 0; i < d->toFetch.size(); ++i) {
480 const QFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node;
481 if (!node->hasInformation()) {
482 d->fileInfoGatherer.fetchExtendedInformation(d->toFetch.at(i).dir,
483 QStringList(d->toFetch.at(i).file));
484 } else {
485 // qDebug("yah!, you saved a little gerbil soul");
486 }
487 }
488#endif
489 d->toFetch.clear();
490 }
491}
492
498{
499 // This function is for public usage only because it could create a file info
500 Q_D(const QFileSystemModel);
501 if (!index.isValid())
502 return true;
504 if (n->hasInformation())
505 return n->isDir();
506 return fileInfo(index).isDir();
507}
508
513{
514 Q_D(const QFileSystemModel);
515 if (!index.isValid())
516 return 0;
517 return d->node(index)->size();
518}
519
524{
525 Q_D(const QFileSystemModel);
526 if (!index.isValid())
527 return QString();
528 return d->node(index)->type();
529}
530
542{
544}
545
559{
560 Q_D(const QFileSystemModel);
561 if (!index.isValid())
562 return QDateTime();
563 return d->node(index)->lastModified(tz);
564}
565
570{
571 Q_D(const QFileSystemModel);
572 if (!d->indexValid(index))
573 return QModelIndex();
574
576 Q_ASSERT(indexNode != nullptr);
577 QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
578 if (parentNode == nullptr || parentNode == &d->root)
579 return QModelIndex();
580
581 // get the parent's row
582 QFileSystemModelPrivate::QFileSystemNode *grandParentNode = parentNode->parent;
583 Q_ASSERT(grandParentNode->children.contains(parentNode->fileName));
584 int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName));
585 if (visualRow == -1)
586 return QModelIndex();
587 return createIndex(visualRow, 0, parentNode);
588}
589
590/*
591 \internal
592
593 return the index for node
594*/
596{
597 Q_Q(const QFileSystemModel);
598 QFileSystemModelPrivate::QFileSystemNode *parentNode = (node ? node->parent : nullptr);
599 if (node == &root || !parentNode)
600 return QModelIndex();
601
602 // get the parent's row
603 Q_ASSERT(node);
604 if (!node->isVisible)
605 return QModelIndex();
606
607 int visualRow = translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName));
608 return q->createIndex(visualRow, column, const_cast<QFileSystemNode*>(node));
609}
610
615{
616 Q_D(const QFileSystemModel);
617 if (parent.column() > 0)
618 return false;
619
620 if (!parent.isValid()) // drives
621 return true;
622
623 const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
624 Q_ASSERT(indexNode);
625 return (indexNode->isDir());
626}
627
632{
633 Q_D(const QFileSystemModel);
634 if (!d->setRootPath)
635 return false;
636 const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
637 return (!indexNode->populatedChildren);
638}
639
644{
645 Q_D(QFileSystemModel);
646 if (!d->setRootPath)
647 return;
649 if (indexNode->populatedChildren)
650 return;
651 indexNode->populatedChildren = true;
652#if QT_CONFIG(filesystemwatcher)
653 d->fileInfoGatherer.list(filePath(parent));
654#endif
655}
656
661{
662 Q_D(const QFileSystemModel);
663 if (parent.column() > 0)
664 return 0;
665
666 if (!parent.isValid())
667 return d->root.visibleChildren.size();
668
669 const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);
670 return parentNode->visibleChildren.size();
671}
672
677{
678 return (parent.column() > 0) ? 0 : QFileSystemModelPrivate::NumColumns;
679}
680
687{
688#if QT_CONFIG(filesystemwatcher)
689 Q_D(const QFileSystemModel);
690#endif
691 switch (role) {
692 case Qt::DisplayRole:
694#if QT_CONFIG(filesystemwatcher)
696 return d->fileInfoGatherer.iconProvider()->icon(QAbstractFileIconProvider::Computer);
697#endif
698 }
699 return QVariant();
700}
701
706{
707 Q_D(const QFileSystemModel);
708 if (!index.isValid() || index.model() != this)
709 return QVariant();
710
711 switch (role) {
712 case Qt::EditRole:
714 return d->name(index);
716 case Qt::DisplayRole:
717 switch (index.column()) {
718 case QFileSystemModelPrivate::NameColumn: return d->displayName(index);
719 case QFileSystemModelPrivate::SizeColumn: return d->size(index);
720 case QFileSystemModelPrivate::TypeColumn: return d->type(index);
721 case QFileSystemModelPrivate::TimeColumn: return d->time(index);
722 default:
723 qWarning("data: invalid display value column %d", index.column());
724 break;
725 }
726 break;
727 case FilePathRole:
728 return filePath(index);
729 case FileNameRole:
730 return d->name(index);
733 QIcon icon = d->icon(index);
734#if QT_CONFIG(filesystemwatcher)
735 if (icon.isNull()) {
736 if (d->node(index)->isDir())
737 icon = d->fileInfoGatherer.iconProvider()->icon(QAbstractFileIconProvider::Folder);
738 else
739 icon = d->fileInfoGatherer.iconProvider()->icon(QAbstractFileIconProvider::File);
740 }
741#endif // filesystemwatcher
742 return icon;
743 }
744 break;
748 break;
749 case FilePermissions:
750 int p = permissions(index);
751 return p;
752 }
753
754 return QVariant();
755}
756
761{
762 if (!index.isValid())
763 return QString();
764 const QFileSystemNode *n = node(index);
765 if (n->isDir()) {
766#ifdef Q_OS_MAC
767 return "--"_L1;
768#else
769 return ""_L1;
770#endif
771 // Windows - ""
772 // OS X - "--"
773 // Konqueror - "4 KB"
774 // Nautilus - "9 items" (the number of children)
775 }
776 return size(n->size());
777}
778
780{
781 return QLocale::system().formattedDataSize(bytes);
782}
783
788{
789 if (!index.isValid())
790 return QString();
791#if QT_CONFIG(datestring)
793#else
795 return QString();
796#endif
797}
798
799/*
800 \internal
801*/
803{
804 if (!index.isValid())
805 return QString();
806 return node(index)->type();
807}
808
813{
814 if (!index.isValid())
815 return QString();
816 QFileSystemNode *dirNode = node(index);
817 if (
818#if QT_CONFIG(filesystemwatcher)
819 fileInfoGatherer.resolveSymlinks() &&
820#endif
821 !resolvedSymLinks.isEmpty() && dirNode->isSymLink(/* ignoreNtfsSymLinks = */ true)) {
823 return resolvedSymLinks.value(fullPath, dirNode->fileName);
824 }
825 return dirNode->fileName;
826}
827
832{
833#if defined(Q_OS_WIN)
834 QFileSystemNode *dirNode = node(index);
835 if (!dirNode->volumeName.isEmpty())
836 return dirNode->volumeName;
837#endif
838 return name(index);
839}
840
845{
846 if (!index.isValid())
847 return QIcon();
848 return node(index)->icon();
849}
850
854bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role)
855{
856 Q_D(QFileSystemModel);
857 if (!idx.isValid()
858 || idx.column() != 0
859 || role != Qt::EditRole
860 || (flags(idx) & Qt::ItemIsEditable) == 0) {
861 return false;
862 }
863
864 QString newName = value.toString();
865 QString oldName = idx.data().toString();
866 if (newName == oldName)
867 return true;
868
869 const QString parentPath = filePath(parent(idx));
870
871 if (newName.isEmpty() || QDir::toNativeSeparators(newName).contains(QDir::separator()))
872 return false;
873
874#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
875 // QTBUG-65683: Remove file system watchers prior to renaming to prevent
876 // failure due to locked files on Windows.
877 const QStringList watchedPaths = d->unwatchPathsAt(idx);
878#endif // filesystemwatcher && Q_OS_WIN
879 if (!QDir(parentPath).rename(oldName, newName)) {
880#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
881 d->watchPaths(watchedPaths);
882#endif
883 return false;
884 } else {
885 /*
886 *After re-naming something we don't want the selection to change*
887 - can't remove rows and later insert
888 - can't quickly remove and insert
889 - index pointer can't change because treeview doesn't use persistent index's
890
891 - if this get any more complicated think of changing it to just
892 use layoutChanged
893 */
894
895 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx);
896 QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
897 int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName);
898
899 parentNode->visibleChildren.removeAt(visibleLocation);
900 std::unique_ptr<QFileSystemModelPrivate::QFileSystemNode> nodeToRename(parentNode->children.take(oldName));
901 nodeToRename->fileName = newName;
902 nodeToRename->parent = parentNode;
903#if QT_CONFIG(filesystemwatcher)
904 nodeToRename->populate(d->fileInfoGatherer.getInfo(QFileInfo(parentPath, newName)));
905#endif
906 nodeToRename->isVisible = true;
907 parentNode->children[newName] = nodeToRename.release();
908 parentNode->visibleChildren.insert(visibleLocation, newName);
909
910 d->delayedSort();
911 emit fileRenamed(parentPath, oldName, newName);
912 }
913 return true;
914}
915
919QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
920{
921 switch (role) {
923 if (section == 0) {
924 // ### TODO oh man this is ugly and doesn't even work all the way!
925 // it is still 2 pixels off
928 return pixmap;
929 }
930 break;
932 return Qt::AlignLeft;
933 }
934
935 if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
936 return QAbstractItemModel::headerData(section, orientation, role);
937
938 QString returnValue;
939 switch (section) {
941 returnValue = tr("Name");
942 break;
944 returnValue = tr("Size");
945 break;
947 returnValue =
948#ifdef Q_OS_MAC
949 tr("Kind", "Match OS X Finder");
950#else
951 tr("Type", "All other platforms");
952#endif
953 break;
954 // Windows - Type
955 // OS X - Kind
956 // Konqueror - File Type
957 // Nautilus - Type
959 returnValue = tr("Date Modified");
960 break;
961 default: return QVariant();
962 }
963 return returnValue;
964}
965
969Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const
970{
971 Q_D(const QFileSystemModel);
972 Qt::ItemFlags flags = QAbstractItemModel::flags(index);
973 if (!index.isValid())
974 return flags;
975
977 if (d->nameFilterDisables && !d->passNameFilters(indexNode)) {
978 flags &= ~Qt::ItemIsEnabled;
979 // ### TODO you shouldn't be able to set this as the current item, task 119433
980 return flags;
981 }
982
984
985 if (!indexNode->isDir())
987 if (d->readOnly)
988 return flags;
989 if ((index.column() == 0) && indexNode->permissions() & QFile::WriteUser) {
991 if (indexNode->isDir())
993 }
994 return flags;
995}
996
1001{
1002 Q_Q(QFileSystemModel);
1003 q->sort(sortColumn, sortOrder);
1004}
1005
1006
1007/*
1008 \internal
1009 Helper functor used by sort()
1010*/
1012{
1013public:
1014 inline QFileSystemModelSorter(int column) : sortColumn(column)
1015 {
1016 naturalCompare.setNumericMode(true);
1018 }
1019
1022 {
1023 switch (sortColumn) {
1025#ifndef Q_OS_MAC
1026 // place directories before files
1027 bool left = l->isDir();
1028 bool right = r->isDir();
1029 if (left ^ right)
1030 return left;
1031#endif
1032 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1033 }
1035 {
1036 // Directories go first
1037 bool left = l->isDir();
1038 bool right = r->isDir();
1039 if (left ^ right)
1040 return left;
1041
1042 qint64 sizeDifference = l->size() - r->size();
1043 if (sizeDifference == 0)
1044 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1045
1046 return sizeDifference < 0;
1047 }
1049 {
1050 int compare = naturalCompare.compare(l->type(), r->type());
1051 if (compare == 0)
1052 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1053
1054 return compare < 0;
1055 }
1057 {
1059 const QDateTime right = r->lastModified(QTimeZone::UTC);
1060 if (left == right)
1061 return naturalCompare.compare(l->fileName, r->fileName) < 0;
1062
1063 return left < right;
1064 }
1065 }
1066 Q_ASSERT(false);
1067 return false;
1068 }
1069
1072 {
1073 return compareNodes(l, r);
1074 }
1075
1076
1077private:
1078 QCollator naturalCompare;
1079 int sortColumn;
1080};
1081
1082/*
1083 \internal
1084
1085 Sort all of the children of parent
1086*/
1088{
1089 Q_Q(QFileSystemModel);
1091 if (indexNode->children.size() == 0)
1092 return;
1093
1095
1096 for (auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) {
1097 if (filtersAcceptsNode(iterator.value())) {
1098 values.append(iterator.value());
1099 } else {
1100 iterator.value()->isVisible = false;
1101 }
1102 }
1104 std::sort(values.begin(), values.end(), ms);
1105 // First update the new visible list
1106 indexNode->visibleChildren.clear();
1107 //No more dirty item we reset our internal dirty index
1108 indexNode->dirtyChildrenIndex = -1;
1109 indexNode->visibleChildren.reserve(values.size());
1110 for (QFileSystemNode *node : std::as_const(values)) {
1111 indexNode->visibleChildren.append(node->fileName);
1112 node->isVisible = true;
1113 }
1114
1115 if (!disableRecursiveSort) {
1116 for (int i = 0; i < q->rowCount(parent); ++i) {
1117 const QModelIndex childIndex = q->index(i, 0, parent);
1118 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(childIndex);
1119 //Only do a recursive sort on visible nodes
1120 if (indexNode->isVisible)
1121 sortChildren(column, childIndex);
1122 }
1123 }
1124}
1125
1130{
1131 Q_D(QFileSystemModel);
1132 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
1133 return;
1134
1138 oldNodes.reserve(oldList.size());
1139 for (const QModelIndex &oldNode : oldList) {
1140 QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldNode), oldNode.column());
1141 oldNodes.append(pair);
1142 }
1143
1144 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
1145 //we sort only from where we are, don't need to sort all the model
1146 d->sortChildren(column, index(rootPath()));
1147 d->sortColumn = column;
1148 d->forceSort = false;
1149 }
1150 d->sortOrder = order;
1151
1152 QModelIndexList newList;
1153 newList.reserve(oldNodes.size());
1154 for (const auto &[node, col]: std::as_const(oldNodes))
1155 newList.append(d->index(node, col));
1156
1157 changePersistentIndexList(oldList, newList);
1159}
1160
1166{
1167 return QStringList("text/uri-list"_L1);
1168}
1169
1179{
1180 QList<QUrl> urls;
1182 for (; it != indexes.end(); ++it)
1183 if ((*it).column() == QFileSystemModelPrivate::NameColumn)
1184 urls << QUrl::fromLocalFile(filePath(*it));
1185 QMimeData *data = new QMimeData();
1186 data->setUrls(urls);
1187 return data;
1188}
1189
1199 int row, int column, const QModelIndex &parent)
1200{
1201 Q_UNUSED(row);
1203 if (!parent.isValid() || isReadOnly())
1204 return false;
1205
1206 bool success = true;
1208
1209 QList<QUrl> urls = data->urls();
1211
1212 switch (action) {
1213 case Qt::CopyAction:
1214 for (; it != urls.constEnd(); ++it) {
1215 QString path = (*it).toLocalFile();
1216 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
1217 }
1218 break;
1219 case Qt::LinkAction:
1220 for (; it != urls.constEnd(); ++it) {
1221 QString path = (*it).toLocalFile();
1222 success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
1223 }
1224 break;
1225 case Qt::MoveAction:
1226 for (; it != urls.constEnd(); ++it) {
1227 QString path = (*it).toLocalFile();
1228 success = QFile::rename(path, to + QFileInfo(path).fileName()) && success;
1229 }
1230 break;
1231 default:
1232 return false;
1233 }
1234
1235 return success;
1236}
1237
1242{
1244}
1245
1250{
1253 QByteArrayLiteral("fileIcon")); // == Qt::decoration
1256 ret.insert(QFileSystemModel::FilePermissions, QByteArrayLiteral("filePermissions"));
1257 return ret;
1258}
1259
1290{
1291 QFileSystemModel::Options previousOptions = options();
1292 setOptions(previousOptions.setFlag(option, on));
1293}
1294
1304{
1305 return options().testFlag(option);
1306}
1307
1320{
1321 const Options changed = (options ^ QFileSystemModel::options());
1322
1323 if (changed.testFlag(DontResolveSymlinks))
1325
1326#if QT_CONFIG(filesystemwatcher)
1327 Q_D(QFileSystemModel);
1328 if (changed.testFlag(DontWatchForChanges))
1329 d->fileInfoGatherer.setWatching(!options.testFlag(DontWatchForChanges));
1330#endif
1331
1332 if (changed.testFlag(DontUseCustomDirectoryIcons)) {
1333 if (auto provider = iconProvider()) {
1334 QAbstractFileIconProvider::Options providerOptions = provider->options();
1337 provider->setOptions(providerOptions);
1338 } else {
1339 qWarning("Setting QFileSystemModel::DontUseCustomDirectoryIcons has no effect when no provider is used");
1340 }
1341 }
1342}
1343
1344QFileSystemModel::Options QFileSystemModel::options() const
1345{
1346 QFileSystemModel::Options result;
1348#if QT_CONFIG(filesystemwatcher)
1349 Q_D(const QFileSystemModel);
1350 result.setFlag(DontWatchForChanges, !d->fileInfoGatherer.isWatching());
1351#else
1352 result.setFlag(DontWatchForChanges);
1353#endif
1354 if (auto provider = iconProvider()) {
1356 provider->options().testFlag(QAbstractFileIconProvider::DontUseCustomDirectoryIcons));
1357 }
1358 return result;
1359}
1360
1366{
1367 Q_D(const QFileSystemModel);
1368 QString fullPath = d->filePath(index);
1370 if (dirNode->isSymLink()
1371#if QT_CONFIG(filesystemwatcher)
1372 && d->fileInfoGatherer.resolveSymlinks()
1373#endif
1374 && d->resolvedSymLinks.contains(fullPath)
1375 && dirNode->isDir()) {
1376 QFileInfo fullPathInfo(dirNode->fileInfo());
1377 if (!dirNode->hasInformation())
1378 fullPathInfo = QFileInfo(fullPath);
1379 QString canonicalPath = fullPathInfo.canonicalFilePath();
1380 auto *canonicalNode = d->node(fullPathInfo.canonicalFilePath(), false);
1381 QFileInfo resolvedInfo = canonicalNode->fileInfo();
1382 if (!canonicalNode->hasInformation())
1383 resolvedInfo = QFileInfo(canonicalPath);
1384 if (resolvedInfo.exists())
1385 return resolvedInfo.filePath();
1386 }
1387 return fullPath;
1388}
1389
1391{
1392 Q_Q(const QFileSystemModel);
1393 Q_UNUSED(q);
1394 if (!index.isValid())
1395 return QString();
1396 Q_ASSERT(index.model() == q);
1397
1399 QModelIndex idx = index;
1400 while (idx.isValid()) {
1402 if (dirNode)
1403 path.prepend(dirNode->fileName);
1404 idx = idx.parent();
1405 }
1407#if !defined(Q_OS_WIN)
1408 if ((fullPath.size() > 2) && fullPath[0] == u'/' && fullPath[1] == u'/')
1409 fullPath = fullPath.mid(1);
1410#else
1411 if (fullPath.length() == 2 && fullPath.endsWith(u':'))
1412 fullPath.append(u'/');
1413#endif
1414 return fullPath;
1415}
1416
1421{
1422 Q_D(QFileSystemModel);
1423 if (!parent.isValid())
1424 return parent;
1425
1427 if (!dir.mkdir(name))
1428 return QModelIndex();
1430 d->addNode(parentNode, name, QFileInfo());
1431 Q_ASSERT(parentNode->children.contains(name));
1433#if QT_CONFIG(filesystemwatcher)
1434 node->populate(d->fileInfoGatherer.getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name)));
1435#endif
1436 d->addVisibleFiles(parentNode, QStringList(name));
1437 return d->index(node);
1438}
1439
1443QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index) const
1444{
1445 Q_D(const QFileSystemModel);
1446 return d->node(index)->permissions();
1447}
1448
1463{
1464 Q_D(QFileSystemModel);
1465#ifdef Q_OS_WIN
1466#ifdef Q_OS_WIN32
1467 QString longNewPath = qt_GetLongPathName(newPath);
1468#else
1469 QString longNewPath = QDir::fromNativeSeparators(newPath);
1470#endif
1471#else
1472 QString longNewPath = newPath;
1473#endif
1474 //we remove .. and . from the given path if exist
1475 if (!newPath.isEmpty())
1476 longNewPath = QDir::cleanPath(longNewPath);
1477
1478 d->setRootPath = true;
1479
1480 //user don't ask for the root path ("") but the conversion failed
1481 if (!newPath.isEmpty() && longNewPath.isEmpty())
1482 return d->index(rootPath());
1483
1484 if (d->rootDir.path() == longNewPath)
1485 return d->index(rootPath());
1486
1487 auto node = d->node(longNewPath);
1488 QFileInfo newPathInfo;
1489 if (node && node->hasInformation())
1490 newPathInfo = node->fileInfo();
1491 else
1492 newPathInfo = QFileInfo(longNewPath);
1493
1494 bool showDrives = (longNewPath.isEmpty() || longNewPath == QFileSystemModelPrivate::myComputer());
1495 if (!showDrives && !newPathInfo.exists())
1496 return d->index(rootPath());
1497
1498 //We remove the watcher on the previous path
1499 if (!rootPath().isEmpty() && rootPath() != "."_L1) {
1500 //This remove the watcher for the old rootPath
1501#if QT_CONFIG(filesystemwatcher)
1502 d->fileInfoGatherer.removePath(rootPath());
1503#endif
1504 //This line "marks" the node as dirty, so the next fetchMore
1505 //call on the path will ask the gatherer to install a watcher again
1506 //But it doesn't re-fetch everything
1507 d->node(rootPath())->populatedChildren = false;
1508 }
1509
1510 // We have a new valid root path
1511 d->rootDir = QDir(longNewPath);
1512 QModelIndex newRootIndex;
1513 if (showDrives) {
1514 // otherwise dir will become '.'
1515 d->rootDir.setPath(""_L1);
1516 } else {
1517 newRootIndex = d->index(d->rootDir.path());
1518 }
1519 fetchMore(newRootIndex);
1520 emit rootPathChanged(longNewPath);
1521 d->forceSort = true;
1522 d->delayedSort();
1523 return newRootIndex;
1524}
1525
1532{
1533 Q_D(const QFileSystemModel);
1534 return d->rootDir.path();
1535}
1536
1543{
1544 Q_D(const QFileSystemModel);
1545 QDir dir(d->rootDir);
1546 dir.setNameFilters(nameFilters());
1547 dir.setFilter(filter());
1548 return dir;
1549}
1550
1555{
1556 Q_D(QFileSystemModel);
1557#if QT_CONFIG(filesystemwatcher)
1558 d->fileInfoGatherer.setIconProvider(provider);
1559#endif
1560 d->root.updateIcon(provider, QString());
1561}
1562
1567{
1568#if QT_CONFIG(filesystemwatcher)
1569 Q_D(const QFileSystemModel);
1570 return d->fileInfoGatherer.iconProvider();
1571#else
1572 return 0;
1573#endif
1574}
1575
1585{
1586 Q_D(QFileSystemModel);
1587 if (d->filters == filters)
1588 return;
1589 const bool changingCaseSensitivity =
1590 filters.testFlag(QDir::CaseSensitive) != d->filters.testFlag(QDir::CaseSensitive);
1591 d->filters = filters;
1592 if (changingCaseSensitivity)
1593 d->rebuildNameFilterRegexps();
1594 d->forceSort = true;
1595 d->delayedSort();
1596}
1597
1606QDir::Filters QFileSystemModel::filter() const
1607{
1608 Q_D(const QFileSystemModel);
1609 return d->filters;
1610}
1611
1623{
1624#if QT_CONFIG(filesystemwatcher)
1625 Q_D(QFileSystemModel);
1626 d->fileInfoGatherer.setResolveSymlinks(enable);
1627#else
1629#endif
1630}
1631
1633{
1634#if QT_CONFIG(filesystemwatcher)
1635 Q_D(const QFileSystemModel);
1636 return d->fileInfoGatherer.resolveSymlinks();
1637#else
1638 return false;
1639#endif
1640}
1641
1652{
1653 Q_D(QFileSystemModel);
1654 d->readOnly = enable;
1655}
1656
1658{
1659 Q_D(const QFileSystemModel);
1660 return d->readOnly;
1661}
1662
1670{
1671 Q_D(QFileSystemModel);
1672 if (d->nameFilterDisables == enable)
1673 return;
1674 d->nameFilterDisables = enable;
1675 d->forceSort = true;
1676 d->delayedSort();
1677}
1678
1680{
1681 Q_D(const QFileSystemModel);
1682 return d->nameFilterDisables;
1683}
1684
1689{
1690#if QT_CONFIG(regularexpression)
1691 Q_D(QFileSystemModel);
1692
1693 if (!d->bypassFilters.isEmpty()) {
1694 // update the bypass filter to only bypass the stuff that must be kept around
1695 d->bypassFilters.clear();
1696 // We guarantee that rootPath will stick around
1698 const QModelIndexList persistentList = persistentIndexList();
1699 for (const auto &persistentIndex : persistentList) {
1700 QFileSystemModelPrivate::QFileSystemNode *node = d->node(persistentIndex);
1701 while (node) {
1702 if (d->bypassFilters.contains(node))
1703 break;
1704 if (node->isDir())
1705 d->bypassFilters[node] = true;
1706 node = node->parent;
1707 }
1708 }
1709 }
1710
1711 d->nameFilters = filters;
1712 d->rebuildNameFilterRegexps();
1713 d->forceSort = true;
1714 d->delayedSort();
1715#else
1717#endif
1718}
1719
1724{
1725#if QT_CONFIG(regularexpression)
1726 Q_D(const QFileSystemModel);
1727 return d->nameFilters;
1728#else
1729 return QStringList();
1730#endif
1731}
1732
1737{
1738#if QT_CONFIG(filesystemwatcher)
1739 Q_D(QFileSystemModel);
1740 if (event->type() == QEvent::LanguageChange) {
1741 d->root.retranslateStrings(d->fileInfoGatherer.iconProvider(), QString());
1742 return true;
1743 }
1744#endif
1746}
1747
1749{
1750 QString path = filePath(aindex);
1751 const bool success = QDir().rmdir(path);
1752#if QT_CONFIG(filesystemwatcher)
1753 if (success) {
1754 QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func());
1755 d->fileInfoGatherer.removePath(path);
1756 }
1757#endif
1758 return success;
1759}
1760
1768{
1770 if (parentNode->children.size() == 0)
1771 return;
1772 QStringList toRemove;
1773 QStringList newFiles = files;
1774 std::sort(newFiles.begin(), newFiles.end());
1775 for (auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) {
1776 QStringList::iterator iterator = std::lower_bound(newFiles.begin(), newFiles.end(), i.value()->fileName);
1777 if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator))
1778 toRemove.append(i.value()->fileName);
1779 }
1780 for (int i = 0 ; i < toRemove.size() ; ++i )
1781 removeNode(parentNode, toRemove[i]);
1782}
1783
1784#if defined(Q_OS_WIN)
1785static QString volumeName(const QString &path)
1786{
1787 IShellItem *item = nullptr;
1788 const QString native = QDir::toNativeSeparators(path);
1789 HRESULT hr = SHCreateItemFromParsingName(reinterpret_cast<const wchar_t *>(native.utf16()),
1790 nullptr, IID_IShellItem,
1791 reinterpret_cast<void **>(&item));
1792 if (FAILED(hr))
1793 return QString();
1794 LPWSTR name = nullptr;
1795 hr = item->GetDisplayName(SIGDN_NORMALDISPLAY, &name);
1796 if (FAILED(hr))
1797 return QString();
1799 CoTaskMemFree(name);
1800 item->Release();
1801 return result;
1802}
1803#endif // Q_OS_WIN
1804
1813{
1814 // In the common case, itemLocation == count() so check there first
1816#if QT_CONFIG(filesystemwatcher)
1817 node->populate(info);
1818#else
1819 Q_UNUSED(info);
1820#endif
1821#if defined(Q_OS_WIN)
1822 //The parentNode is "" so we are listing the drives
1823 if (parentNode->fileName.isEmpty())
1824 node->volumeName = volumeName(fileName);
1825#endif
1826 Q_ASSERT(!parentNode->children.contains(fileName));
1827 parentNode->children.insert(fileName, node);
1828 return node;
1829}
1830
1840{
1841 Q_Q(QFileSystemModel);
1842 QModelIndex parent = index(parentNode);
1843 bool indexHidden = isHiddenByFilter(parentNode, parent);
1844
1845 int vLocation = parentNode->visibleLocation(name);
1846 if (vLocation >= 0 && !indexHidden)
1847 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1848 translateVisibleLocation(parentNode, vLocation));
1849 QFileSystemNode * node = parentNode->children.take(name);
1850 delete node;
1851 // cleanup sort files after removing rather then re-sorting which is O(n)
1852 if (vLocation >= 0)
1853 parentNode->visibleChildren.removeAt(vLocation);
1854 if (vLocation >= 0 && !indexHidden)
1855 q->endRemoveRows();
1856}
1857
1867{
1868 Q_Q(QFileSystemModel);
1869 QModelIndex parent = index(parentNode);
1870 bool indexHidden = isHiddenByFilter(parentNode, parent);
1871 if (!indexHidden) {
1872 q->beginInsertRows(parent, parentNode->visibleChildren.size() , parentNode->visibleChildren.size() + newFiles.size() - 1);
1873 }
1874
1875 if (parentNode->dirtyChildrenIndex == -1)
1876 parentNode->dirtyChildrenIndex = parentNode->visibleChildren.size();
1877
1878 for (const auto &newFile : newFiles) {
1879 parentNode->visibleChildren.append(newFile);
1880 parentNode->children.value(newFile)->isVisible = true;
1881 }
1882 if (!indexHidden)
1883 q->endInsertRows();
1884}
1885
1894{
1895 Q_Q(QFileSystemModel);
1896 if (vLocation == -1)
1897 return;
1898 QModelIndex parent = index(parentNode);
1899 bool indexHidden = isHiddenByFilter(parentNode, parent);
1900 if (!indexHidden)
1901 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1902 translateVisibleLocation(parentNode, vLocation));
1903 parentNode->children.value(parentNode->visibleChildren.at(vLocation))->isVisible = false;
1904 parentNode->visibleChildren.removeAt(vLocation);
1905 if (!indexHidden)
1906 q->endRemoveRows();
1907}
1908
1916 const QList<QPair<QString, QFileInfo>> &updates)
1917{
1918#if QT_CONFIG(filesystemwatcher)
1919 Q_Q(QFileSystemModel);
1920 QList<QString> rowsToUpdate;
1921 QStringList newFiles;
1923 QModelIndex parentIndex = index(parentNode);
1924 for (const auto &update : updates) {
1925 QString fileName = update.first;
1926 Q_ASSERT(!fileName.isEmpty());
1927 QExtendedInformation info = fileInfoGatherer.getInfo(update.second);
1928 bool previouslyHere = parentNode->children.contains(fileName);
1929 if (!previouslyHere) {
1930 addNode(parentNode, fileName, info.fileInfo());
1931 }
1933 bool isCaseSensitive = parentNode->caseSensitive();
1934 if (isCaseSensitive) {
1935 if (node->fileName != fileName)
1936 continue;
1937 } else {
1939 continue;
1940 }
1941 if (isCaseSensitive) {
1943 } else {
1945 }
1946
1947 if (*node != info ) {
1948 node->populate(info);
1949 bypassFilters.remove(node);
1950 // brand new information.
1951 if (filtersAcceptsNode(node)) {
1952 if (!node->isVisible) {
1953 newFiles.append(fileName);
1954 } else {
1955 rowsToUpdate.append(fileName);
1956 }
1957 } else {
1958 if (node->isVisible) {
1959 int visibleLocation = parentNode->visibleLocation(fileName);
1960 removeVisibleFile(parentNode, visibleLocation);
1961 } else {
1962 // The file is not visible, don't do anything
1963 }
1964 }
1965 }
1966 }
1967
1968 // bundle up all of the changed signals into as few as possible.
1969 std::sort(rowsToUpdate.begin(), rowsToUpdate.end());
1970 QString min;
1971 QString max;
1972 for (const QString &value : std::as_const(rowsToUpdate)) {
1973 //##TODO is there a way to bundle signals with QString as the content of the list?
1974 /*if (min.isEmpty()) {
1975 min = value;
1976 if (i != rowsToUpdate.count() - 1)
1977 continue;
1978 }
1979 if (i != rowsToUpdate.count() - 1) {
1980 if ((value == min + 1 && max.isEmpty()) || value == max + 1) {
1981 max = value;
1982 continue;
1983 }
1984 }*/
1985 max = value;
1986 min = value;
1987 int visibleMin = parentNode->visibleLocation(min);
1988 int visibleMax = parentNode->visibleLocation(max);
1989 if (visibleMin >= 0
1990 && visibleMin < parentNode->visibleChildren.size()
1991 && parentNode->visibleChildren.at(visibleMin) == min
1992 && visibleMax >= 0) {
1993 // don't use NumColumns here, a subclass might override columnCount
1994 const int lastColumn = q->columnCount(parentIndex) - 1;
1995 const QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMin),
1997 const QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMax),
1998 lastColumn, parentIndex);
1999 // We document that emitting dataChanged with indexes that don't have the
2000 // same parent is undefined behavior.
2001 Q_ASSERT(bottom.parent() == top.parent());
2002 emit q->dataChanged(top, bottom);
2003 }
2004
2005 /*min = QString();
2006 max = QString();*/
2007 }
2008
2009 if (newFiles.size() > 0) {
2010 addVisibleFiles(parentNode, newFiles);
2011 }
2012
2013 if (newFiles.size() > 0 || (sortColumn != 0 && rowsToUpdate.size() > 0)) {
2014 forceSort = true;
2015 delayedSort();
2016 }
2017#else
2018 Q_UNUSED(path);
2019 Q_UNUSED(updates);
2020#endif // filesystemwatcher
2021}
2022
2027{
2028 resolvedSymLinks[fileName] = resolvedName;
2029}
2030
2031#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
2032// Remove file system watchers at/below the index and return a list of previously
2033// watched files. This should be called prior to operations like rename/remove
2034// which might fail due to watchers on platforms like Windows. The watchers
2035// should be restored on failure.
2036QStringList QFileSystemModelPrivate::unwatchPathsAt(const QModelIndex &index)
2037{
2039 if (indexNode == nullptr)
2040 return QStringList();
2041 const Qt::CaseSensitivity caseSensitivity = indexNode->caseSensitive()
2043 const QString path = indexNode->fileInfo().absoluteFilePath();
2044
2046 const auto filter = [path, caseSensitivity] (const QString &watchedPath)
2047 {
2048 const int pathSize = path.size();
2049 if (pathSize == watchedPath.size()) {
2050 return path.compare(watchedPath, caseSensitivity) == 0;
2051 } else if (watchedPath.size() > pathSize) {
2052 return watchedPath.at(pathSize) == u'/'
2053 && watchedPath.startsWith(path, caseSensitivity);
2054 }
2055 return false;
2056 };
2057
2058 const QStringList &watchedFiles = fileInfoGatherer.watchedFiles();
2059 std::copy_if(watchedFiles.cbegin(), watchedFiles.cend(),
2060 std::back_inserter(result), filter);
2061
2062 const QStringList &watchedDirectories = fileInfoGatherer.watchedDirectories();
2063 std::copy_if(watchedDirectories.cbegin(), watchedDirectories.cend(),
2064 std::back_inserter(result), filter);
2065
2066 fileInfoGatherer.unwatchPaths(result);
2067 return result;
2068}
2069#endif // filesystemwatcher && Q_OS_WIN
2070
2072
2074
2079{
2080 Q_Q(QFileSystemModel);
2081
2083
2084 qRegisterMetaType<QList<QPair<QString, QFileInfo>>>();
2085#if QT_CONFIG(filesystemwatcher)
2086 q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(QString,QStringList)),
2088 q->connect(&fileInfoGatherer, SIGNAL(updates(QString, QList<QPair<QString, QFileInfo>>)), q,
2090 q->connect(&fileInfoGatherer, SIGNAL(nameResolved(QString,QString)),
2092 q->connect(&fileInfoGatherer, SIGNAL(directoryLoaded(QString)),
2093 q, SIGNAL(directoryLoaded(QString)));
2094#endif // filesystemwatcher
2096}
2097
2107{
2108 // always accept drives
2109 if (node->parent == &root || bypassFilters.contains(node))
2110 return true;
2111
2112 // If we don't know anything yet don't accept it
2113 if (!node->hasInformation())
2114 return false;
2115
2116 const bool filterPermissions = ((filters & QDir::PermissionMask)
2118 const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
2119 const bool hideFiles = !(filters & QDir::Files);
2120 const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
2121 const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
2122 const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
2123 const bool hideHidden = !(filters & QDir::Hidden);
2124 const bool hideSystem = !(filters & QDir::System);
2125 const bool hideSymlinks = (filters & QDir::NoSymLinks);
2126 const bool hideDot = (filters & QDir::NoDot);
2127 const bool hideDotDot = (filters & QDir::NoDotDot);
2128
2129 // Note that we match the behavior of entryList and not QFileInfo on this.
2130 bool isDot = (node->fileName == "."_L1);
2131 bool isDotDot = (node->fileName == ".."_L1);
2132 if ( (hideHidden && !(isDot || isDotDot) && node->isHidden())
2133 || (hideSystem && node->isSystem())
2134 || (hideDirs && node->isDir())
2135 || (hideFiles && node->isFile())
2136 || (hideSymlinks && node->isSymLink())
2137 || (hideReadable && node->isReadable())
2138 || (hideWritable && node->isWritable())
2139 || (hideExecutable && node->isExecutable())
2140 || (hideDot && isDot)
2141 || (hideDotDot && isDotDot))
2142 return false;
2143
2145}
2146
2147/*
2148 \internal
2149
2150 Returns \c true if node passes the name filters and should be visible.
2151 */
2153{
2154#if QT_CONFIG(regularexpression)
2155 if (nameFilters.isEmpty())
2156 return true;
2157
2158 // Check the name regularexpression filters
2159 if (!(node->isDir() && (filters & QDir::AllDirs))) {
2160 const auto matchesNodeFileName = [node](const QRegularExpression &re)
2161 {
2162 return node->fileName.contains(re);
2163 };
2164 return std::any_of(nameFiltersRegexps.begin(),
2165 nameFiltersRegexps.end(),
2166 matchesNodeFileName);
2167 }
2168#else
2169 Q_UNUSED(node);
2170#endif
2171 return true;
2172}
2173
2174#if QT_CONFIG(regularexpression)
2175void QFileSystemModelPrivate::rebuildNameFilterRegexps()
2176{
2177 nameFiltersRegexps.clear();
2178 nameFiltersRegexps.reserve(nameFilters.size());
2179 const auto cs = (filters & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
2180 const auto convertWildcardToRegexp = [cs](const QString &nameFilter)
2181 {
2182 return QRegularExpression::fromWildcard(nameFilter, cs);
2183 };
2184 std::transform(nameFilters.constBegin(),
2185 nameFilters.constEnd(),
2186 std::back_inserter(nameFiltersRegexps),
2187 convertWildcardToRegexp);
2188}
2189#endif
2190
2192
2193#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
Definition qchar.h:48
QChar toUpper() const noexcept
Returns the uppercase equivalent if the character is lowercase or titlecase; otherwise returns the ch...
Definition qchar.h:449
\inmodule QtCore
Definition qcollator.h:42
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:68
\inmodule QtCore\reentrant
Definition qdatetime.h:257
\inmodule QtCore
Definition qdir.h:19
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:206
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:2395
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
static QString rootPath()
Returns the absolute path of the root directory.
Definition qdir.cpp:2156
bool rmdir(const QString &dirName) const
Removes the directory specified by dirName.
Definition qdir.cpp:1551
@ Executable
Definition qdir.h:30
@ CaseSensitive
Definition qdir.h:40
@ Files
Definition qdir.h:22
@ PermissionMask
Definition qdir.h:31
@ Hidden
Definition qdir.h:34
@ AllDirs
Definition qdir.h:39
@ NoSymLinks
Definition qdir.h:24
@ NoDotDot
Definition qdir.h:42
@ Readable
Definition qdir.h:28
@ Writable
Definition qdir.h:29
@ System
Definition qdir.h:35
@ NoDot
Definition qdir.h:41
@ Dirs
Definition qdir.h:21
\inmodule QtCore
Definition qcoreevent.h:45
@ LanguageChange
Definition qcoreevent.h:123
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
bool isSymLink() const
Returns true if this object points to a symbolic link, shortcut, or alias; otherwise returns false.
QString absoluteFilePath() const
Returns an absolute path including the file name.
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.
QString canonicalFilePath() const
Returns the canonical path including the file name, i.e.
QString filePath() const
Returns the file name, including the path (which may be absolute or relative).
bool exists() const
Returns true if the file exists; otherwise returns false.
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
void _q_directoryChanged(const QString &directory, const QStringList &list)
QString time(const QModelIndex &index) const
QString size(const QModelIndex &index) const
bool isHiddenByFilter(QFileSystemNode *indexNode, const QModelIndex &index) const
void _q_fileSystemChanged(const QString &path, const QList< QPair< QString, QFileInfo > > &)
QIcon icon(const QModelIndex &index) const
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 _q_resolvedName(const QString &fileName, const QString &resolvedName)
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:311
~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 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:699
bool copy(const QString &newName)
Copies the file named fileName() to newName.
Definition qfile.cpp:744
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:533
\inmodule QtCore
Definition qhash.h:818
T value(const Key &key) const noexcept
Definition qhash.h:1044
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
Definition qhash.h:926
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:973
\inmodule QtGui
Definition qimage.h:37
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
void removeAt(qsizetype i)
Definition qlist.h:573
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:471
iterator end()
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
T value(qsizetype i) const
Definition qlist.h:661
const_iterator constBegin() const noexcept
Definition qlist.h:615
iterator begin()
Definition qlist.h:608
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
const_iterator constEnd() const noexcept
Definition qlist.h:616
void clear()
Definition qlist.h:417
@ ShortFormat
Definition qlocale.h:865
QString formattedDataSize(qint64 bytes, int precision=2, DataSizeFormats format=DataSizeIecFormat) const
Definition qlocale.cpp:4533
QString toString(qlonglong i) const
Returns a localized string representation of i.
Definition qlocale.cpp:1962
static QLocale system()
Returns a QLocale object initialized to the system locale.
Definition qlocale.cpp:2742
\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:61
\inmodule QtCore
Definition qobject.h:90
const QObjectList & children() const
Returns a list of child objects.
Definition qobject.h:171
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:1363
\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:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6180
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6737
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:7956
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
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:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6498
QString toLower() const &
Definition qstring.h:368
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1217
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1164
QString & append(QChar c)
Definition qstring.cpp:3227
QString toUpper() const &
Definition qstring.h:372
qsizetype length() const
Returns the number of characters in this string.
Definition qstring.h:187
\inmodule QtCore
Definition qtimezone.h:25
\inmodule QtCore
Definition qcoreevent.h:359
void setSingleShot(bool singleShot)
Definition qtimer.cpp:580
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3354
\inmodule QtCore
Definition qvariant.h:64
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ AlignVCenter
Definition qnamespace.h:154
@ AlignTrailing
Definition qnamespace.h:146
@ AlignLeft
Definition qnamespace.h:143
Orientation
Definition qnamespace.h:97
@ Horizontal
Definition qnamespace.h:98
@ transparent
Definition qnamespace.h:46
@ TextAlignmentRole
@ DecorationRole
@ EditRole
@ DisplayRole
SortOrder
Definition qnamespace.h:120
CaseSensitivity
@ CaseInsensitive
@ CaseSensitive
DropAction
@ CopyAction
@ MoveAction
@ LinkAction
@ QueuedConnection
@ SkipEmptyParts
Definition qnamespace.h:127
@ ItemNeverHasChildren
@ ItemIsEditable
@ ItemIsDragEnabled
@ ItemIsDropEnabled
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
#define Q_FALLTHROUGH()
std::pair< T1, T2 > QPair
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:162
return ret
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLenum GLsizei GLsizei GLint * values
[15]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLdouble GLdouble GLdouble GLdouble top
GLbitfield GLuint64 timeout
[4]
GLdouble GLdouble right
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
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
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:55
long HRESULT
QFileInfo info(fileName)
[8]
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
bool contains(const AT &t) const noexcept
Definition qlist.h:44
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent