340QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(
const QString &path,
bool fetch)
const
342 Q_Q(
const QFileSystemModel);
344 if (path.isEmpty() || path == myComputer() || path.startsWith(u':'))
345 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
348 QString absolutePath;
350 QString longPath = qt_GetLongPathName(path);
352 QString longPath = path;
354 if (longPath == rootDir.path())
355 absolutePath = rootDir.absolutePath();
357 absolutePath = QDir(longPath).absolutePath();
360 QStringList pathElements = absolutePath.split(u'/', Qt::SkipEmptyParts);
361 if ((pathElements.isEmpty())
362#if !defined(Q_OS_WIN)
363 && QDir::fromNativeSeparators(longPath) !=
"/"_L1
366 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
367 QModelIndex index = QModelIndex();
369 QChar separator = u'/';
370 QString trailingSeparator;
372 if (absolutePath.startsWith(
"//"_L1)) {
373 QString host =
"\\\\"_L1 + pathElements.constFirst();
374 if (absolutePath == QDir::fromNativeSeparators(host))
375 absolutePath.append(u'/');
376 if (longPath.endsWith(u'/') && !absolutePath.endsWith(u'/'))
377 absolutePath.append(u'/');
378 if (absolutePath.endsWith(u'/'))
379 trailingSeparator =
"\\"_L1;
381 auto rootNode =
const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
382 auto it = root.children.constFind(host);
383 if (it != root.children.cend()) {
386 if (pathElements.count() == 1 && !absolutePath.endsWith(u'/'))
388 QFileInfo info(host);
391 QFileSystemModelPrivate *p =
const_cast<QFileSystemModelPrivate*>(
this);
392 p->addNode(rootNode, host,info);
393 p->addVisibleFiles(rootNode, QStringList(host));
395 r = rootNode->visibleLocation(host);
396 r = translateVisibleLocation(rootNode, r);
397 index = q->index(r, 0, QModelIndex());
398 pathElements.pop_front();
401 elementPath.append(separator);
403 if (!pathElements.at(0).contains(u':')) {
404 QString rootPath = QDir(longPath).rootPath();
405 pathElements.prepend(rootPath);
410 if (absolutePath[0] == u'/')
411 pathElements.prepend(
"/"_L1);
414 QFileSystemModelPrivate::QFileSystemNode *parent = node(index);
416 for (
int i = 0; i < pathElements.size(); ++i) {
417 QString element = pathElements.at(i);
418 if (i != 0 && !elementPath.endsWith(separator))
419 elementPath.append(separator);
420 elementPath.append(element);
421 if (i == pathElements.size() - 1)
422 elementPath.append(trailingSeparator);
427 chopSpaceAndDot(element);
429 if (element.isEmpty())
432 bool alreadyExisted = parent->children.contains(element);
436 if (alreadyExisted) {
437 if ((parent->children.size() == 0)
438 || (parent->caseSensitive()
439 && parent->children.value(element)->fileName != element)
440 || (!parent->caseSensitive()
441 && parent->children.value(element)->fileName.toLower() != element.toLower()))
442 alreadyExisted =
false;
445 QFileSystemModelPrivate::QFileSystemNode *node;
446 if (!alreadyExisted) {
447 QString ePath = elementPath;
451 if (ePath.length() == 2 && ePath.at(0).isLetter() && ePath.at(1) == u':')
456 QFileInfo info(ePath);
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
951 Q_D(
const QFileSystemModel);
953 case Qt::DecorationRole:
955 if (d->emptySection0Icon.isNull()) {
958 QPixmap pixmap(16, 1);
959 pixmap.fill(Qt::transparent);
961 ico.addPixmap(pixmap);
962 d->emptySection0Icon = QVariant::fromValue(ico);
964 return d->emptySection0Icon;
967 case Qt::TextAlignmentRole:
968 return Qt::AlignLeft;
971 if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
972 return QAbstractItemModel::headerData(section, orientation, role);
976 case QFileSystemModelPrivate::NameColumn:
977 returnValue = tr(
"Name");
979 case QFileSystemModelPrivate::SizeColumn:
980 returnValue = tr(
"Size");
982 case QFileSystemModelPrivate::TypeColumn:
985 tr(
"Kind",
"Match OS X Finder");
987 tr(
"Type",
"All other platforms");
994 case QFileSystemModelPrivate::TimeColumn:
995 returnValue = tr(
"Date Modified");
997 default:
return QVariant();
1123void QFileSystemModelPrivate::sortChildren(
int column,
const QModelIndex &parent)
1125 Q_Q(QFileSystemModel);
1126 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent);
1127 if (indexNode->children.size() == 0)
1130 QList<QFileSystemModelPrivate::QFileSystemNode *> values;
1132 for (
auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) {
1133 if (filtersAcceptsNode(iterator.value())) {
1134 values.append(iterator.value());
1136 iterator.value()->isVisible =
false;
1140 const QFileSystemModelSorter ms(column);
1141 std::sort(values.begin(), values.end(), std::cref(ms));
1144 indexNode->visibleChildren.clear();
1146 indexNode->dirtyChildrenIndex = -1;
1147 indexNode->visibleChildren.reserve(values.size());
1148 for (QFileSystemNode *node : std::as_const(values)) {
1149 indexNode->visibleChildren.append(node->fileName);
1150 node->isVisible =
true;
1153 if (!disableRecursiveSort) {
1154 for (
int i = 0; i < q->rowCount(parent); ++i) {
1155 const QModelIndex childIndex = q->index(i, 0, parent);
1156 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(childIndex);
1158 if (indexNode->isVisible)
1159 sortChildren(column, childIndex);
1167void QFileSystemModel::sort(
int column, Qt::SortOrder order)
1169 Q_D(QFileSystemModel);
1170 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
1173 emit layoutAboutToBeChanged();
1174 const QModelIndexList oldList = persistentIndexList();
1175 std::vector<std::pair<QFileSystemModelPrivate::QFileSystemNode *,
int>> oldNodes;
1176 oldNodes.reserve(oldList.size());
1177 for (
const QModelIndex &oldNode : oldList)
1178 oldNodes.emplace_back(d->node(oldNode), oldNode.column());
1180 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
1182 d->sortChildren(column, index(rootPath()));
1183 d->sortColumn = column;
1184 d->forceSort =
false;
1186 d->sortOrder = order;
1188 QModelIndexList newList;
1189 newList.reserve(oldNodes.size());
1190 for (
const auto &[node, col]: std::as_const(oldNodes))
1191 newList.append(d->index(node, col));
1193 changePersistentIndexList(oldList, newList);
1194 emit layoutChanged({}, VerticalSortHint);
1234bool QFileSystemModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
1235 int row,
int column,
const QModelIndex &parent)
1239 if (!parent.isValid() || isReadOnly())
1242 bool success =
true;
1243 QString to = filePath(parent) + QDir::separator();
1245 QList<QUrl> urls = data->urls();
1246 QList<QUrl>::const_iterator it = urls.constBegin();
1249 case Qt::CopyAction:
1250 for (; it != urls.constEnd(); ++it) {
1251 QString path = (*it).toLocalFile();
1252 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
1255 case Qt::LinkAction:
1256 for (; it != urls.constEnd(); ++it) {
1257 QString path = (*it).toLocalFile();
1258 success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
1261 case Qt::MoveAction:
1262 for (; it != urls.constEnd(); ++it) {
1263 QString path = (*it).toLocalFile();
1264 success = QFile::rename(path, to + QFileInfo(path).fileName()) && success;
1358void QFileSystemModel::setOptions(Options options)
1360 const Options changed = (options ^ QFileSystemModel::options());
1362 if (changed.testFlag(DontResolveSymlinks))
1363 setResolveSymlinks(!options.testFlag(DontResolveSymlinks));
1365#if QT_CONFIG(filesystemwatcher)
1366 Q_D(QFileSystemModel);
1367 if (changed.testFlag(DontWatchForChanges))
1368 d->fileInfoGatherer->setWatching(!options.testFlag(DontWatchForChanges));
1371 if (changed.testFlag(DontUseCustomDirectoryIcons)) {
1372 if (
auto provider = iconProvider()) {
1373 QAbstractFileIconProvider::Options providerOptions = provider->options();
1374 providerOptions.setFlag(QAbstractFileIconProvider::DontUseCustomDirectoryIcons,
1375 options.testFlag(QFileSystemModel::DontUseCustomDirectoryIcons));
1376 provider->setOptions(providerOptions);
1378 qWarning(
"Setting QFileSystemModel::DontUseCustomDirectoryIcons has no effect when no provider is used");
1404QString QFileSystemModel::filePath(
const QModelIndex &index)
const
1406 Q_D(
const QFileSystemModel);
1407 QString fullPath = d->filePath(index);
1408 QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);
1409 if (dirNode->isSymLink()
1410#if QT_CONFIG(filesystemwatcher)
1411 && d->fileInfoGatherer->resolveSymlinks()
1413 && d->resolvedSymLinks.contains(fullPath)
1414 && dirNode->isDir()) {
1415 QFileInfo fullPathInfo(dirNode->fileInfo());
1416 if (!dirNode->hasInformation())
1417 fullPathInfo = QFileInfo(fullPath);
1418 QString canonicalPath = fullPathInfo.canonicalFilePath();
1419 auto *canonicalNode = d->node(fullPathInfo.canonicalFilePath(),
false);
1420 QFileInfo resolvedInfo = canonicalNode->fileInfo();
1421 if (!canonicalNode->hasInformation())
1422 resolvedInfo = QFileInfo(canonicalPath);
1423 if (resolvedInfo.exists())
1424 return resolvedInfo.filePath();
1510QModelIndex QFileSystemModel::setRootPath(
const QString &newPath)
1512 Q_D(QFileSystemModel);
1515 QString longNewPath = qt_GetLongPathName(newPath);
1517 QString longNewPath = QDir::fromNativeSeparators(newPath);
1520 QString longNewPath = newPath;
1523 if (!newPath.isEmpty())
1524 longNewPath = QDir::cleanPath(longNewPath);
1526 d->setRootPath =
true;
1529 if (!newPath.isEmpty() && longNewPath.isEmpty())
1530 return d->index(rootPath());
1532 if (d->rootDir.path() == longNewPath)
1533 return d->index(rootPath());
1535 auto node = d->node(longNewPath);
1536 QFileInfo newPathInfo;
1537 if (node && node->hasInformation())
1538 newPathInfo = node->fileInfo();
1540 newPathInfo = QFileInfo(longNewPath);
1542 bool showDrives = (longNewPath.isEmpty() || longNewPath == QFileSystemModelPrivate::myComputer());
1543 if (!showDrives && !newPathInfo.exists())
1544 return d->index(rootPath());
1547 if (!rootPath().isEmpty() && rootPath() !=
"."_L1) {
1549#if QT_CONFIG(filesystemwatcher)
1550 d->fileInfoGatherer->removePath(rootPath());
1555 d->node(rootPath())->populatedChildren =
false;
1559 d->rootDir = QDir(longNewPath);
1560 QModelIndex newRootIndex;
1563 d->rootDir.setPath(
""_L1);
1565 newRootIndex = d->index(d->rootDir.path());
1567 fetchMore(newRootIndex);
1568 emit rootPathChanged(longNewPath);
1569 d->forceSort =
true;
1571 return newRootIndex;
1818void QFileSystemModelPrivate::directoryChanged(
const QString &directory,
const QStringList &files)
1820 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory,
false);
1821 if (parentNode->children.size() == 0)
1823 QStringList toRemove;
1824 QStringList newFiles = files;
1825 std::sort(newFiles.begin(), newFiles.end());
1826 for (
auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) {
1827 QStringList::iterator iterator = std::lower_bound(newFiles.begin(), newFiles.end(), i.value()->fileName);
1828 if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator))
1829 toRemove.append(i.value()->fileName);
1831 for (
int i = 0 ; i < toRemove.size() ; ++i )
1832 removeNode(parentNode, toRemove[i]);
1890void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode,
const QString& name)
1892 Q_Q(QFileSystemModel);
1893 QModelIndex parent = index(parentNode);
1894 bool indexHidden = isHiddenByFilter(parentNode, parent);
1896 int vLocation = parentNode->visibleLocation(name);
1897 if (vLocation >= 0 && !indexHidden)
1898 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1899 translateVisibleLocation(parentNode, vLocation));
1900 QFileSystemNode * node = parentNode->children.take(name);
1903 if (vLocation >= 0) {
1904 parentNode->visibleChildren.removeAt(vLocation);
1905 if (vLocation < parentNode->dirtyChildrenIndex)
1906 --parentNode->dirtyChildrenIndex;
1908 if (vLocation >= 0 && !indexHidden)
1971void QFileSystemModelPrivate::fileSystemChanged(
const QString &path,
1972 const QList<std::pair<QString, QFileInfo>> &updates)
1974#if QT_CONFIG(filesystemwatcher)
1975 Q_Q(QFileSystemModel);
1976 QList<QString> rowsToUpdate;
1977 QStringList newFiles;
1978 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path,
false);
1979 QModelIndex parentIndex = index(parentNode);
1980 for (
const auto &update : updates) {
1981 QString fileName = update.first;
1982 Q_ASSERT(!fileName.isEmpty());
1983 QExtendedInformation info = fileInfoGatherer->getInfo(update.second);
1984 bool previouslyHere = parentNode->children.contains(fileName);
1985 if (!previouslyHere) {
1987 chopSpaceAndDot(fileName);
1988 if (fileName.isEmpty())
1991 addNode(parentNode, fileName, info.fileInfo());
1993 QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName);
1994 bool isCaseSensitive = parentNode->caseSensitive();
1995 if (isCaseSensitive) {
1996 if (node->fileName != fileName)
1999 if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0)
2002 if (isCaseSensitive) {
2003 Q_ASSERT(node->fileName == fileName);
2005 node->fileName = fileName;
2008 if (*node != info ) {
2009 node->populate(info);
2010 bypassFilters.remove(node);
2012 if (filtersAcceptsNode(node)) {
2013 if (!node->isVisible) {
2014 newFiles.append(fileName);
2016 rowsToUpdate.append(fileName);
2019 if (node->isVisible) {
2020 int visibleLocation = parentNode->visibleLocation(fileName);
2021 removeVisibleFile(parentNode, visibleLocation);
2030 std::sort(rowsToUpdate.begin(), rowsToUpdate.end());
2033 for (
const QString &value : std::as_const(rowsToUpdate)) {
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2048 int visibleMin = parentNode->visibleLocation(min);
2049 int visibleMax = parentNode->visibleLocation(max);
2051 && visibleMin < parentNode->visibleChildren.size()
2052 && parentNode->visibleChildren.at(visibleMin) == min
2053 && visibleMax >= 0) {
2055 const int lastColumn = q->columnCount(parentIndex) - 1;
2056 const QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMin),
2057 QFileSystemModelPrivate::NameColumn, parentIndex);
2058 const QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMax),
2059 lastColumn, parentIndex);
2062 Q_ASSERT(bottom.parent() == top.parent());
2063 emit q->dataChanged(top, bottom);
2067
2070 if (newFiles.size() > 0) {
2071 addVisibleFiles(parentNode, newFiles);
2074 if (newFiles.size() > 0 || (sortColumn != 0 && rowsToUpdate.size() > 0)) {
2158void QFileSystemModelPrivate::init()
2160 delayedSortTimer.setSingleShot(
true);
2162 qRegisterMetaType<QList<std::pair<QString, QFileInfo>>>();
2163#if QT_CONFIG(filesystemwatcher)
2164 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::newListOfFiles,
2165 this, &QFileSystemModelPrivate::directoryChanged);
2166 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::updates,
2167 this, &QFileSystemModelPrivate::fileSystemChanged);
2168 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::nameResolved,
2169 this, &QFileSystemModelPrivate::resolvedName);
2170 Q_Q(QFileSystemModel);
2171 q->connect(fileInfoGatherer.get(), &QFileInfoGatherer::directoryLoaded,
2172 q, &QFileSystemModel::directoryLoaded);
2174 QObjectPrivate::connect(&delayedSortTimer, &QTimer::timeout,
2175 this, &QFileSystemModelPrivate::performDelayedSort,
2176 Qt::QueuedConnection);
2187bool QFileSystemModelPrivate::filtersAcceptsNode(
const QFileSystemNode *node)
const
2192 const bool hideDirs = (filters & (QDir::Dirs | QDir::AllDirs)) == 0;
2193 const bool shouldHideDirNode = hideDirs && node->isDir();
2196 if (node->parent == &root || (!shouldHideDirNode && bypassFilters.contains(node)))
2200 if (!node->hasInformation())
2203 const bool filterPermissions = ((filters & QDir::PermissionMask)
2204 && (filters & QDir::PermissionMask) != QDir::PermissionMask);
2205 const bool hideFiles = !(filters & QDir::Files);
2206 const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
2207 const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
2208 const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
2209 const bool hideHidden = !(filters & QDir::Hidden);
2210 const bool hideSystem = !(filters & QDir::System);
2211 const bool hideSymlinks = (filters & QDir::NoSymLinks);
2212 const bool hideDot = (filters & QDir::NoDot);
2213 const bool hideDotDot = (filters & QDir::NoDotDot);
2216 bool isDot = (node->fileName ==
"."_L1);
2217 bool isDotDot = (node->fileName ==
".."_L1);
2218 if ( (hideHidden && !(isDot || isDotDot) && node->isHidden())
2219 || (hideSystem && node->isSystem())
2220 || (hideDirs && node->isDir())
2221 || (hideFiles && node->isFile())
2222 || (hideSymlinks && node->isSymLink())
2223 || (hideReadable && node->isReadable())
2224 || (hideWritable && node->isWritable())
2225 || (hideExecutable && node->isExecutable())
2226 || (hideDot && isDot)
2227 || (hideDotDot && isDotDot))
2230 return nameFilterDisables || passNameFilters(node);