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);
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) {
450 if (elementPath.length() == 2 && elementPath.at(0).isLetter()
451 && elementPath.at(1) == u':') {
452 elementPath.append(u'/');
457 QFileInfo info(elementPath);
459 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
460 QFileSystemModelPrivate *p =
const_cast<QFileSystemModelPrivate*>(
this);
461 node = p->addNode(parent, element,info);
462#if QT_CONFIG(filesystemwatcher)
463 node->populate(fileInfoGatherer->getInfo(info));
466 node = parent->children.value(element);
470 if (!node->isVisible) {
472 if (alreadyExisted && node->hasInformation() && !fetch)
473 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
475 QFileSystemModelPrivate *p =
const_cast<QFileSystemModelPrivate*>(
this);
476 p->addVisibleFiles(parent, QStringList(element));
477 if (!p->bypassFilters.contains(node))
478 p->bypassFilters[node] = 1;
479 QString dir = q->filePath(
this->index(parent));
480 if (!node->hasInformation() && fetch) {
481 Fetching f = { std::move(dir), std::move(element), node };
482 p->toFetch.append(std::move(f));
483 p->fetchingTimer.start(0,
const_cast<QFileSystemModel*>(q));
729QVariant QFileSystemModel::data(
const QModelIndex &index,
int role)
const
731 Q_D(
const QFileSystemModel);
732 if (!index.isValid() || index.model() !=
this)
737 if (index.column() == QFileSystemModelPrivate::NameColumn)
738 return d->name(index);
740 case Qt::DisplayRole:
741 switch (index.column()) {
742 case QFileSystemModelPrivate::NameColumn:
return d->displayName(index);
743 case QFileSystemModelPrivate::SizeColumn:
return d->size(index);
744 case QFileSystemModelPrivate::TypeColumn:
return d->type(index);
745 case QFileSystemModelPrivate::TimeColumn:
return d->time(index);
747 qWarning(
"data: invalid display value column %d", index.column());
752 return filePath(index);
754 return d->name(index);
756 return QVariant::fromValue(fileInfo(index));
757 case Qt::DecorationRole:
758 if (index.column() == QFileSystemModelPrivate::NameColumn) {
759 QIcon icon = d->icon(index);
760#if QT_CONFIG(filesystemwatcher)
762 using P = QAbstractFileIconProvider;
763 if (
auto *provider = d->fileInfoGatherer->iconProvider())
764 icon = provider->icon(d->node(index)->isDir() ? P::Folder: P::File);
770 case Qt::TextAlignmentRole:
771 if (index.column() == QFileSystemModelPrivate::SizeColumn)
772 return QVariant(Qt::AlignTrailing | Qt::AlignVCenter);
774 case FilePermissions:
775 int p = permissions(index);
879bool QFileSystemModel::setData(
const QModelIndex &idx,
const QVariant &value,
int role)
881 Q_D(QFileSystemModel);
884 || role != Qt::EditRole
885 || (flags(idx) & Qt::ItemIsEditable) == 0) {
889 QString newName = value.toString();
891 chopSpaceAndDot(newName);
892 if (newName.isEmpty())
896 QString oldName = idx.data().toString();
897 if (newName == oldName)
900 const QString parentPath = filePath(parent(idx));
902 if (newName.isEmpty() || QDir::toNativeSeparators(newName).contains(QDir::separator()))
905#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
908 const QStringList watchedPaths = d->unwatchPathsAt(idx);
910 if (!QDir(parentPath).rename(oldName, newName)) {
911#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
912 d->watchPaths(watchedPaths);
917
918
919
920
921
922
923
924
926 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx);
927 QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
928 int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName);
930 parentNode->visibleChildren.removeAt(visibleLocation);
931 std::unique_ptr<QFileSystemModelPrivate::QFileSystemNode> nodeToRename(parentNode->children.take(oldName));
932 nodeToRename->fileName = newName;
933 nodeToRename->parent = parentNode;
934#if QT_CONFIG(filesystemwatcher)
935 nodeToRename->populate(d->fileInfoGatherer->getInfo(QFileInfo(parentPath, newName)));
937 nodeToRename->isVisible =
true;
938 parentNode->children[newName] = nodeToRename.release();
939 parentNode->visibleChildren.insert(visibleLocation, newName);
942 emit fileRenamed(parentPath, oldName, newName);
950QVariant QFileSystemModel::headerData(
int section, Qt::Orientation orientation,
int role)
const
953 case Qt::DecorationRole:
957 QImage pixmap(16, 1, QImage::Format_ARGB32_Premultiplied);
958 pixmap.fill(Qt::transparent);
962 case Qt::TextAlignmentRole:
963 return Qt::AlignLeft;
966 if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
967 return QAbstractItemModel::headerData(section, orientation, role);
971 case QFileSystemModelPrivate::NameColumn:
972 returnValue = tr(
"Name");
974 case QFileSystemModelPrivate::SizeColumn:
975 returnValue = tr(
"Size");
977 case QFileSystemModelPrivate::TypeColumn:
980 tr(
"Kind",
"Match OS X Finder");
982 tr(
"Type",
"All other platforms");
989 case QFileSystemModelPrivate::TimeColumn:
990 returnValue = tr(
"Date Modified");
992 default:
return QVariant();
1118void QFileSystemModelPrivate::sortChildren(
int column,
const QModelIndex &parent)
1120 Q_Q(QFileSystemModel);
1121 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent);
1122 if (indexNode->children.size() == 0)
1125 QList<QFileSystemModelPrivate::QFileSystemNode *> values;
1127 for (
auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) {
1128 if (filtersAcceptsNode(iterator.value())) {
1129 values.append(iterator.value());
1131 iterator.value()->isVisible =
false;
1135 const QFileSystemModelSorter ms(column);
1136 std::sort(values.begin(), values.end(), std::cref(ms));
1139 indexNode->visibleChildren.clear();
1141 indexNode->dirtyChildrenIndex = -1;
1142 indexNode->visibleChildren.reserve(values.size());
1143 for (QFileSystemNode *node : std::as_const(values)) {
1144 indexNode->visibleChildren.append(node->fileName);
1145 node->isVisible =
true;
1148 if (!disableRecursiveSort) {
1149 for (
int i = 0; i < q->rowCount(parent); ++i) {
1150 const QModelIndex childIndex = q->index(i, 0, parent);
1151 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(childIndex);
1153 if (indexNode->isVisible)
1154 sortChildren(column, childIndex);
1162void QFileSystemModel::sort(
int column, Qt::SortOrder order)
1164 Q_D(QFileSystemModel);
1165 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
1168 emit layoutAboutToBeChanged();
1169 QModelIndexList oldList = persistentIndexList();
1170 QList<std::pair<QFileSystemModelPrivate::QFileSystemNode *,
int>> oldNodes;
1171 oldNodes.reserve(oldList.size());
1172 for (
const QModelIndex &oldNode : oldList)
1173 oldNodes.emplace_back(d->node(oldNode), oldNode.column());
1175 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
1177 d->sortChildren(column, index(rootPath()));
1178 d->sortColumn = column;
1179 d->forceSort =
false;
1181 d->sortOrder = order;
1183 QModelIndexList newList;
1184 newList.reserve(oldNodes.size());
1185 for (
const auto &[node, col]: std::as_const(oldNodes))
1186 newList.append(d->index(node, col));
1188 changePersistentIndexList(oldList, newList);
1189 emit layoutChanged({}, VerticalSortHint);
1229bool QFileSystemModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
1230 int row,
int column,
const QModelIndex &parent)
1234 if (!parent.isValid() || isReadOnly())
1237 bool success =
true;
1238 QString to = filePath(parent) + QDir::separator();
1240 QList<QUrl> urls = data->urls();
1241 QList<QUrl>::const_iterator it = urls.constBegin();
1244 case Qt::CopyAction:
1245 for (; it != urls.constEnd(); ++it) {
1246 QString path = (*it).toLocalFile();
1247 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
1250 case Qt::LinkAction:
1251 for (; it != urls.constEnd(); ++it) {
1252 QString path = (*it).toLocalFile();
1253 success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
1256 case Qt::MoveAction:
1257 for (; it != urls.constEnd(); ++it) {
1258 QString path = (*it).toLocalFile();
1259 success = QFile::rename(path, to + QFileInfo(path).fileName()) && success;
1353void QFileSystemModel::setOptions(Options options)
1355 const Options changed = (options ^ QFileSystemModel::options());
1357 if (changed.testFlag(DontResolveSymlinks))
1358 setResolveSymlinks(!options.testFlag(DontResolveSymlinks));
1360#if QT_CONFIG(filesystemwatcher)
1361 Q_D(QFileSystemModel);
1362 if (changed.testFlag(DontWatchForChanges))
1363 d->fileInfoGatherer->setWatching(!options.testFlag(DontWatchForChanges));
1366 if (changed.testFlag(DontUseCustomDirectoryIcons)) {
1367 if (
auto provider = iconProvider()) {
1368 QAbstractFileIconProvider::Options providerOptions = provider->options();
1369 providerOptions.setFlag(QAbstractFileIconProvider::DontUseCustomDirectoryIcons,
1370 options.testFlag(QFileSystemModel::DontUseCustomDirectoryIcons));
1371 provider->setOptions(providerOptions);
1373 qWarning(
"Setting QFileSystemModel::DontUseCustomDirectoryIcons has no effect when no provider is used");
1399QString QFileSystemModel::filePath(
const QModelIndex &index)
const
1401 Q_D(
const QFileSystemModel);
1402 QString fullPath = d->filePath(index);
1403 QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);
1404 if (dirNode->isSymLink()
1405#if QT_CONFIG(filesystemwatcher)
1406 && d->fileInfoGatherer->resolveSymlinks()
1408 && d->resolvedSymLinks.contains(fullPath)
1409 && dirNode->isDir()) {
1410 QFileInfo fullPathInfo(dirNode->fileInfo());
1411 if (!dirNode->hasInformation())
1412 fullPathInfo = QFileInfo(fullPath);
1413 QString canonicalPath = fullPathInfo.canonicalFilePath();
1414 auto *canonicalNode = d->node(fullPathInfo.canonicalFilePath(),
false);
1415 QFileInfo resolvedInfo = canonicalNode->fileInfo();
1416 if (!canonicalNode->hasInformation())
1417 resolvedInfo = QFileInfo(canonicalPath);
1418 if (resolvedInfo.exists())
1419 return resolvedInfo.filePath();
1505QModelIndex QFileSystemModel::setRootPath(
const QString &newPath)
1507 Q_D(QFileSystemModel);
1510 QString longNewPath = qt_GetLongPathName(newPath);
1512 QString longNewPath = QDir::fromNativeSeparators(newPath);
1515 QString longNewPath = newPath;
1518 if (!newPath.isEmpty())
1519 longNewPath = QDir::cleanPath(longNewPath);
1521 d->setRootPath =
true;
1524 if (!newPath.isEmpty() && longNewPath.isEmpty())
1525 return d->index(rootPath());
1527 if (d->rootDir.path() == longNewPath)
1528 return d->index(rootPath());
1530 auto node = d->node(longNewPath);
1531 QFileInfo newPathInfo;
1532 if (node && node->hasInformation())
1533 newPathInfo = node->fileInfo();
1535 newPathInfo = QFileInfo(longNewPath);
1537 bool showDrives = (longNewPath.isEmpty() || longNewPath == QFileSystemModelPrivate::myComputer());
1538 if (!showDrives && !newPathInfo.exists())
1539 return d->index(rootPath());
1542 if (!rootPath().isEmpty() && rootPath() !=
"."_L1) {
1544#if QT_CONFIG(filesystemwatcher)
1545 d->fileInfoGatherer->removePath(rootPath());
1550 d->node(rootPath())->populatedChildren =
false;
1554 d->rootDir = QDir(longNewPath);
1555 QModelIndex newRootIndex;
1558 d->rootDir.setPath(
""_L1);
1560 newRootIndex = d->index(d->rootDir.path());
1562 fetchMore(newRootIndex);
1563 emit rootPathChanged(longNewPath);
1564 d->forceSort =
true;
1566 return newRootIndex;
1813void QFileSystemModelPrivate::directoryChanged(
const QString &directory,
const QStringList &files)
1815 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory,
false);
1816 if (parentNode->children.size() == 0)
1818 QStringList toRemove;
1819 QStringList newFiles = files;
1820 std::sort(newFiles.begin(), newFiles.end());
1821 for (
auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) {
1822 QStringList::iterator iterator = std::lower_bound(newFiles.begin(), newFiles.end(), i.value()->fileName);
1823 if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator))
1824 toRemove.append(i.value()->fileName);
1826 for (
int i = 0 ; i < toRemove.size() ; ++i )
1827 removeNode(parentNode, toRemove[i]);
1885void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode,
const QString& name)
1887 Q_Q(QFileSystemModel);
1888 QModelIndex parent = index(parentNode);
1889 bool indexHidden = isHiddenByFilter(parentNode, parent);
1891 int vLocation = parentNode->visibleLocation(name);
1892 if (vLocation >= 0 && !indexHidden)
1893 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1894 translateVisibleLocation(parentNode, vLocation));
1895 QFileSystemNode * node = parentNode->children.take(name);
1899 parentNode->visibleChildren.removeAt(vLocation);
1900 if (vLocation >= 0 && !indexHidden)
1961void QFileSystemModelPrivate::fileSystemChanged(
const QString &path,
1962 const QList<std::pair<QString, QFileInfo>> &updates)
1964#if QT_CONFIG(filesystemwatcher)
1965 Q_Q(QFileSystemModel);
1966 QList<QString> rowsToUpdate;
1967 QStringList newFiles;
1968 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path,
false);
1969 QModelIndex parentIndex = index(parentNode);
1970 for (
const auto &update : updates) {
1971 QString fileName = update.first;
1972 Q_ASSERT(!fileName.isEmpty());
1973 QExtendedInformation info = fileInfoGatherer->getInfo(update.second);
1974 bool previouslyHere = parentNode->children.contains(fileName);
1975 if (!previouslyHere) {
1977 chopSpaceAndDot(fileName);
1978 if (fileName.isEmpty())
1981 addNode(parentNode, fileName, info.fileInfo());
1983 QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName);
1984 bool isCaseSensitive = parentNode->caseSensitive();
1985 if (isCaseSensitive) {
1986 if (node->fileName != fileName)
1989 if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0)
1992 if (isCaseSensitive) {
1993 Q_ASSERT(node->fileName == fileName);
1995 node->fileName = fileName;
1998 if (*node != info ) {
1999 node->populate(info);
2000 bypassFilters.remove(node);
2002 if (filtersAcceptsNode(node)) {
2003 if (!node->isVisible) {
2004 newFiles.append(fileName);
2006 rowsToUpdate.append(fileName);
2009 if (node->isVisible) {
2010 int visibleLocation = parentNode->visibleLocation(fileName);
2011 removeVisibleFile(parentNode, visibleLocation);
2020 std::sort(rowsToUpdate.begin(), rowsToUpdate.end());
2023 for (
const QString &value : std::as_const(rowsToUpdate)) {
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2038 int visibleMin = parentNode->visibleLocation(min);
2039 int visibleMax = parentNode->visibleLocation(max);
2041 && visibleMin < parentNode->visibleChildren.size()
2042 && parentNode->visibleChildren.at(visibleMin) == min
2043 && visibleMax >= 0) {
2045 const int lastColumn = q->columnCount(parentIndex) - 1;
2046 const QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMin),
2047 QFileSystemModelPrivate::NameColumn, parentIndex);
2048 const QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMax),
2049 lastColumn, parentIndex);
2052 Q_ASSERT(bottom.parent() == top.parent());
2053 emit q->dataChanged(top, bottom);
2057
2060 if (newFiles.size() > 0) {
2061 addVisibleFiles(parentNode, newFiles);
2064 if (newFiles.size() > 0 || (sortColumn != 0 && rowsToUpdate.size() > 0)) {
2148void QFileSystemModelPrivate::init()
2150 delayedSortTimer.setSingleShot(
true);
2152 qRegisterMetaType<QList<std::pair<QString, QFileInfo>>>();
2153#if QT_CONFIG(filesystemwatcher)
2154 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::newListOfFiles,
2155 this, &QFileSystemModelPrivate::directoryChanged);
2156 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::updates,
2157 this, &QFileSystemModelPrivate::fileSystemChanged);
2158 QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::nameResolved,
2159 this, &QFileSystemModelPrivate::resolvedName);
2160 Q_Q(QFileSystemModel);
2161 q->connect(fileInfoGatherer.get(), &QFileInfoGatherer::directoryLoaded,
2162 q, &QFileSystemModel::directoryLoaded);
2164 QObjectPrivate::connect(&delayedSortTimer, &QTimer::timeout,
2165 this, &QFileSystemModelPrivate::performDelayedSort,
2166 Qt::QueuedConnection);
2177bool QFileSystemModelPrivate::filtersAcceptsNode(
const QFileSystemNode *node)
const
2182 const bool hideDirs = (filters & (QDir::Dirs | QDir::AllDirs)) == 0;
2183 const bool shouldHideDirNode = hideDirs && node->isDir();
2186 if (node->parent == &root || (!shouldHideDirNode && bypassFilters.contains(node)))
2190 if (!node->hasInformation())
2193 const bool filterPermissions = ((filters & QDir::PermissionMask)
2194 && (filters & QDir::PermissionMask) != QDir::PermissionMask);
2195 const bool hideFiles = !(filters & QDir::Files);
2196 const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
2197 const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
2198 const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
2199 const bool hideHidden = !(filters & QDir::Hidden);
2200 const bool hideSystem = !(filters & QDir::System);
2201 const bool hideSymlinks = (filters & QDir::NoSymLinks);
2202 const bool hideDot = (filters & QDir::NoDot);
2203 const bool hideDotDot = (filters & QDir::NoDotDot);
2206 bool isDot = (node->fileName ==
"."_L1);
2207 bool isDotDot = (node->fileName ==
".."_L1);
2208 if ( (hideHidden && !(isDot || isDotDot) && node->isHidden())
2209 || (hideSystem && node->isSystem())
2210 || (hideDirs && node->isDir())
2211 || (hideFiles && node->isFile())
2212 || (hideSymlinks && node->isSymLink())
2213 || (hideReadable && node->isReadable())
2214 || (hideWritable && node->isWritable())
2215 || (hideExecutable && node->isExecutable())
2216 || (hideDot && isDot)
2217 || (hideDotDot && isDotDot))
2220 return nameFilterDisables || passNameFilters(node);