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
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;
1134 const QFileSystemModelSorter ms(column);
1135 std::sort(values.begin(), values.end(), std::cref(ms));
1138 indexNode->visibleChildren.clear();
1140 indexNode->dirtyChildrenIndex = -1;
1141 indexNode->visibleChildren.reserve(values.size());
1142 for (QFileSystemNode *node : std::as_const(values)) {
1143 indexNode->visibleChildren.append(node->fileName);
1144 node->isVisible =
true;
1147 if (!disableRecursiveSort) {
1148 for (
int i = 0; i < q->rowCount(parent); ++i) {
1149 const QModelIndex childIndex = q->index(i, 0, parent);
1150 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(childIndex);
1152 if (indexNode->isVisible)
1153 sortChildren(column, childIndex);
1161void QFileSystemModel::sort(
int column, Qt::SortOrder order)
1163 Q_D(QFileSystemModel);
1164 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
1167 emit layoutAboutToBeChanged();
1168 QModelIndexList oldList = persistentIndexList();
1169 QList<std::pair<QFileSystemModelPrivate::QFileSystemNode *,
int>> oldNodes;
1170 oldNodes.reserve(oldList.size());
1171 for (
const QModelIndex &oldNode : oldList)
1172 oldNodes.emplace_back(d->node(oldNode), oldNode.column());
1174 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
1176 d->sortChildren(column, index(rootPath()));
1177 d->sortColumn = column;
1178 d->forceSort =
false;
1180 d->sortOrder = order;
1182 QModelIndexList newList;
1183 newList.reserve(oldNodes.size());
1184 for (
const auto &[node, col]: std::as_const(oldNodes))
1185 newList.append(d->index(node, col));
1187 changePersistentIndexList(oldList, newList);
1188 emit layoutChanged({}, VerticalSortHint);
1228bool QFileSystemModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
1229 int row,
int column,
const QModelIndex &parent)
1233 if (!parent.isValid() || isReadOnly())
1236 bool success =
true;
1237 QString to = filePath(parent) + QDir::separator();
1239 QList<QUrl> urls = data->urls();
1240 QList<QUrl>::const_iterator it = urls.constBegin();
1243 case Qt::CopyAction:
1244 for (; it != urls.constEnd(); ++it) {
1245 QString path = (*it).toLocalFile();
1246 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
1249 case Qt::LinkAction:
1250 for (; it != urls.constEnd(); ++it) {
1251 QString path = (*it).toLocalFile();
1252 success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
1255 case Qt::MoveAction:
1256 for (; it != urls.constEnd(); ++it) {
1257 QString path = (*it).toLocalFile();
1258 success = QFile::rename(path, to + QFileInfo(path).fileName()) && success;
1352void QFileSystemModel::setOptions(Options options)
1354 const Options changed = (options ^ QFileSystemModel::options());
1356 if (changed.testFlag(DontResolveSymlinks))
1357 setResolveSymlinks(!options.testFlag(DontResolveSymlinks));
1359#if QT_CONFIG(filesystemwatcher)
1360 Q_D(QFileSystemModel);
1361 if (changed.testFlag(DontWatchForChanges))
1362 d->fileInfoGatherer->setWatching(!options.testFlag(DontWatchForChanges));
1365 if (changed.testFlag(DontUseCustomDirectoryIcons)) {
1366 if (
auto provider = iconProvider()) {
1367 QAbstractFileIconProvider::Options providerOptions = provider->options();
1368 providerOptions.setFlag(QAbstractFileIconProvider::DontUseCustomDirectoryIcons,
1369 options.testFlag(QFileSystemModel::DontUseCustomDirectoryIcons));
1370 provider->setOptions(providerOptions);
1372 qWarning(
"Setting QFileSystemModel::DontUseCustomDirectoryIcons has no effect when no provider is used");
1398QString QFileSystemModel::filePath(
const QModelIndex &index)
const
1400 Q_D(
const QFileSystemModel);
1401 QString fullPath = d->filePath(index);
1402 QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);
1403 if (dirNode->isSymLink()
1404#if QT_CONFIG(filesystemwatcher)
1405 && d->fileInfoGatherer->resolveSymlinks()
1407 && d->resolvedSymLinks.contains(fullPath)
1408 && dirNode->isDir()) {
1409 QFileInfo fullPathInfo(dirNode->fileInfo());
1410 if (!dirNode->hasInformation())
1411 fullPathInfo = QFileInfo(fullPath);
1412 QString canonicalPath = fullPathInfo.canonicalFilePath();
1413 auto *canonicalNode = d->node(fullPathInfo.canonicalFilePath(),
false);
1414 QFileInfo resolvedInfo = canonicalNode->fileInfo();
1415 if (!canonicalNode->hasInformation())
1416 resolvedInfo = QFileInfo(canonicalPath);
1417 if (resolvedInfo.exists())
1418 return resolvedInfo.filePath();
1504QModelIndex QFileSystemModel::setRootPath(
const QString &newPath)
1506 Q_D(QFileSystemModel);
1509 QString longNewPath = qt_GetLongPathName(newPath);
1511 QString longNewPath = QDir::fromNativeSeparators(newPath);
1514 QString longNewPath = newPath;
1517 if (!newPath.isEmpty())
1518 longNewPath = QDir::cleanPath(longNewPath);
1520 d->setRootPath =
true;
1523 if (!newPath.isEmpty() && longNewPath.isEmpty())
1524 return d->index(rootPath());
1526 if (d->rootDir.path() == longNewPath)
1527 return d->index(rootPath());
1529 auto node = d->node(longNewPath);
1530 QFileInfo newPathInfo;
1531 if (node && node->hasInformation())
1532 newPathInfo = node->fileInfo();
1534 newPathInfo = QFileInfo(longNewPath);
1536 bool showDrives = (longNewPath.isEmpty() || longNewPath == QFileSystemModelPrivate::myComputer());
1537 if (!showDrives && !newPathInfo.exists())
1538 return d->index(rootPath());
1541 if (!rootPath().isEmpty() && rootPath() !=
"."_L1) {
1543#if QT_CONFIG(filesystemwatcher)
1544 d->fileInfoGatherer->removePath(rootPath());
1549 d->node(rootPath())->populatedChildren =
false;
1553 d->rootDir = QDir(longNewPath);
1554 QModelIndex newRootIndex;
1557 d->rootDir.setPath(
""_L1);
1559 newRootIndex = d->index(d->rootDir.path());
1561 fetchMore(newRootIndex);
1562 emit rootPathChanged(longNewPath);
1563 d->forceSort =
true;
1565 return newRootIndex;
1812void QFileSystemModelPrivate::directoryChanged(
const QString &directory,
const QStringList &files)
1814 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory,
false);
1815 if (parentNode->children.size() == 0)
1817 QStringList toRemove;
1818 QStringList newFiles = files;
1819 std::sort(newFiles.begin(), newFiles.end());
1820 for (
auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) {
1821 QStringList::iterator iterator = std::lower_bound(newFiles.begin(), newFiles.end(), i.value()->fileName);
1822 if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator))
1823 toRemove.append(i.value()->fileName);
1825 for (
int i = 0 ; i < toRemove.size() ; ++i )
1826 removeNode(parentNode, toRemove[i]);
1884void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode,
const QString& name)
1886 Q_Q(QFileSystemModel);
1887 QModelIndex parent = index(parentNode);
1888 bool indexHidden = isHiddenByFilter(parentNode, parent);
1890 int vLocation = parentNode->visibleLocation(name);
1891 if (vLocation >= 0 && !indexHidden)
1892 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1893 translateVisibleLocation(parentNode, vLocation));
1894 QFileSystemNode * node = parentNode->children.take(name);
1897 if (vLocation >= 0) {
1898 parentNode->visibleChildren.removeAt(vLocation);
1899 if (vLocation < parentNode->dirtyChildrenIndex)
1900 --parentNode->dirtyChildrenIndex;
1902 if (vLocation >= 0 && !indexHidden)
1965void QFileSystemModelPrivate::fileSystemChanged(
const QString &path,
1966 const QList<std::pair<QString, QFileInfo>> &updates)
1968#if QT_CONFIG(filesystemwatcher)
1969 Q_Q(QFileSystemModel);
1970 QList<QString> rowsToUpdate;
1971 QStringList newFiles;
1972 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path,
false);
1973 QModelIndex parentIndex = index(parentNode);
1974 for (
const auto &update : updates) {
1975 QString fileName = update.first;
1976 Q_ASSERT(!fileName.isEmpty());
1977 QExtendedInformation info = fileInfoGatherer->getInfo(update.second);
1978 bool previouslyHere = parentNode->children.contains(fileName);
1979 if (!previouslyHere) {
1981 chopSpaceAndDot(fileName);
1982 if (fileName.isEmpty())
1985 addNode(parentNode, fileName, info.fileInfo());
1987 QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName);
1988 bool isCaseSensitive = parentNode->caseSensitive();
1989 if (isCaseSensitive) {
1990 if (node->fileName != fileName)
1993 if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0)
1996 if (isCaseSensitive) {
1997 Q_ASSERT(node->fileName == fileName);
1999 node->fileName = fileName;
2002 if (*node != info ) {
2003 node->populate(info);
2004 bypassFilters.remove(node);
2006 if (filtersAcceptsNode(node)) {
2007 if (!node->isVisible) {
2008 newFiles.append(fileName);
2010 rowsToUpdate.append(fileName);
2013 if (node->isVisible) {
2014 int visibleLocation = parentNode->visibleLocation(fileName);
2015 removeVisibleFile(parentNode, visibleLocation);
2024 std::sort(rowsToUpdate.begin(), rowsToUpdate.end());
2027 for (
const QString &value : std::as_const(rowsToUpdate)) {
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2042 int visibleMin = parentNode->visibleLocation(min);
2043 int visibleMax = parentNode->visibleLocation(max);
2045 && visibleMin < parentNode->visibleChildren.size()
2046 && parentNode->visibleChildren.at(visibleMin) == min
2047 && visibleMax >= 0) {
2049 const int lastColumn = q->columnCount(parentIndex) - 1;
2050 const QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMin),
2051 QFileSystemModelPrivate::NameColumn, parentIndex);
2052 const QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMax),
2053 lastColumn, parentIndex);
2056 Q_ASSERT(bottom.parent() == top.parent());
2057 emit q->dataChanged(top, bottom);
2061
2064 if (newFiles.size() > 0) {
2065 addVisibleFiles(parentNode, newFiles);
2068 if (newFiles.size() > 0 || (sortColumn != 0 && rowsToUpdate.size() > 0)) {
2152void QFileSystemModelPrivate::init()
2154 delayedSortTimer.setSingleShot(
true);
2156 qRegisterMetaType<QList<std::pair<QString, QFileInfo>>>();
2157#if QT_CONFIG(filesystemwatcher)
2158 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::newListOfFiles,
2159 this, &QFileSystemModelPrivate::directoryChanged);
2160 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::updates,
2161 this, &QFileSystemModelPrivate::fileSystemChanged);
2162 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::nameResolved,
2163 this, &QFileSystemModelPrivate::resolvedName);
2164 Q_Q(QFileSystemModel);
2165 q->connect(fileInfoGatherer.get(), &QFileInfoGatherer::directoryLoaded,
2166 q, &QFileSystemModel::directoryLoaded);
2168 QObjectPrivate::connect(&delayedSortTimer, &QTimer::timeout,
2169 this, &QFileSystemModelPrivate::performDelayedSort,
2170 Qt::QueuedConnection);
2181bool QFileSystemModelPrivate::filtersAcceptsNode(
const QFileSystemNode *node)
const
2186 const bool hideDirs = (filters & (QDir::Dirs | QDir::AllDirs)) == 0;
2187 const bool shouldHideDirNode = hideDirs && node->isDir();
2190 if (node->parent == &root || (!shouldHideDirNode && bypassFilters.contains(node)))
2194 if (!node->hasInformation())
2197 const bool filterPermissions = ((filters & QDir::PermissionMask)
2198 && (filters & QDir::PermissionMask) != QDir::PermissionMask);
2199 const bool hideFiles = !(filters & QDir::Files);
2200 const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
2201 const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
2202 const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
2203 const bool hideHidden = !(filters & QDir::Hidden);
2204 const bool hideSystem = !(filters & QDir::System);
2205 const bool hideSymlinks = (filters & QDir::NoSymLinks);
2206 const bool hideDot = (filters & QDir::NoDot);
2207 const bool hideDotDot = (filters & QDir::NoDotDot);
2210 bool isDot = (node->fileName ==
"."_L1);
2211 bool isDotDot = (node->fileName ==
".."_L1);
2212 if ( (hideHidden && !(isDot || isDotDot) && node->isHidden())
2213 || (hideSystem && node->isSystem())
2214 || (hideDirs && node->isDir())
2215 || (hideFiles && node->isFile())
2216 || (hideSymlinks && node->isSymLink())
2217 || (hideReadable && node->isReadable())
2218 || (hideWritable && node->isWritable())
2219 || (hideExecutable && node->isExecutable())
2220 || (hideDot && isDot)
2221 || (hideDotDot && isDotDot))
2224 return nameFilterDisables || passNameFilters(node);