339QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(
const QString &path,
bool fetch)
const
341 Q_Q(
const QFileSystemModel);
343 if (path.isEmpty() || path == myComputer() || path.startsWith(u':'))
344 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
347 QString absolutePath;
349 QString longPath = qt_GetLongPathName(path);
351 QString longPath = path;
353 if (longPath == rootDir.path())
354 absolutePath = rootDir.absolutePath();
356 absolutePath = QDir(longPath).absolutePath();
359 QStringList pathElements = absolutePath.split(u'/', Qt::SkipEmptyParts);
360 if ((pathElements.isEmpty())
361#if !defined(Q_OS_WIN)
362 && QDir::fromNativeSeparators(longPath) !=
"/"_L1
365 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
366 QModelIndex index = QModelIndex();
368 QChar separator = u'/';
369 QString trailingSeparator;
371 if (absolutePath.startsWith(
"//"_L1)) {
372 QString host =
"\\\\"_L1 + pathElements.constFirst();
373 if (absolutePath == QDir::fromNativeSeparators(host))
374 absolutePath.append(u'/');
375 if (longPath.endsWith(u'/') && !absolutePath.endsWith(u'/'))
376 absolutePath.append(u'/');
377 if (absolutePath.endsWith(u'/'))
378 trailingSeparator =
"\\"_L1;
380 auto rootNode =
const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
381 auto it = root.children.constFind(host);
382 if (it != root.children.cend()) {
385 if (pathElements.count() == 1 && !absolutePath.endsWith(u'/'))
387 QFileInfo info(host);
390 QFileSystemModelPrivate *p =
const_cast<QFileSystemModelPrivate*>(
this);
391 p->addNode(rootNode, host,info);
392 p->addVisibleFiles(rootNode, QStringList(host));
394 r = rootNode->visibleLocation(host);
395 r = translateVisibleLocation(rootNode, r);
396 index = q->index(r, 0, QModelIndex());
397 pathElements.pop_front();
400 elementPath.append(separator);
402 if (!pathElements.at(0).contains(u':')) {
403 QString rootPath = QDir(longPath).rootPath();
404 pathElements.prepend(rootPath);
409 if (absolutePath[0] == u'/')
410 pathElements.prepend(
"/"_L1);
413 QFileSystemModelPrivate::QFileSystemNode *parent = node(index);
415 for (
int i = 0; i < pathElements.size(); ++i) {
416 QString element = pathElements.at(i);
418 elementPath.append(separator);
419 elementPath.append(element);
420 if (i == pathElements.size() - 1)
421 elementPath.append(trailingSeparator);
426 chopSpaceAndDot(element);
428 if (element.isEmpty())
431 bool alreadyExisted = parent->children.contains(element);
435 if (alreadyExisted) {
436 if ((parent->children.size() == 0)
437 || (parent->caseSensitive()
438 && parent->children.value(element)->fileName != element)
439 || (!parent->caseSensitive()
440 && parent->children.value(element)->fileName.toLower() != element.toLower()))
441 alreadyExisted =
false;
444 QFileSystemModelPrivate::QFileSystemNode *node;
445 if (!alreadyExisted) {
449 if (elementPath.length() == 2 && elementPath.at(0).isLetter()
450 && elementPath.at(1) == u':') {
451 elementPath.append(u'/');
456 QFileInfo info(elementPath);
458 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
459 QFileSystemModelPrivate *p =
const_cast<QFileSystemModelPrivate*>(
this);
460 node = p->addNode(parent, element,info);
461#if QT_CONFIG(filesystemwatcher)
462 node->populate(fileInfoGatherer->getInfo(info));
465 node = parent->children.value(element);
469 if (!node->isVisible) {
471 if (alreadyExisted && node->hasInformation() && !fetch)
472 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
474 QFileSystemModelPrivate *p =
const_cast<QFileSystemModelPrivate*>(
this);
475 p->addVisibleFiles(parent, QStringList(element));
476 if (!p->bypassFilters.contains(node))
477 p->bypassFilters[node] = 1;
478 QString dir = q->filePath(
this->index(parent));
479 if (!node->hasInformation() && fetch) {
480 Fetching f = { std::move(dir), std::move(element), node };
481 p->toFetch.append(std::move(f));
482 p->fetchingTimer.start(0,
const_cast<QFileSystemModel*>(q));
728QVariant QFileSystemModel::data(
const QModelIndex &index,
int role)
const
730 Q_D(
const QFileSystemModel);
731 if (!index.isValid() || index.model() !=
this)
736 if (index.column() == QFileSystemModelPrivate::NameColumn)
737 return d->name(index);
739 case Qt::DisplayRole:
740 switch (index.column()) {
741 case QFileSystemModelPrivate::NameColumn:
return d->displayName(index);
742 case QFileSystemModelPrivate::SizeColumn:
return d->size(index);
743 case QFileSystemModelPrivate::TypeColumn:
return d->type(index);
744 case QFileSystemModelPrivate::TimeColumn:
return d->time(index);
746 qWarning(
"data: invalid display value column %d", index.column());
751 return filePath(index);
753 return d->name(index);
755 return QVariant::fromValue(fileInfo(index));
756 case Qt::DecorationRole:
757 if (index.column() == QFileSystemModelPrivate::NameColumn) {
758 QIcon icon = d->icon(index);
759#if QT_CONFIG(filesystemwatcher)
761 using P = QAbstractFileIconProvider;
762 if (
auto *provider = d->fileInfoGatherer->iconProvider())
763 icon = provider->icon(d->node(index)->isDir() ? P::Folder: P::File);
769 case Qt::TextAlignmentRole:
770 if (index.column() == QFileSystemModelPrivate::SizeColumn)
771 return QVariant(Qt::AlignTrailing | Qt::AlignVCenter);
773 case FilePermissions:
774 int p = permissions(index);
878bool QFileSystemModel::setData(
const QModelIndex &idx,
const QVariant &value,
int role)
880 Q_D(QFileSystemModel);
883 || role != Qt::EditRole
884 || (flags(idx) & Qt::ItemIsEditable) == 0) {
888 QString newName = value.toString();
890 chopSpaceAndDot(newName);
891 if (newName.isEmpty())
895 QString oldName = idx.data().toString();
896 if (newName == oldName)
899 const QString parentPath = filePath(parent(idx));
901 if (newName.isEmpty() || QDir::toNativeSeparators(newName).contains(QDir::separator()))
904#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
907 const QStringList watchedPaths = d->unwatchPathsAt(idx);
909 if (!QDir(parentPath).rename(oldName, newName)) {
910#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
911 d->watchPaths(watchedPaths);
916
917
918
919
920
921
922
923
925 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx);
926 QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
927 int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName);
929 parentNode->visibleChildren.removeAt(visibleLocation);
930 std::unique_ptr<QFileSystemModelPrivate::QFileSystemNode> nodeToRename(parentNode->children.take(oldName));
931 nodeToRename->fileName = newName;
932 nodeToRename->parent = parentNode;
933#if QT_CONFIG(filesystemwatcher)
934 nodeToRename->populate(d->fileInfoGatherer->getInfo(QFileInfo(parentPath, newName)));
936 nodeToRename->isVisible =
true;
937 parentNode->children[newName] = nodeToRename.release();
938 parentNode->visibleChildren.insert(visibleLocation, newName);
941 emit fileRenamed(parentPath, oldName, newName);
949QVariant QFileSystemModel::headerData(
int section, Qt::Orientation orientation,
int role)
const
952 case Qt::DecorationRole:
956 QImage pixmap(16, 1, QImage::Format_ARGB32_Premultiplied);
957 pixmap.fill(Qt::transparent);
961 case Qt::TextAlignmentRole:
962 return Qt::AlignLeft;
965 if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
966 return QAbstractItemModel::headerData(section, orientation, role);
970 case QFileSystemModelPrivate::NameColumn:
971 returnValue = tr(
"Name");
973 case QFileSystemModelPrivate::SizeColumn:
974 returnValue = tr(
"Size");
976 case QFileSystemModelPrivate::TypeColumn:
979 tr(
"Kind",
"Match OS X Finder");
981 tr(
"Type",
"All other platforms");
988 case QFileSystemModelPrivate::TimeColumn:
989 returnValue = tr(
"Date Modified");
991 default:
return QVariant();
1117void QFileSystemModelPrivate::sortChildren(
int column,
const QModelIndex &parent)
1119 Q_Q(QFileSystemModel);
1120 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent);
1121 if (indexNode->children.size() == 0)
1124 QList<QFileSystemModelPrivate::QFileSystemNode *> values;
1126 for (
auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) {
1127 if (filtersAcceptsNode(iterator.value())) {
1128 values.append(iterator.value());
1130 iterator.value()->isVisible =
false;
1133 QFileSystemModelSorter ms(column);
1134 std::sort(values.begin(), values.end(), ms);
1136 indexNode->visibleChildren.clear();
1138 indexNode->dirtyChildrenIndex = -1;
1139 indexNode->visibleChildren.reserve(values.size());
1140 for (QFileSystemNode *node : std::as_const(values)) {
1141 indexNode->visibleChildren.append(node->fileName);
1142 node->isVisible =
true;
1145 if (!disableRecursiveSort) {
1146 for (
int i = 0; i < q->rowCount(parent); ++i) {
1147 const QModelIndex childIndex = q->index(i, 0, parent);
1148 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(childIndex);
1150 if (indexNode->isVisible)
1151 sortChildren(column, childIndex);
1159void QFileSystemModel::sort(
int column, Qt::SortOrder order)
1161 Q_D(QFileSystemModel);
1162 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
1165 emit layoutAboutToBeChanged();
1166 QModelIndexList oldList = persistentIndexList();
1167 QList<std::pair<QFileSystemModelPrivate::QFileSystemNode *,
int>> oldNodes;
1168 oldNodes.reserve(oldList.size());
1169 for (
const QModelIndex &oldNode : oldList)
1170 oldNodes.emplace_back(d->node(oldNode), oldNode.column());
1172 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
1174 d->sortChildren(column, index(rootPath()));
1175 d->sortColumn = column;
1176 d->forceSort =
false;
1178 d->sortOrder = order;
1180 QModelIndexList newList;
1181 newList.reserve(oldNodes.size());
1182 for (
const auto &[node, col]: std::as_const(oldNodes))
1183 newList.append(d->index(node, col));
1185 changePersistentIndexList(oldList, newList);
1186 emit layoutChanged({}, VerticalSortHint);
1226bool QFileSystemModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
1227 int row,
int column,
const QModelIndex &parent)
1231 if (!parent.isValid() || isReadOnly())
1234 bool success =
true;
1235 QString to = filePath(parent) + QDir::separator();
1237 QList<QUrl> urls = data->urls();
1238 QList<QUrl>::const_iterator it = urls.constBegin();
1241 case Qt::CopyAction:
1242 for (; it != urls.constEnd(); ++it) {
1243 QString path = (*it).toLocalFile();
1244 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
1247 case Qt::LinkAction:
1248 for (; it != urls.constEnd(); ++it) {
1249 QString path = (*it).toLocalFile();
1250 success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
1253 case Qt::MoveAction:
1254 for (; it != urls.constEnd(); ++it) {
1255 QString path = (*it).toLocalFile();
1256 success = QFile::rename(path, to + QFileInfo(path).fileName()) && success;
1350void QFileSystemModel::setOptions(Options options)
1352 const Options changed = (options ^ QFileSystemModel::options());
1354 if (changed.testFlag(DontResolveSymlinks))
1355 setResolveSymlinks(!options.testFlag(DontResolveSymlinks));
1357#if QT_CONFIG(filesystemwatcher)
1358 Q_D(QFileSystemModel);
1359 if (changed.testFlag(DontWatchForChanges))
1360 d->fileInfoGatherer->setWatching(!options.testFlag(DontWatchForChanges));
1363 if (changed.testFlag(DontUseCustomDirectoryIcons)) {
1364 if (
auto provider = iconProvider()) {
1365 QAbstractFileIconProvider::Options providerOptions = provider->options();
1366 providerOptions.setFlag(QAbstractFileIconProvider::DontUseCustomDirectoryIcons,
1367 options.testFlag(QFileSystemModel::DontUseCustomDirectoryIcons));
1368 provider->setOptions(providerOptions);
1370 qWarning(
"Setting QFileSystemModel::DontUseCustomDirectoryIcons has no effect when no provider is used");
1396QString QFileSystemModel::filePath(
const QModelIndex &index)
const
1398 Q_D(
const QFileSystemModel);
1399 QString fullPath = d->filePath(index);
1400 QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);
1401 if (dirNode->isSymLink()
1402#if QT_CONFIG(filesystemwatcher)
1403 && d->fileInfoGatherer->resolveSymlinks()
1405 && d->resolvedSymLinks.contains(fullPath)
1406 && dirNode->isDir()) {
1407 QFileInfo fullPathInfo(dirNode->fileInfo());
1408 if (!dirNode->hasInformation())
1409 fullPathInfo = QFileInfo(fullPath);
1410 QString canonicalPath = fullPathInfo.canonicalFilePath();
1411 auto *canonicalNode = d->node(fullPathInfo.canonicalFilePath(),
false);
1412 QFileInfo resolvedInfo = canonicalNode->fileInfo();
1413 if (!canonicalNode->hasInformation())
1414 resolvedInfo = QFileInfo(canonicalPath);
1415 if (resolvedInfo.exists())
1416 return resolvedInfo.filePath();
1502QModelIndex QFileSystemModel::setRootPath(
const QString &newPath)
1504 Q_D(QFileSystemModel);
1507 QString longNewPath = qt_GetLongPathName(newPath);
1509 QString longNewPath = QDir::fromNativeSeparators(newPath);
1512 QString longNewPath = newPath;
1515 if (!newPath.isEmpty())
1516 longNewPath = QDir::cleanPath(longNewPath);
1518 d->setRootPath =
true;
1521 if (!newPath.isEmpty() && longNewPath.isEmpty())
1522 return d->index(rootPath());
1524 if (d->rootDir.path() == longNewPath)
1525 return d->index(rootPath());
1527 auto node = d->node(longNewPath);
1528 QFileInfo newPathInfo;
1529 if (node && node->hasInformation())
1530 newPathInfo = node->fileInfo();
1532 newPathInfo = QFileInfo(longNewPath);
1534 bool showDrives = (longNewPath.isEmpty() || longNewPath == QFileSystemModelPrivate::myComputer());
1535 if (!showDrives && !newPathInfo.exists())
1536 return d->index(rootPath());
1539 if (!rootPath().isEmpty() && rootPath() !=
"."_L1) {
1541#if QT_CONFIG(filesystemwatcher)
1542 d->fileInfoGatherer->removePath(rootPath());
1547 d->node(rootPath())->populatedChildren =
false;
1551 d->rootDir = QDir(longNewPath);
1552 QModelIndex newRootIndex;
1555 d->rootDir.setPath(
""_L1);
1557 newRootIndex = d->index(d->rootDir.path());
1559 fetchMore(newRootIndex);
1560 emit rootPathChanged(longNewPath);
1561 d->forceSort =
true;
1563 return newRootIndex;
1807void QFileSystemModelPrivate::directoryChanged(
const QString &directory,
const QStringList &files)
1809 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory,
false);
1810 if (parentNode->children.size() == 0)
1812 QStringList toRemove;
1813 QStringList newFiles = files;
1814 std::sort(newFiles.begin(), newFiles.end());
1815 for (
auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) {
1816 QStringList::iterator iterator = std::lower_bound(newFiles.begin(), newFiles.end(), i.value()->fileName);
1817 if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator))
1818 toRemove.append(i.value()->fileName);
1820 for (
int i = 0 ; i < toRemove.size() ; ++i )
1821 removeNode(parentNode, toRemove[i]);
1879void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode,
const QString& name)
1881 Q_Q(QFileSystemModel);
1882 QModelIndex parent = index(parentNode);
1883 bool indexHidden = isHiddenByFilter(parentNode, parent);
1885 int vLocation = parentNode->visibleLocation(name);
1886 if (vLocation >= 0 && !indexHidden)
1887 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1888 translateVisibleLocation(parentNode, vLocation));
1889 QFileSystemNode * node = parentNode->children.take(name);
1893 parentNode->visibleChildren.removeAt(vLocation);
1894 if (vLocation >= 0 && !indexHidden)
1955void QFileSystemModelPrivate::fileSystemChanged(
const QString &path,
1956 const QList<std::pair<QString, QFileInfo>> &updates)
1958#if QT_CONFIG(filesystemwatcher)
1959 Q_Q(QFileSystemModel);
1960 QList<QString> rowsToUpdate;
1961 QStringList newFiles;
1962 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path,
false);
1963 QModelIndex parentIndex = index(parentNode);
1964 for (
const auto &update : updates) {
1965 QString fileName = update.first;
1966 Q_ASSERT(!fileName.isEmpty());
1967 QExtendedInformation info = fileInfoGatherer->getInfo(update.second);
1968 bool previouslyHere = parentNode->children.contains(fileName);
1969 if (!previouslyHere) {
1971 chopSpaceAndDot(fileName);
1972 if (fileName.isEmpty())
1975 addNode(parentNode, fileName, info.fileInfo());
1977 QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName);
1978 bool isCaseSensitive = parentNode->caseSensitive();
1979 if (isCaseSensitive) {
1980 if (node->fileName != fileName)
1983 if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0)
1986 if (isCaseSensitive) {
1987 Q_ASSERT(node->fileName == fileName);
1989 node->fileName = fileName;
1992 if (*node != info ) {
1993 node->populate(info);
1994 bypassFilters.remove(node);
1996 if (filtersAcceptsNode(node)) {
1997 if (!node->isVisible) {
1998 newFiles.append(fileName);
2000 rowsToUpdate.append(fileName);
2003 if (node->isVisible) {
2004 int visibleLocation = parentNode->visibleLocation(fileName);
2005 removeVisibleFile(parentNode, visibleLocation);
2014 std::sort(rowsToUpdate.begin(), rowsToUpdate.end());
2017 for (
const QString &value : std::as_const(rowsToUpdate)) {
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2032 int visibleMin = parentNode->visibleLocation(min);
2033 int visibleMax = parentNode->visibleLocation(max);
2035 && visibleMin < parentNode->visibleChildren.size()
2036 && parentNode->visibleChildren.at(visibleMin) == min
2037 && visibleMax >= 0) {
2039 const int lastColumn = q->columnCount(parentIndex) - 1;
2040 const QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMin),
2041 QFileSystemModelPrivate::NameColumn, parentIndex);
2042 const QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMax),
2043 lastColumn, parentIndex);
2046 Q_ASSERT(bottom.parent() == top.parent());
2047 emit q->dataChanged(top, bottom);
2051
2054 if (newFiles.size() > 0) {
2055 addVisibleFiles(parentNode, newFiles);
2058 if (newFiles.size() > 0 || (sortColumn != 0 && rowsToUpdate.size() > 0)) {
2142void QFileSystemModelPrivate::init()
2144 delayedSortTimer.setSingleShot(
true);
2146 qRegisterMetaType<QList<std::pair<QString, QFileInfo>>>();
2147#if QT_CONFIG(filesystemwatcher)
2148 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::newListOfFiles,
2149 this, &QFileSystemModelPrivate::directoryChanged);
2150 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::updates,
2151 this, &QFileSystemModelPrivate::fileSystemChanged);
2152 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::nameResolved,
2153 this, &QFileSystemModelPrivate::resolvedName);
2154 Q_Q(QFileSystemModel);
2155 q->connect(fileInfoGatherer.get(), &QFileInfoGatherer::directoryLoaded,
2156 q, &QFileSystemModel::directoryLoaded);
2158 QObjectPrivate::connect(&delayedSortTimer, &QTimer::timeout,
2159 this, &QFileSystemModelPrivate::performDelayedSort,
2160 Qt::QueuedConnection);
2171bool QFileSystemModelPrivate::filtersAcceptsNode(
const QFileSystemNode *node)
const
2176 const bool hideDirs = (filters & (QDir::Dirs | QDir::AllDirs)) == 0;
2177 const bool shouldHideDirNode = hideDirs && node->isDir();
2180 if (node->parent == &root || (!shouldHideDirNode && bypassFilters.contains(node)))
2184 if (!node->hasInformation())
2187 const bool filterPermissions = ((filters & QDir::PermissionMask)
2188 && (filters & QDir::PermissionMask) != QDir::PermissionMask);
2189 const bool hideFiles = !(filters & QDir::Files);
2190 const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
2191 const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
2192 const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
2193 const bool hideHidden = !(filters & QDir::Hidden);
2194 const bool hideSystem = !(filters & QDir::System);
2195 const bool hideSymlinks = (filters & QDir::NoSymLinks);
2196 const bool hideDot = (filters & QDir::NoDot);
2197 const bool hideDotDot = (filters & QDir::NoDotDot);
2200 bool isDot = (node->fileName ==
"."_L1);
2201 bool isDotDot = (node->fileName ==
".."_L1);
2202 if ( (hideHidden && !(isDot || isDotDot) && node->isHidden())
2203 || (hideSystem && node->isSystem())
2204 || (hideDirs && node->isDir())
2205 || (hideFiles && node->isFile())
2206 || (hideSymlinks && node->isSymLink())
2207 || (hideReadable && node->isReadable())
2208 || (hideWritable && node->isWritable())
2209 || (hideExecutable && node->isExecutable())
2210 || (hideDot && isDot)
2211 || (hideDotDot && isDotDot))
2214 return nameFilterDisables || passNameFilters(node);