summaryrefslogtreecommitdiffstats
path: root/src/gui/itemmodels
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/itemmodels')
-rw-r--r--src/gui/itemmodels/itemmodels.pri8
-rw-r--r--src/gui/itemmodels/qfileinfogatherer.cpp463
-rw-r--r--src/gui/itemmodels/qfileinfogatherer_p.h199
-rw-r--r--src/gui/itemmodels/qfilesystemmodel.cpp2224
-rw-r--r--src/gui/itemmodels/qfilesystemmodel.h150
-rw-r--r--src/gui/itemmodels/qfilesystemmodel_p.h308
-rw-r--r--src/gui/itemmodels/qstandarditemmodel.cpp285
-rw-r--r--src/gui/itemmodels/qstandarditemmodel.h66
-rw-r--r--src/gui/itemmodels/qstandarditemmodel_p.h56
9 files changed, 3533 insertions, 226 deletions
diff --git a/src/gui/itemmodels/itemmodels.pri b/src/gui/itemmodels/itemmodels.pri
deleted file mode 100644
index cab0594174..0000000000
--- a/src/gui/itemmodels/itemmodels.pri
+++ /dev/null
@@ -1,8 +0,0 @@
-!qtConfig(standarditemmodel): return()
-
-HEADERS += \
- itemmodels/qstandarditemmodel.h \
- itemmodels/qstandarditemmodel_p.h \
-
-SOURCES += \
- itemmodels/qstandarditemmodel.cpp \
diff --git a/src/gui/itemmodels/qfileinfogatherer.cpp b/src/gui/itemmodels/qfileinfogatherer.cpp
new file mode 100644
index 0000000000..41fb0a0db5
--- /dev/null
+++ b/src/gui/itemmodels/qfileinfogatherer.cpp
@@ -0,0 +1,463 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qfileinfogatherer_p.h"
+#include <qcoreapplication.h>
+#include <qdebug.h>
+#include <qdirlisting.h>
+#include <private/qabstractfileiconprovider_p.h>
+#include <private/qfileinfo_p.h>
+#ifndef Q_OS_WIN
+# include <unistd.h>
+# include <sys/types.h>
+#endif
+#if defined(Q_OS_VXWORKS)
+# include "qplatformdefs.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+#ifdef QT_BUILD_INTERNAL
+Q_CONSTINIT static QBasicAtomicInt fetchedRoot = Q_BASIC_ATOMIC_INITIALIZER(false);
+Q_AUTOTEST_EXPORT void qt_test_resetFetchedRoot()
+{
+ fetchedRoot.storeRelaxed(false);
+}
+
+Q_AUTOTEST_EXPORT bool qt_test_isFetchedRoot()
+{
+ return fetchedRoot.loadRelaxed();
+}
+#endif
+
+static QString translateDriveName(const QFileInfo &drive)
+{
+ QString driveName = drive.absoluteFilePath();
+#ifdef Q_OS_WIN
+ if (driveName.startsWith(u'/')) // UNC host
+ return drive.fileName();
+ if (driveName.endsWith(u'/'))
+ driveName.chop(1);
+#endif // Q_OS_WIN
+ return driveName;
+}
+
+/*!
+ Creates thread
+*/
+QFileInfoGatherer::QFileInfoGatherer(QObject *parent)
+ : QThread(parent)
+ , m_iconProvider(&defaultProvider)
+{
+ start(LowPriority);
+}
+
+/*!
+ Destroys thread
+*/
+QFileInfoGatherer::~QFileInfoGatherer()
+{
+ requestAbort();
+ wait();
+}
+
+bool QFileInfoGatherer::event(QEvent *event)
+{
+ if (event->type() == QEvent::DeferredDelete && isRunning()) {
+ // We have been asked to shut down later but were blocked,
+ // so the owning QFileSystemModel proceeded with its shut-down
+ // and deferred the destruction of the gatherer.
+ // If we are still blocked now, then we have three bad options:
+ // terminate, wait forever (preventing the process from shutting down),
+ // or accept a memory leak.
+ requestAbort();
+ if (!wait(5000)) {
+ // If the application is shutting down, then we terminate.
+ // Otherwise assume that sooner or later the thread will finish,
+ // and we delete it then.
+ if (QCoreApplication::closingDown())
+ terminate();
+ else
+ connect(this, &QThread::finished, this, [this]{ delete this; });
+ return true;
+ }
+ }
+
+ return QThread::event(event);
+}
+
+void QFileInfoGatherer::requestAbort()
+{
+ requestInterruption();
+ QMutexLocker locker(&mutex);
+ condition.wakeAll();
+}
+
+void QFileInfoGatherer::setResolveSymlinks(bool enable)
+{
+ Q_UNUSED(enable);
+#ifdef Q_OS_WIN
+ m_resolveSymlinks = enable;
+#endif
+}
+
+void QFileInfoGatherer::driveAdded()
+{
+ fetchExtendedInformation(QString(), QStringList());
+}
+
+void QFileInfoGatherer::driveRemoved()
+{
+ QStringList drives;
+ const QFileInfoList driveInfoList = QDir::drives();
+ for (const QFileInfo &fi : driveInfoList)
+ drives.append(translateDriveName(fi));
+ emit newListOfFiles(QString(), drives);
+}
+
+bool QFileInfoGatherer::resolveSymlinks() const
+{
+#ifdef Q_OS_WIN
+ return m_resolveSymlinks;
+#else
+ return false;
+#endif
+}
+
+void QFileInfoGatherer::setIconProvider(QAbstractFileIconProvider *provider)
+{
+ m_iconProvider = provider;
+}
+
+QAbstractFileIconProvider *QFileInfoGatherer::iconProvider() const
+{
+ return m_iconProvider;
+}
+
+/*!
+ Fetch extended information for all \a files in \a path
+
+ \sa updateFile(), update(), resolvedName()
+*/
+void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStringList &files)
+{
+ QMutexLocker locker(&mutex);
+ // See if we already have this dir/file in our queue
+ qsizetype loc = 0;
+ while ((loc = this->path.lastIndexOf(path, loc - 1)) != -1) {
+ if (this->files.at(loc) == files)
+ return;
+ }
+
+#if QT_CONFIG(thread)
+ this->path.push(path);
+ this->files.push(files);
+ condition.wakeAll();
+#else // !QT_CONFIG(thread)
+ getFileInfos(path, files);
+#endif // QT_CONFIG(thread)
+
+#if QT_CONFIG(filesystemwatcher)
+ if (files.isEmpty()
+ && !path.isEmpty()
+ && !path.startsWith("//"_L1) /*don't watch UNC path*/) {
+ if (!watchedDirectories().contains(path))
+ watchPaths(QStringList(path));
+ }
+#endif
+}
+
+/*!
+ Fetch extended information for all \a filePath
+
+ \sa fetchExtendedInformation()
+*/
+void QFileInfoGatherer::updateFile(const QString &filePath)
+{
+ QString dir = filePath.mid(0, filePath.lastIndexOf(u'/'));
+ QString fileName = filePath.mid(dir.size() + 1);
+ fetchExtendedInformation(dir, QStringList(fileName));
+}
+
+QStringList QFileInfoGatherer::watchedFiles() const
+{
+#if QT_CONFIG(filesystemwatcher)
+ if (m_watcher)
+ return m_watcher->files();
+#endif
+ return {};
+}
+
+QStringList QFileInfoGatherer::watchedDirectories() const
+{
+#if QT_CONFIG(filesystemwatcher)
+ if (m_watcher)
+ return m_watcher->directories();
+#endif
+ return {};
+}
+
+void QFileInfoGatherer::createWatcher()
+{
+#if QT_CONFIG(filesystemwatcher)
+ m_watcher = new QFileSystemWatcher(this);
+ connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &QFileInfoGatherer::list);
+ connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &QFileInfoGatherer::updateFile);
+# if defined(Q_OS_WIN)
+ const QVariant listener = m_watcher->property("_q_driveListener");
+ if (listener.canConvert<QObject *>()) {
+ if (QObject *driveListener = listener.value<QObject *>()) {
+ connect(driveListener, SIGNAL(driveAdded()), this, SLOT(driveAdded()));
+ connect(driveListener, SIGNAL(driveRemoved()), this, SLOT(driveRemoved()));
+ }
+ }
+# endif // Q_OS_WIN
+#endif
+}
+
+void QFileInfoGatherer::watchPaths(const QStringList &paths)
+{
+#if QT_CONFIG(filesystemwatcher)
+ if (m_watching) {
+ if (m_watcher == nullptr)
+ createWatcher();
+ m_watcher->addPaths(paths);
+ }
+#else
+ Q_UNUSED(paths);
+#endif
+}
+
+void QFileInfoGatherer::unwatchPaths(const QStringList &paths)
+{
+#if QT_CONFIG(filesystemwatcher)
+ if (m_watcher && !paths.isEmpty())
+ m_watcher->removePaths(paths);
+#else
+ Q_UNUSED(paths);
+#endif
+}
+
+bool QFileInfoGatherer::isWatching() const
+{
+ bool result = false;
+#if QT_CONFIG(filesystemwatcher)
+ QMutexLocker locker(&mutex);
+ result = m_watching;
+#endif
+ return result;
+}
+
+/*! \internal
+
+ If \a v is \c false, the QFileSystemWatcher used internally will be deleted
+ and subsequent calls to watchPaths() will do nothing.
+
+ If \a v is \c true, subsequent calls to watchPaths() will add those paths to
+ the filesystem watcher; watchPaths() will initialize a QFileSystemWatcher if
+ one hasn't already been initialized.
+*/
+void QFileInfoGatherer::setWatching(bool v)
+{
+#if QT_CONFIG(filesystemwatcher)
+ QMutexLocker locker(&mutex);
+ if (v != m_watching) {
+ m_watching = v;
+ if (!m_watching)
+ delete std::exchange(m_watcher, nullptr);
+ }
+#else
+ Q_UNUSED(v);
+#endif
+}
+
+/*
+ List all files in \a directoryPath
+
+ \sa listed()
+*/
+void QFileInfoGatherer::clear()
+{
+#if QT_CONFIG(filesystemwatcher)
+ QMutexLocker locker(&mutex);
+ unwatchPaths(watchedFiles());
+ unwatchPaths(watchedDirectories());
+#endif
+}
+
+/*
+ Remove a \a path from the watcher
+
+ \sa listed()
+*/
+void QFileInfoGatherer::removePath(const QString &path)
+{
+#if QT_CONFIG(filesystemwatcher)
+ QMutexLocker locker(&mutex);
+ unwatchPaths(QStringList(path));
+#else
+ Q_UNUSED(path);
+#endif
+}
+
+/*
+ List all files in \a directoryPath
+
+ \sa listed()
+*/
+void QFileInfoGatherer::list(const QString &directoryPath)
+{
+ fetchExtendedInformation(directoryPath, QStringList());
+}
+
+/*
+ Until aborted wait to fetch a directory or files
+*/
+void QFileInfoGatherer::run()
+{
+ forever {
+ // Disallow termination while we are holding a mutex or can be
+ // woken up cleanly.
+ setTerminationEnabled(false);
+ QMutexLocker locker(&mutex);
+ while (!isInterruptionRequested() && path.isEmpty())
+ condition.wait(&mutex);
+ if (isInterruptionRequested())
+ return;
+ const QString thisPath = std::as_const(path).front();
+ path.pop_front();
+ const QStringList thisList = std::as_const(files).front();
+ files.pop_front();
+ locker.unlock();
+
+ // Some of the system APIs we call when gathering file infomration
+ // might hang (e.g. waiting for network), so we explicitly allow
+ // termination now.
+ setTerminationEnabled(true);
+ getFileInfos(thisPath, thisList);
+ }
+}
+
+QExtendedInformation QFileInfoGatherer::getInfo(const QFileInfo &fileInfo) const
+{
+ QExtendedInformation info(fileInfo);
+ if (m_iconProvider) {
+ info.icon = m_iconProvider->icon(fileInfo);
+ info.displayType = m_iconProvider->type(fileInfo);
+ } else {
+ info.displayType = QAbstractFileIconProviderPrivate::getFileType(fileInfo);
+ }
+#if QT_CONFIG(filesystemwatcher)
+ // ### Not ready to listen all modifications by default
+ static const bool watchFiles = qEnvironmentVariableIsSet("QT_FILESYSTEMMODEL_WATCH_FILES");
+ if (watchFiles) {
+ if (!fileInfo.exists() && !fileInfo.isSymLink()) {
+ const_cast<QFileInfoGatherer *>(this)->
+ unwatchPaths(QStringList(fileInfo.absoluteFilePath()));
+ } else {
+ const QString path = fileInfo.absoluteFilePath();
+ if (!path.isEmpty() && fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable()
+ && !watchedFiles().contains(path)) {
+ const_cast<QFileInfoGatherer *>(this)->watchPaths(QStringList(path));
+ }
+ }
+ }
+#endif // filesystemwatcher
+
+#ifdef Q_OS_WIN
+ if (m_resolveSymlinks && info.isSymLink(/* ignoreNtfsSymLinks = */ true)) {
+ QFileInfo resolvedInfo(QFileInfo(fileInfo.symLinkTarget()).canonicalFilePath());
+ if (resolvedInfo.exists()) {
+ emit nameResolved(fileInfo.filePath(), resolvedInfo.fileName());
+ }
+ }
+#endif
+ return info;
+}
+
+/*
+ Get specific file info's, batch the files so update when we have 100
+ items and every 200ms after that
+ */
+void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &files)
+{
+ // List drives
+ if (path.isEmpty()) {
+#ifdef QT_BUILD_INTERNAL
+ fetchedRoot.storeRelaxed(true);
+#endif
+ QList<std::pair<QString, QFileInfo>> updatedFiles;
+ auto addToUpdatedFiles = [&updatedFiles](QFileInfo &&fileInfo) {
+ fileInfo.stat();
+ updatedFiles.emplace_back(std::pair{translateDriveName(fileInfo), fileInfo});
+ };
+
+ if (files.isEmpty()) {
+ // QDir::drives() calls QFSFileEngine::drives() which creates the QFileInfoList on
+ // the stack and return it, so this list is not shared, so no detaching.
+ QFileInfoList infoList = QDir::drives();
+ updatedFiles.reserve(infoList.size());
+ for (auto rit = infoList.rbegin(), rend = infoList.rend(); rit != rend; ++rit)
+ addToUpdatedFiles(std::move(*rit));
+ } else {
+ updatedFiles.reserve(files.size());
+ for (auto rit = files.crbegin(), rend = files.crend(); rit != rend; ++rit)
+ addToUpdatedFiles(QFileInfo(*rit));
+ }
+ emit updates(path, updatedFiles);
+ return;
+ }
+
+ QElapsedTimer base;
+ base.start();
+ QFileInfo fileInfo;
+ bool firstTime = true;
+ QList<std::pair<QString, QFileInfo>> updatedFiles;
+ QStringList filesToCheck = files;
+
+ QStringList allFiles;
+ if (files.isEmpty()) {
+ constexpr auto dirFilters = QDir::AllEntries | QDir::System | QDir::Hidden;
+ for (const auto &dirEntry : QDirListing(path, dirFilters)) {
+ if (isInterruptionRequested())
+ break;
+ fileInfo = dirEntry.fileInfo();
+ fileInfo.stat();
+ allFiles.append(fileInfo.fileName());
+ fetch(fileInfo, base, firstTime, updatedFiles, path);
+ }
+ }
+ if (!allFiles.isEmpty())
+ emit newListOfFiles(path, allFiles);
+
+ QStringList::const_iterator filesIt = filesToCheck.constBegin();
+ while (!isInterruptionRequested() && filesIt != filesToCheck.constEnd()) {
+ fileInfo.setFile(path + QDir::separator() + *filesIt);
+ ++filesIt;
+ fileInfo.stat();
+ fetch(fileInfo, base, firstTime, updatedFiles, path);
+ }
+ if (!updatedFiles.isEmpty())
+ emit updates(path, updatedFiles);
+ emit directoryLoaded(path);
+}
+
+void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QElapsedTimer &base, bool &firstTime,
+ QList<std::pair<QString, QFileInfo>> &updatedFiles, const QString &path)
+{
+ updatedFiles.emplace_back(std::pair(fileInfo.fileName(), fileInfo));
+ QElapsedTimer current;
+ current.start();
+ if ((firstTime && updatedFiles.size() > 100) || base.msecsTo(current) > 1000) {
+ emit updates(path, updatedFiles);
+ updatedFiles.clear();
+ base = current;
+ firstTime = false;
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qfileinfogatherer_p.cpp"
diff --git a/src/gui/itemmodels/qfileinfogatherer_p.h b/src/gui/itemmodels/qfileinfogatherer_p.h
new file mode 100644
index 0000000000..3d5f59c22e
--- /dev/null
+++ b/src/gui/itemmodels/qfileinfogatherer_p.h
@@ -0,0 +1,199 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFILEINFOGATHERER_H
+#define QFILEINFOGATHERER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtguiglobal_p.h>
+
+#include <qthread.h>
+#include <qmutex.h>
+#include <qwaitcondition.h>
+#if QT_CONFIG(filesystemwatcher)
+#include <qfilesystemwatcher.h>
+#endif
+#include <qabstractfileiconprovider.h>
+#include <qstack.h>
+#include <qdatetime.h>
+#include <qdir.h>
+#include <qelapsedtimer.h>
+
+#include <private/qfilesystemengine_p.h>
+
+#include <utility>
+
+QT_REQUIRE_CONFIG(filesystemmodel);
+
+QT_BEGIN_NAMESPACE
+
+class QExtendedInformation {
+public:
+ enum Type { Dir, File, System };
+
+ QExtendedInformation() {}
+ QExtendedInformation(const QFileInfo &info) : mFileInfo(info) {}
+
+ inline bool isDir() { return type() == Dir; }
+ inline bool isFile() { return type() == File; }
+ inline bool isSystem() { return type() == System; }
+
+ bool operator ==(const QExtendedInformation &fileInfo) const {
+ return mFileInfo == fileInfo.mFileInfo
+ && displayType == fileInfo.displayType
+ && permissions() == fileInfo.permissions()
+ && lastModified(QTimeZone::UTC) == fileInfo.lastModified(QTimeZone::UTC);
+ }
+
+#ifndef QT_NO_FSFILEENGINE
+ bool isCaseSensitive() const {
+ return QFileSystemEngine::isCaseSensitive();
+ }
+#endif
+
+ QFile::Permissions permissions() const {
+ return mFileInfo.permissions();
+ }
+
+ Type type() const {
+ if (mFileInfo.isDir()) {
+ return QExtendedInformation::Dir;
+ }
+ if (mFileInfo.isFile()) {
+ return QExtendedInformation::File;
+ }
+ if (!mFileInfo.exists() && mFileInfo.isSymLink()) {
+ return QExtendedInformation::System;
+ }
+ return QExtendedInformation::System;
+ }
+
+ bool isSymLink(bool ignoreNtfsSymLinks = false) const
+ {
+ if (ignoreNtfsSymLinks) {
+#ifdef Q_OS_WIN
+ return !mFileInfo.suffix().compare(QLatin1StringView("lnk"), Qt::CaseInsensitive);
+#endif
+ }
+ return mFileInfo.isSymLink();
+ }
+
+ bool isHidden() const {
+ return mFileInfo.isHidden();
+ }
+
+ QFileInfo fileInfo() const {
+ return mFileInfo;
+ }
+
+ QDateTime lastModified(const QTimeZone &tz) const {
+ return mFileInfo.lastModified(tz);
+ }
+
+ qint64 size() const {
+ qint64 size = -1;
+ if (type() == QExtendedInformation::Dir)
+ size = 0;
+ if (type() == QExtendedInformation::File)
+ size = mFileInfo.size();
+ if (!mFileInfo.exists() && !mFileInfo.isSymLink())
+ size = -1;
+ return size;
+ }
+
+ QString displayType;
+ QIcon icon;
+
+private :
+ QFileInfo mFileInfo;
+};
+
+class QFileIconProvider;
+
+class Q_GUI_EXPORT QFileInfoGatherer : public QThread
+{
+Q_OBJECT
+
+Q_SIGNALS:
+ void updates(const QString &directory, const QList<std::pair<QString, QFileInfo>> &updates);
+ void newListOfFiles(const QString &directory, const QStringList &listOfFiles) const;
+ void nameResolved(const QString &fileName, const QString &resolvedName) const;
+ void directoryLoaded(const QString &path);
+
+public:
+ explicit QFileInfoGatherer(QObject *parent = nullptr);
+ ~QFileInfoGatherer();
+
+ QStringList watchedFiles() const;
+ QStringList watchedDirectories() const;
+ void watchPaths(const QStringList &paths);
+ void unwatchPaths(const QStringList &paths);
+
+ bool isWatching() const;
+ void setWatching(bool v);
+
+ // only callable from this->thread():
+ void clear();
+ void removePath(const QString &path);
+ QExtendedInformation getInfo(const QFileInfo &info) const;
+ QAbstractFileIconProvider *iconProvider() const;
+ bool resolveSymlinks() const;
+
+ void requestAbort();
+
+public Q_SLOTS:
+ void list(const QString &directoryPath);
+ void fetchExtendedInformation(const QString &path, const QStringList &files);
+ void updateFile(const QString &path);
+ void setResolveSymlinks(bool enable);
+ void setIconProvider(QAbstractFileIconProvider *provider);
+
+private Q_SLOTS:
+ void driveAdded();
+ void driveRemoved();
+
+protected:
+ bool event(QEvent *event) override;
+
+private:
+ void run() override;
+ // called by run():
+ void getFileInfos(const QString &path, const QStringList &files);
+ void fetch(const QFileInfo &info, QElapsedTimer &base, bool &firstTime,
+ QList<std::pair<QString, QFileInfo>> &updatedFiles, const QString &path);
+
+private:
+ void createWatcher();
+
+ mutable QMutex mutex;
+ // begin protected by mutex
+ QWaitCondition condition;
+ QStack<QString> path;
+ QStack<QStringList> files;
+ // end protected by mutex
+
+#if QT_CONFIG(filesystemwatcher)
+ QFileSystemWatcher *m_watcher = nullptr;
+#endif
+ QAbstractFileIconProvider *m_iconProvider; // not accessed by run()
+ QAbstractFileIconProvider defaultProvider;
+#ifdef Q_OS_WIN
+ bool m_resolveSymlinks = true; // not accessed by run()
+#endif
+#if QT_CONFIG(filesystemwatcher)
+ bool m_watching = true;
+#endif
+};
+
+QT_END_NAMESPACE
+#endif // QFILEINFOGATHERER_H
diff --git a/src/gui/itemmodels/qfilesystemmodel.cpp b/src/gui/itemmodels/qfilesystemmodel.cpp
new file mode 100644
index 0000000000..290891322f
--- /dev/null
+++ b/src/gui/itemmodels/qfilesystemmodel.cpp
@@ -0,0 +1,2224 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qfilesystemmodel_p.h"
+#include "qfilesystemmodel.h"
+#include <qabstractfileiconprovider.h>
+#include <qlocale.h>
+#include <qmimedata.h>
+#include <qurl.h>
+#include <qdebug.h>
+#include <QtCore/qcollator.h>
+#if QT_CONFIG(regularexpression)
+# include <QtCore/qregularexpression.h>
+#endif
+
+#include <algorithm>
+
+#ifdef Q_OS_WIN
+# include <QtCore/QVarLengthArray>
+# include <qt_windows.h>
+# include <shlobj.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+/*!
+ \enum QFileSystemModel::Roles
+ \value FileIconRole
+ \value FilePathRole
+ \value FileNameRole
+ \value FilePermissions
+ \value FileInfoRole The QFileInfo object for the index
+*/
+
+/*!
+ \class QFileSystemModel
+ \since 4.4
+
+ \brief The QFileSystemModel class provides a data model for the local filesystem.
+
+ \ingroup model-view
+ \inmodule QtGui
+
+ This class provides access to the local filesystem, providing functions
+ for renaming and removing files and directories, and for creating new
+ directories. In the simplest case, it can be used with a suitable display
+ widget as part of a browser or filter.
+
+ QFileSystemModel can be accessed using the standard interface provided by
+ QAbstractItemModel, but it also provides some convenience functions that are
+ specific to a directory model.
+ The fileInfo(), isDir(), fileName() and filePath() functions provide information
+ about the underlying files and directories related to items in the model.
+ Directories can be created and removed using mkdir(), rmdir().
+
+ \note QFileSystemModel requires an instance of \l QApplication.
+
+ \section1 Example Usage
+
+ A directory model that displays the contents of a default directory
+ is usually constructed with a parent object:
+
+ \snippet shareddirmodel/main.cpp 2
+
+ A tree view can be used to display the contents of the model
+
+ \snippet shareddirmodel/main.cpp 4
+
+ and the contents of a particular directory can be displayed by
+ setting the tree view's root index:
+
+ \snippet shareddirmodel/main.cpp 7
+
+ The view's root index can be used to control how much of a
+ hierarchical model is displayed. QFileSystemModel provides a convenience
+ function that returns a suitable model index for a path to a
+ directory within the model.
+
+ \section1 Caching and Performance
+
+ QFileSystemModel will not fetch any files or directories until setRootPath()
+ is called. This will prevent any unnecessary querying on the file system
+ until that point such as listing the drives on Windows.
+
+ QFileSystemModel uses a separate thread to populate itself so it will not
+ cause the main thread to hang as the file system is being queried.
+ Calls to rowCount() will return 0 until the model populates a directory.
+
+ QFileSystemModel keeps a cache with file information. The cache is
+ automatically kept up to date using the QFileSystemWatcher.
+
+ \sa {Model Classes}
+*/
+
+/*!
+ \fn bool QFileSystemModel::rmdir(const QModelIndex &index)
+
+ Removes the directory corresponding to the model item \a index in the
+ file system model and \b{deletes the corresponding directory from the
+ file system}, returning true if successful. If the directory cannot be
+ removed, false is returned.
+
+ \warning This function deletes directories from the file system; it does
+ \b{not} move them to a location where they can be recovered.
+
+ \sa remove()
+*/
+
+/*!
+ \fn QString QFileSystemModel::fileName(const QModelIndex &index) const
+
+ Returns the file name for the item stored in the model under the given
+ \a index.
+*/
+
+/*!
+ \fn QIcon QFileSystemModel::fileIcon(const QModelIndex &index) const
+
+ Returns the icon for the item stored in the model under the given
+ \a index.
+*/
+
+/*!
+ \fn QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const
+
+ Returns the QFileInfo for the item stored in the model under the given
+ \a index.
+*/
+QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ return d->node(index)->fileInfo();
+}
+
+/*!
+ \fn void QFileSystemModel::rootPathChanged(const QString &newPath);
+
+ This signal is emitted whenever the root path has been changed to a \a newPath.
+*/
+
+/*!
+ \fn void QFileSystemModel::fileRenamed(const QString &path, const QString &oldName, const QString &newName)
+
+ This signal is emitted whenever a file with the \a oldName is successfully
+ renamed to \a newName. The file is located in the directory \a path.
+*/
+
+/*!
+ \since 4.7
+ \fn void QFileSystemModel::directoryLoaded(const QString &path)
+
+ This signal is emitted when the gatherer thread has finished to load the \a path.
+
+*/
+
+/*!
+ \fn bool QFileSystemModel::remove(const QModelIndex &index)
+
+ Removes the model item \a index from the file system model and \b{deletes the
+ corresponding file from the file system}, returning true if successful. If the
+ item cannot be removed, false is returned.
+
+ \warning This function deletes files from the file system; it does \b{not}
+ move them to a location where they can be recovered.
+
+ \sa rmdir()
+*/
+
+bool QFileSystemModel::remove(const QModelIndex &aindex)
+{
+ Q_D(QFileSystemModel);
+
+ const QString path = d->filePath(aindex);
+ const QFileInfo fileInfo(path);
+#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
+ // QTBUG-65683: Remove file system watchers prior to deletion to prevent
+ // failure due to locked files on Windows.
+ const QStringList watchedPaths = d->unwatchPathsAt(aindex);
+#endif // filesystemwatcher && Q_OS_WIN
+ const bool success = (fileInfo.isFile() || fileInfo.isSymLink())
+ ? QFile::remove(path) : QDir(path).removeRecursively();
+#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
+ if (!success)
+ d->watchPaths(watchedPaths);
+#endif // filesystemwatcher && Q_OS_WIN
+ return success;
+}
+
+/*!
+ Constructs a file system model with the given \a parent.
+*/
+QFileSystemModel::QFileSystemModel(QObject *parent) :
+ QFileSystemModel(*new QFileSystemModelPrivate, parent)
+{
+}
+
+/*!
+ \internal
+*/
+QFileSystemModel::QFileSystemModel(QFileSystemModelPrivate &dd, QObject *parent)
+ : QAbstractItemModel(dd, parent)
+{
+ Q_D(QFileSystemModel);
+ d->init();
+}
+
+/*!
+ Destroys this file system model.
+*/
+QFileSystemModel::~QFileSystemModel() = default;
+
+/*!
+ \reimp
+*/
+QModelIndex QFileSystemModel::index(int row, int column, const QModelIndex &parent) const
+{
+ Q_D(const QFileSystemModel);
+ if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
+ return QModelIndex();
+
+ // get the parent node
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = (d->indexValid(parent) ? d->node(parent) :
+ const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&d->root));
+ Q_ASSERT(parentNode);
+
+ // now get the internal pointer for the index
+ const int i = d->translateVisibleLocation(parentNode, row);
+ if (i >= parentNode->visibleChildren.size())
+ return QModelIndex();
+ const QString &childName = parentNode->visibleChildren.at(i);
+ const QFileSystemModelPrivate::QFileSystemNode *indexNode = parentNode->children.value(childName);
+ Q_ASSERT(indexNode);
+
+ return createIndex(row, column, const_cast<QFileSystemModelPrivate::QFileSystemNode*>(indexNode));
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QFileSystemModel::sibling(int row, int column, const QModelIndex &idx) const
+{
+ if (row == idx.row() && column < columnCount(idx.parent())) {
+ // cheap sibling operation: just adjust the column:
+ return createIndex(row, column, idx.internalPointer());
+ } else {
+ // for anything else: call the default implementation
+ // (this could probably be optimized, too):
+ return QAbstractItemModel::sibling(row, column, idx);
+ }
+}
+
+/*!
+ \overload
+
+ Returns the model item index for the given \a path and \a column.
+*/
+QModelIndex QFileSystemModel::index(const QString &path, int column) const
+{
+ Q_D(const QFileSystemModel);
+ QFileSystemModelPrivate::QFileSystemNode *node = d->node(path, false);
+ return d->index(node, column);
+}
+
+/*!
+ \internal
+
+ Return the QFileSystemNode that goes to index.
+ */
+QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return const_cast<QFileSystemNode*>(&root);
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = static_cast<QFileSystemModelPrivate::QFileSystemNode*>(index.internalPointer());
+ Q_ASSERT(indexNode);
+ return indexNode;
+}
+
+#ifdef Q_OS_WIN32
+static QString qt_GetLongPathName(const QString &strShortPath)
+{
+ if (strShortPath.isEmpty()
+ || strShortPath == "."_L1 || strShortPath == ".."_L1)
+ return strShortPath;
+ if (strShortPath.length() == 2 && strShortPath.endsWith(u':'))
+ return strShortPath.toUpper();
+ const QString absPath = QDir(strShortPath).absolutePath();
+ if (absPath.startsWith("//"_L1)
+ || absPath.startsWith("\\\\"_L1)) // unc
+ return QDir::fromNativeSeparators(absPath);
+ if (absPath.startsWith(u'/'))
+ return QString();
+ const QString inputString = "\\\\?\\"_L1 + QDir::toNativeSeparators(absPath);
+ QVarLengthArray<TCHAR, MAX_PATH> buffer(MAX_PATH);
+ DWORD result = ::GetLongPathName((wchar_t*)inputString.utf16(),
+ buffer.data(),
+ buffer.size());
+ if (result > DWORD(buffer.size())) {
+ buffer.resize(result);
+ result = ::GetLongPathName((wchar_t*)inputString.utf16(),
+ buffer.data(),
+ buffer.size());
+ }
+ if (result > 4) {
+ QString longPath = QString::fromWCharArray(buffer.data() + 4); // ignoring prefix
+ longPath[0] = longPath.at(0).toUpper(); // capital drive letters
+ return QDir::fromNativeSeparators(longPath);
+ } else {
+ return QDir::fromNativeSeparators(strShortPath);
+ }
+}
+#endif
+
+/*!
+ \internal
+
+ Given a path return the matching QFileSystemNode or &root if invalid
+*/
+QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QString &path, bool fetch) const
+{
+ Q_Q(const QFileSystemModel);
+ Q_UNUSED(q);
+ if (path.isEmpty() || path == myComputer() || path.startsWith(u':'))
+ return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
+
+ // Construct the nodes up to the new root path if they need to be built
+ QString absolutePath;
+#ifdef Q_OS_WIN32
+ QString longPath = qt_GetLongPathName(path);
+#else
+ QString longPath = path;
+#endif
+ if (longPath == rootDir.path())
+ absolutePath = rootDir.absolutePath();
+ else
+ absolutePath = QDir(longPath).absolutePath();
+
+ // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const?
+ QStringList pathElements = absolutePath.split(u'/', Qt::SkipEmptyParts);
+ if ((pathElements.isEmpty())
+#if !defined(Q_OS_WIN)
+ && QDir::fromNativeSeparators(longPath) != "/"_L1
+#endif
+ )
+ return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
+ QModelIndex index = QModelIndex(); // start with "My Computer"
+ QString elementPath;
+ QChar separator = u'/';
+ QString trailingSeparator;
+#if defined(Q_OS_WIN)
+ if (absolutePath.startsWith("//"_L1)) { // UNC path
+ QString host = "\\\\"_L1 + pathElements.constFirst();
+ if (absolutePath == QDir::fromNativeSeparators(host))
+ absolutePath.append(u'/');
+ if (longPath.endsWith(u'/') && !absolutePath.endsWith(u'/'))
+ absolutePath.append(u'/');
+ if (absolutePath.endsWith(u'/'))
+ trailingSeparator = "\\"_L1;
+ int r = 0;
+ auto rootNode = const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
+ auto it = root.children.constFind(host);
+ if (it != root.children.cend()) {
+ host = it.key(); // Normalize case for lookup in visibleLocation()
+ } else {
+ if (pathElements.count() == 1 && !absolutePath.endsWith(u'/'))
+ return rootNode;
+ QFileInfo info(host);
+ if (!info.exists())
+ return rootNode;
+ QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
+ p->addNode(rootNode, host,info);
+ p->addVisibleFiles(rootNode, QStringList(host));
+ }
+ r = rootNode->visibleLocation(host);
+ r = translateVisibleLocation(rootNode, r);
+ index = q->index(r, 0, QModelIndex());
+ pathElements.pop_front();
+ separator = u'\\';
+ elementPath = host;
+ elementPath.append(separator);
+ } else {
+ if (!pathElements.at(0).contains(u':')) {
+ QString rootPath = QDir(longPath).rootPath();
+ pathElements.prepend(rootPath);
+ }
+ if (pathElements.at(0).endsWith(u'/'))
+ pathElements[0].chop(1);
+ }
+#else
+ // add the "/" item, since it is a valid path element on Unix
+ if (absolutePath[0] == u'/')
+ pathElements.prepend("/"_L1);
+#endif
+
+ QFileSystemModelPrivate::QFileSystemNode *parent = node(index);
+
+ for (int i = 0; i < pathElements.size(); ++i) {
+ QString element = pathElements.at(i);
+ if (i != 0)
+ elementPath.append(separator);
+ elementPath.append(element);
+ if (i == pathElements.size() - 1)
+ elementPath.append(trailingSeparator);
+#ifdef Q_OS_WIN
+ // On Windows, "filename " and "filename" are equivalent and
+ // "filename . " and "filename" are equivalent
+ // "filename......." and "filename" are equivalent Task #133928
+ // whereas "filename .txt" is still "filename .txt"
+ // If after stripping the characters there is nothing left then we
+ // just return the parent directory as it is assumed that the path
+ // is referring to the parent
+ while (element.endsWith(u'.') || element.endsWith(u' '))
+ element.chop(1);
+ // Only filenames that can't possibly exist will be end up being empty
+ if (element.isEmpty())
+ return parent;
+#endif
+ bool alreadyExisted = parent->children.contains(element);
+
+ // we couldn't find the path element, we create a new node since we
+ // _know_ that the path is valid
+ if (alreadyExisted) {
+ if ((parent->children.size() == 0)
+ || (parent->caseSensitive()
+ && parent->children.value(element)->fileName != element)
+ || (!parent->caseSensitive()
+ && parent->children.value(element)->fileName.toLower() != element.toLower()))
+ alreadyExisted = false;
+ }
+
+ QFileSystemModelPrivate::QFileSystemNode *node;
+ if (!alreadyExisted) {
+ // Someone might call ::index("file://cookie/monster/doesn't/like/veggies"),
+ // a path that doesn't exists, I.E. don't blindly create directories.
+ QFileInfo info(elementPath);
+ if (!info.exists())
+ return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
+ QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
+ node = p->addNode(parent, element,info);
+#if QT_CONFIG(filesystemwatcher)
+ node->populate(fileInfoGatherer->getInfo(info));
+#endif
+ } else {
+ node = parent->children.value(element);
+ }
+
+ Q_ASSERT(node);
+ if (!node->isVisible) {
+ // It has been filtered out
+ if (alreadyExisted && node->hasInformation() && !fetch)
+ return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
+
+ QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
+ p->addVisibleFiles(parent, QStringList(element));
+ if (!p->bypassFilters.contains(node))
+ p->bypassFilters[node] = 1;
+ QString dir = q->filePath(this->index(parent));
+ if (!node->hasInformation() && fetch) {
+ Fetching f = { std::move(dir), std::move(element), node };
+ p->toFetch.append(std::move(f));
+ p->fetchingTimer.start(0, const_cast<QFileSystemModel*>(q));
+ }
+ }
+ parent = node;
+ }
+
+ return parent;
+}
+
+/*!
+ \reimp
+*/
+void QFileSystemModel::timerEvent(QTimerEvent *event)
+{
+ Q_D(QFileSystemModel);
+ if (event->timerId() == d->fetchingTimer.timerId()) {
+ d->fetchingTimer.stop();
+#if QT_CONFIG(filesystemwatcher)
+ for (int i = 0; i < d->toFetch.size(); ++i) {
+ const QFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node;
+ if (!node->hasInformation()) {
+ d->fileInfoGatherer->fetchExtendedInformation(d->toFetch.at(i).dir,
+ QStringList(d->toFetch.at(i).file));
+ } else {
+ // qDebug("yah!, you saved a little gerbil soul");
+ }
+ }
+#endif
+ d->toFetch.clear();
+ }
+}
+
+/*!
+ Returns \c true if the model item \a index represents a directory;
+ otherwise returns \c false.
+*/
+bool QFileSystemModel::isDir(const QModelIndex &index) const
+{
+ // This function is for public usage only because it could create a file info
+ Q_D(const QFileSystemModel);
+ if (!index.isValid())
+ return true;
+ QFileSystemModelPrivate::QFileSystemNode *n = d->node(index);
+ if (n->hasInformation())
+ return n->isDir();
+ return fileInfo(index).isDir();
+}
+
+/*!
+ Returns the size in bytes of \a index. If the file does not exist, 0 is returned.
+ */
+qint64 QFileSystemModel::size(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ if (!index.isValid())
+ return 0;
+ return d->node(index)->size();
+}
+
+/*!
+ Returns the type of file \a index such as "Directory" or "JPEG file".
+ */
+QString QFileSystemModel::type(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ if (!index.isValid())
+ return QString();
+ return d->node(index)->type();
+}
+
+/*!
+ Returns the date and time (in local time) when \a index was last modified.
+
+ This is an overloaded function, equivalent to calling:
+ \code
+ lastModified(index, QTimeZone::LocalTime);
+ \endcode
+
+ If \a index is invalid, a default constructed QDateTime is returned.
+ */
+QDateTime QFileSystemModel::lastModified(const QModelIndex &index) const
+{
+ return lastModified(index, QTimeZone::LocalTime);
+}
+
+/*!
+ \since 6.6
+ Returns the date and time, in the time zone \a tz, when
+ \a index was last modified.
+
+ Typical arguments for \a tz are \c QTimeZone::UTC or \c QTimeZone::LocalTime.
+ UTC does not require any conversion from the time returned by the native file
+ system API, therefore getting the time in UTC is potentially faster. LocalTime
+ is typically chosen if the time is shown to the user.
+
+ If \a index is invalid, a default constructed QDateTime is returned.
+ */
+QDateTime QFileSystemModel::lastModified(const QModelIndex &index, const QTimeZone &tz) const
+{
+ Q_D(const QFileSystemModel);
+ if (!index.isValid())
+ return QDateTime();
+ return d->node(index)->lastModified(tz);
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QFileSystemModel::parent(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ if (!d->indexValid(index))
+ return QModelIndex();
+
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index);
+ Q_ASSERT(indexNode != nullptr);
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
+ if (parentNode == nullptr || parentNode == &d->root)
+ return QModelIndex();
+
+ // get the parent's row
+ QFileSystemModelPrivate::QFileSystemNode *grandParentNode = parentNode->parent;
+ Q_ASSERT(grandParentNode->children.contains(parentNode->fileName));
+ int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName));
+ if (visualRow == -1)
+ return QModelIndex();
+ return createIndex(visualRow, 0, parentNode);
+}
+
+/*
+ \internal
+
+ return the index for node
+*/
+QModelIndex QFileSystemModelPrivate::index(const QFileSystemModelPrivate::QFileSystemNode *node, int column) const
+{
+ Q_Q(const QFileSystemModel);
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = (node ? node->parent : nullptr);
+ if (node == &root || !parentNode)
+ return QModelIndex();
+
+ // get the parent's row
+ Q_ASSERT(node);
+ if (!node->isVisible)
+ return QModelIndex();
+
+ int visualRow = translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName));
+ return q->createIndex(visualRow, column, const_cast<QFileSystemNode*>(node));
+}
+
+/*!
+ \reimp
+*/
+bool QFileSystemModel::hasChildren(const QModelIndex &parent) const
+{
+ Q_D(const QFileSystemModel);
+ if (parent.column() > 0)
+ return false;
+
+ if (!parent.isValid()) // drives
+ return true;
+
+ const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
+ Q_ASSERT(indexNode);
+ return (indexNode->isDir());
+}
+
+/*!
+ \reimp
+ */
+bool QFileSystemModel::canFetchMore(const QModelIndex &parent) const
+{
+ Q_D(const QFileSystemModel);
+ if (!d->setRootPath)
+ return false;
+ const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
+ return (!indexNode->populatedChildren);
+}
+
+/*!
+ \reimp
+ */
+void QFileSystemModel::fetchMore(const QModelIndex &parent)
+{
+ Q_D(QFileSystemModel);
+ if (!d->setRootPath)
+ return;
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
+ if (indexNode->populatedChildren)
+ return;
+ indexNode->populatedChildren = true;
+#if QT_CONFIG(filesystemwatcher)
+ d->fileInfoGatherer->list(filePath(parent));
+#endif
+}
+
+/*!
+ \reimp
+*/
+int QFileSystemModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QFileSystemModel);
+ if (parent.column() > 0)
+ return 0;
+
+ if (!parent.isValid())
+ return d->root.visibleChildren.size();
+
+ const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);
+ return parentNode->visibleChildren.size();
+}
+
+/*!
+ \reimp
+*/
+int QFileSystemModel::columnCount(const QModelIndex &parent) const
+{
+ return (parent.column() > 0) ? 0 : QFileSystemModelPrivate::NumColumns;
+}
+
+/*!
+ Returns the data stored under the given \a role for the item "My Computer".
+
+ \sa Qt::ItemDataRole
+ */
+QVariant QFileSystemModel::myComputer(int role) const
+{
+#if QT_CONFIG(filesystemwatcher)
+ Q_D(const QFileSystemModel);
+#endif
+ switch (role) {
+ case Qt::DisplayRole:
+ return QFileSystemModelPrivate::myComputer();
+#if QT_CONFIG(filesystemwatcher)
+ case Qt::DecorationRole:
+ if (auto *provider = d->fileInfoGatherer->iconProvider())
+ return provider->icon(QAbstractFileIconProvider::Computer);
+ break;
+#endif
+ }
+ return QVariant();
+}
+
+/*!
+ \reimp
+*/
+QVariant QFileSystemModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QFileSystemModel);
+ if (!index.isValid() || index.model() != this)
+ return QVariant();
+
+ switch (role) {
+ case Qt::EditRole:
+ if (index.column() == QFileSystemModelPrivate::NameColumn)
+ return d->name(index);
+ Q_FALLTHROUGH();
+ case Qt::DisplayRole:
+ switch (index.column()) {
+ case QFileSystemModelPrivate::NameColumn: return d->displayName(index);
+ case QFileSystemModelPrivate::SizeColumn: return d->size(index);
+ case QFileSystemModelPrivate::TypeColumn: return d->type(index);
+ case QFileSystemModelPrivate::TimeColumn: return d->time(index);
+ default:
+ qWarning("data: invalid display value column %d", index.column());
+ break;
+ }
+ break;
+ case FilePathRole:
+ return filePath(index);
+ case FileNameRole:
+ return d->name(index);
+ case FileInfoRole:
+ return QVariant::fromValue(fileInfo(index));
+ case Qt::DecorationRole:
+ if (index.column() == QFileSystemModelPrivate::NameColumn) {
+ QIcon icon = d->icon(index);
+#if QT_CONFIG(filesystemwatcher)
+ if (icon.isNull()) {
+ using P = QAbstractFileIconProvider;
+ if (auto *provider = d->fileInfoGatherer->iconProvider())
+ icon = provider->icon(d->node(index)->isDir() ? P::Folder: P::File);
+ }
+#endif // filesystemwatcher
+ return icon;
+ }
+ break;
+ case Qt::TextAlignmentRole:
+ if (index.column() == QFileSystemModelPrivate::SizeColumn)
+ return QVariant(Qt::AlignTrailing | Qt::AlignVCenter);
+ break;
+ case FilePermissions:
+ int p = permissions(index);
+ return p;
+ }
+
+ return QVariant();
+}
+
+/*!
+ \internal
+*/
+QString QFileSystemModelPrivate::size(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QString();
+ const QFileSystemNode *n = node(index);
+ if (n->isDir()) {
+#ifdef Q_OS_MAC
+ return "--"_L1;
+#else
+ return ""_L1;
+#endif
+ // Windows - ""
+ // OS X - "--"
+ // Konqueror - "4 KB"
+ // Nautilus - "9 items" (the number of children)
+ }
+ return size(n->size());
+}
+
+QString QFileSystemModelPrivate::size(qint64 bytes)
+{
+ return QLocale::system().formattedDataSize(bytes);
+}
+
+/*!
+ \internal
+*/
+QString QFileSystemModelPrivate::time(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QString();
+#if QT_CONFIG(datestring)
+ return QLocale::system().toString(node(index)->lastModified(QTimeZone::LocalTime), QLocale::ShortFormat);
+#else
+ Q_UNUSED(index);
+ return QString();
+#endif
+}
+
+/*
+ \internal
+*/
+QString QFileSystemModelPrivate::type(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QString();
+ return node(index)->type();
+}
+
+/*!
+ \internal
+*/
+QString QFileSystemModelPrivate::name(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QString();
+ QFileSystemNode *dirNode = node(index);
+ if (
+#if QT_CONFIG(filesystemwatcher)
+ fileInfoGatherer->resolveSymlinks() &&
+#endif
+ !resolvedSymLinks.isEmpty() && dirNode->isSymLink(/* ignoreNtfsSymLinks = */ true)) {
+ QString fullPath = QDir::fromNativeSeparators(filePath(index));
+ return resolvedSymLinks.value(fullPath, dirNode->fileName);
+ }
+ return dirNode->fileName;
+}
+
+/*!
+ \internal
+*/
+QString QFileSystemModelPrivate::displayName(const QModelIndex &index) const
+{
+#if defined(Q_OS_WIN)
+ QFileSystemNode *dirNode = node(index);
+ if (!dirNode->volumeName.isEmpty())
+ return dirNode->volumeName;
+#endif
+ return name(index);
+}
+
+/*!
+ \internal
+*/
+QIcon QFileSystemModelPrivate::icon(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QIcon();
+ return node(index)->icon();
+}
+
+/*!
+ \reimp
+*/
+bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role)
+{
+ Q_D(QFileSystemModel);
+ if (!idx.isValid()
+ || idx.column() != 0
+ || role != Qt::EditRole
+ || (flags(idx) & Qt::ItemIsEditable) == 0) {
+ return false;
+ }
+
+ QString newName = value.toString();
+ QString oldName = idx.data().toString();
+ if (newName == oldName)
+ return true;
+
+ const QString parentPath = filePath(parent(idx));
+
+ if (newName.isEmpty() || QDir::toNativeSeparators(newName).contains(QDir::separator()))
+ return false;
+
+#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
+ // QTBUG-65683: Remove file system watchers prior to renaming to prevent
+ // failure due to locked files on Windows.
+ const QStringList watchedPaths = d->unwatchPathsAt(idx);
+#endif // filesystemwatcher && Q_OS_WIN
+ if (!QDir(parentPath).rename(oldName, newName)) {
+#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
+ d->watchPaths(watchedPaths);
+#endif
+ return false;
+ } else {
+ /*
+ *After re-naming something we don't want the selection to change*
+ - can't remove rows and later insert
+ - can't quickly remove and insert
+ - index pointer can't change because treeview doesn't use persistent index's
+
+ - if this get any more complicated think of changing it to just
+ use layoutChanged
+ */
+
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx);
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
+ int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName);
+
+ parentNode->visibleChildren.removeAt(visibleLocation);
+ std::unique_ptr<QFileSystemModelPrivate::QFileSystemNode> nodeToRename(parentNode->children.take(oldName));
+ nodeToRename->fileName = newName;
+ nodeToRename->parent = parentNode;
+#if QT_CONFIG(filesystemwatcher)
+ nodeToRename->populate(d->fileInfoGatherer->getInfo(QFileInfo(parentPath, newName)));
+#endif
+ nodeToRename->isVisible = true;
+ parentNode->children[newName] = nodeToRename.release();
+ parentNode->visibleChildren.insert(visibleLocation, newName);
+
+ d->delayedSort();
+ emit fileRenamed(parentPath, oldName, newName);
+ }
+ return true;
+}
+
+/*!
+ \reimp
+*/
+QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role) {
+ case Qt::DecorationRole:
+ if (section == 0) {
+ // ### TODO oh man this is ugly and doesn't even work all the way!
+ // it is still 2 pixels off
+ QImage pixmap(16, 1, QImage::Format_ARGB32_Premultiplied);
+ pixmap.fill(Qt::transparent);
+ return pixmap;
+ }
+ break;
+ case Qt::TextAlignmentRole:
+ return Qt::AlignLeft;
+ }
+
+ if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
+ return QAbstractItemModel::headerData(section, orientation, role);
+
+ QString returnValue;
+ switch (section) {
+ case QFileSystemModelPrivate::NameColumn:
+ returnValue = tr("Name");
+ break;
+ case QFileSystemModelPrivate::SizeColumn:
+ returnValue = tr("Size");
+ break;
+ case QFileSystemModelPrivate::TypeColumn:
+ returnValue =
+#ifdef Q_OS_MAC
+ tr("Kind", "Match OS X Finder");
+#else
+ tr("Type", "All other platforms");
+#endif
+ break;
+ // Windows - Type
+ // OS X - Kind
+ // Konqueror - File Type
+ // Nautilus - Type
+ case QFileSystemModelPrivate::TimeColumn:
+ returnValue = tr("Date Modified");
+ break;
+ default: return QVariant();
+ }
+ return returnValue;
+}
+
+/*!
+ \reimp
+*/
+Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ Qt::ItemFlags flags = QAbstractItemModel::flags(index);
+ if (!index.isValid())
+ return flags;
+
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index);
+ if (d->nameFilterDisables && !d->passNameFilters(indexNode)) {
+ flags &= ~Qt::ItemIsEnabled;
+ // ### TODO you shouldn't be able to set this as the current item, task 119433
+ return flags;
+ }
+
+ flags |= Qt::ItemIsDragEnabled;
+
+ if (!indexNode->isDir())
+ flags |= Qt::ItemNeverHasChildren;
+ if (d->readOnly)
+ return flags;
+ if ((index.column() == 0) && indexNode->permissions() & QFile::WriteUser) {
+ flags |= Qt::ItemIsEditable;
+ if (indexNode->isDir())
+ flags |= Qt::ItemIsDropEnabled;
+ }
+ return flags;
+}
+
+/*!
+ \internal
+*/
+void QFileSystemModelPrivate::performDelayedSort()
+{
+ Q_Q(QFileSystemModel);
+ q->sort(sortColumn, sortOrder);
+}
+
+
+/*
+ \internal
+ Helper functor used by sort()
+*/
+class QFileSystemModelSorter
+{
+public:
+ inline QFileSystemModelSorter(int column) : sortColumn(column)
+ {
+ naturalCompare.setNumericMode(true);
+ naturalCompare.setCaseSensitivity(Qt::CaseInsensitive);
+ }
+
+ bool compareNodes(const QFileSystemModelPrivate::QFileSystemNode *l,
+ const QFileSystemModelPrivate::QFileSystemNode *r) const
+ {
+ switch (sortColumn) {
+ case QFileSystemModelPrivate::NameColumn: {
+#ifndef Q_OS_MAC
+ // place directories before files
+ bool left = l->isDir();
+ bool right = r->isDir();
+ if (left ^ right)
+ return left;
+#endif
+ return naturalCompare.compare(l->fileName, r->fileName) < 0;
+ }
+ case QFileSystemModelPrivate::SizeColumn:
+ {
+ // Directories go first
+ bool left = l->isDir();
+ bool right = r->isDir();
+ if (left ^ right)
+ return left;
+
+ qint64 sizeDifference = l->size() - r->size();
+ if (sizeDifference == 0)
+ return naturalCompare.compare(l->fileName, r->fileName) < 0;
+
+ return sizeDifference < 0;
+ }
+ case QFileSystemModelPrivate::TypeColumn:
+ {
+ int compare = naturalCompare.compare(l->type(), r->type());
+ if (compare == 0)
+ return naturalCompare.compare(l->fileName, r->fileName) < 0;
+
+ return compare < 0;
+ }
+ case QFileSystemModelPrivate::TimeColumn:
+ {
+ const QDateTime left = l->lastModified(QTimeZone::UTC);
+ const QDateTime right = r->lastModified(QTimeZone::UTC);
+ if (left == right)
+ return naturalCompare.compare(l->fileName, r->fileName) < 0;
+
+ return left < right;
+ }
+ }
+ Q_ASSERT(false);
+ return false;
+ }
+
+ bool operator()(const QFileSystemModelPrivate::QFileSystemNode *l,
+ const QFileSystemModelPrivate::QFileSystemNode *r) const
+ {
+ return compareNodes(l, r);
+ }
+
+
+private:
+ QCollator naturalCompare;
+ int sortColumn;
+};
+
+/*
+ \internal
+
+ Sort all of the children of parent
+*/
+void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent)
+{
+ Q_Q(QFileSystemModel);
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent);
+ if (indexNode->children.size() == 0)
+ return;
+
+ QList<QFileSystemModelPrivate::QFileSystemNode *> values;
+
+ for (auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) {
+ if (filtersAcceptsNode(iterator.value())) {
+ values.append(iterator.value());
+ } else {
+ iterator.value()->isVisible = false;
+ }
+ }
+ QFileSystemModelSorter ms(column);
+ std::sort(values.begin(), values.end(), ms);
+ // First update the new visible list
+ indexNode->visibleChildren.clear();
+ //No more dirty item we reset our internal dirty index
+ indexNode->dirtyChildrenIndex = -1;
+ indexNode->visibleChildren.reserve(values.size());
+ for (QFileSystemNode *node : std::as_const(values)) {
+ indexNode->visibleChildren.append(node->fileName);
+ node->isVisible = true;
+ }
+
+ if (!disableRecursiveSort) {
+ for (int i = 0; i < q->rowCount(parent); ++i) {
+ const QModelIndex childIndex = q->index(i, 0, parent);
+ QFileSystemModelPrivate::QFileSystemNode *indexNode = node(childIndex);
+ //Only do a recursive sort on visible nodes
+ if (indexNode->isVisible)
+ sortChildren(column, childIndex);
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QFileSystemModel::sort(int column, Qt::SortOrder order)
+{
+ Q_D(QFileSystemModel);
+ if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
+ return;
+
+ emit layoutAboutToBeChanged();
+ QModelIndexList oldList = persistentIndexList();
+ QList<QPair<QFileSystemModelPrivate::QFileSystemNode *, int>> oldNodes;
+ oldNodes.reserve(oldList.size());
+ for (const QModelIndex &oldNode : oldList) {
+ QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldNode), oldNode.column());
+ oldNodes.append(pair);
+ }
+
+ if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
+ //we sort only from where we are, don't need to sort all the model
+ d->sortChildren(column, index(rootPath()));
+ d->sortColumn = column;
+ d->forceSort = false;
+ }
+ d->sortOrder = order;
+
+ QModelIndexList newList;
+ newList.reserve(oldNodes.size());
+ for (const auto &[node, col]: std::as_const(oldNodes))
+ newList.append(d->index(node, col));
+
+ changePersistentIndexList(oldList, newList);
+ emit layoutChanged();
+}
+
+/*!
+ Returns a list of MIME types that can be used to describe a list of items
+ in the model.
+*/
+QStringList QFileSystemModel::mimeTypes() const
+{
+ return QStringList("text/uri-list"_L1);
+}
+
+/*!
+ Returns an object that contains a serialized description of the specified
+ \a indexes. The format used to describe the items corresponding to the
+ indexes is obtained from the mimeTypes() function.
+
+ If the list of indexes is empty, \nullptr is returned rather than a
+ serialized empty list.
+*/
+QMimeData *QFileSystemModel::mimeData(const QModelIndexList &indexes) const
+{
+ QList<QUrl> urls;
+ QList<QModelIndex>::const_iterator it = indexes.begin();
+ for (; it != indexes.end(); ++it)
+ if ((*it).column() == QFileSystemModelPrivate::NameColumn)
+ urls << QUrl::fromLocalFile(filePath(*it));
+ QMimeData *data = new QMimeData();
+ data->setUrls(urls);
+ return data;
+}
+
+/*!
+ Handles the \a data supplied by a drag and drop operation that ended with
+ the given \a action over the row in the model specified by the \a row and
+ \a column and by the \a parent index. Returns true if the operation was
+ successful.
+
+ \sa supportedDropActions()
+*/
+bool QFileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent)
+{
+ Q_UNUSED(row);
+ Q_UNUSED(column);
+ if (!parent.isValid() || isReadOnly())
+ return false;
+
+ bool success = true;
+ QString to = filePath(parent) + QDir::separator();
+
+ QList<QUrl> urls = data->urls();
+ QList<QUrl>::const_iterator it = urls.constBegin();
+
+ switch (action) {
+ case Qt::CopyAction:
+ for (; it != urls.constEnd(); ++it) {
+ QString path = (*it).toLocalFile();
+ success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
+ }
+ break;
+ case Qt::LinkAction:
+ for (; it != urls.constEnd(); ++it) {
+ QString path = (*it).toLocalFile();
+ success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
+ }
+ break;
+ case Qt::MoveAction:
+ for (; it != urls.constEnd(); ++it) {
+ QString path = (*it).toLocalFile();
+ success = QFile::rename(path, to + QFileInfo(path).fileName()) && success;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return success;
+}
+
+/*!
+ \reimp
+*/
+Qt::DropActions QFileSystemModel::supportedDropActions() const
+{
+ return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
+}
+
+/*!
+ \reimp
+*/
+QHash<int, QByteArray> QFileSystemModel::roleNames() const
+{
+ auto ret = QAbstractItemModel::roleNames();
+ ret.insert(QFileSystemModel::FileIconRole,
+ QByteArrayLiteral("fileIcon")); // == Qt::decoration
+ ret.insert(QFileSystemModel::FilePathRole, QByteArrayLiteral("filePath"));
+ ret.insert(QFileSystemModel::FileNameRole, QByteArrayLiteral("fileName"));
+ ret.insert(QFileSystemModel::FilePermissions, QByteArrayLiteral("filePermissions"));
+ ret.insert(QFileSystemModel::FileInfoRole, QByteArrayLiteral("fileInfo"));
+ return ret;
+}
+
+/*!
+ \enum QFileSystemModel::Option
+ \since 5.14
+
+ \value DontWatchForChanges Do not add file watchers to the paths.
+ This reduces overhead when using the model for simple tasks
+ like line edit completion.
+
+ \value DontResolveSymlinks Don't resolve symlinks in the file
+ system model. By default, symlinks are resolved.
+
+ \value DontUseCustomDirectoryIcons Always use the default directory icon.
+ Some platforms allow the user to set a different icon. Custom icon lookup
+ causes a big performance impact over network or removable drives.
+ This sets the QFileIconProvider::DontUseCustomDirectoryIcons
+ option in the icon provider accordingly.
+
+ \sa resolveSymlinks
+*/
+
+/*!
+ \since 5.14
+ Sets the given \a option to be enabled if \a on is true; otherwise,
+ clears the given \a option.
+
+ Options should be set before changing properties.
+
+ \sa options, testOption()
+*/
+void QFileSystemModel::setOption(Option option, bool on)
+{
+ QFileSystemModel::Options previousOptions = options();
+ setOptions(previousOptions.setFlag(option, on));
+}
+
+/*!
+ \since 5.14
+
+ Returns \c true if the given \a option is enabled; otherwise, returns
+ false.
+
+ \sa options, setOption()
+*/
+bool QFileSystemModel::testOption(Option option) const
+{
+ return options().testFlag(option);
+}
+
+/*!
+ \property QFileSystemModel::options
+ \brief the various options that affect the model
+ \since 5.14
+
+ By default, all options are disabled.
+
+ Options should be set before changing properties.
+
+ \sa setOption(), testOption()
+*/
+void QFileSystemModel::setOptions(Options options)
+{
+ const Options changed = (options ^ QFileSystemModel::options());
+
+ if (changed.testFlag(DontResolveSymlinks))
+ setResolveSymlinks(!options.testFlag(DontResolveSymlinks));
+
+#if QT_CONFIG(filesystemwatcher)
+ Q_D(QFileSystemModel);
+ if (changed.testFlag(DontWatchForChanges))
+ d->fileInfoGatherer->setWatching(!options.testFlag(DontWatchForChanges));
+#endif
+
+ if (changed.testFlag(DontUseCustomDirectoryIcons)) {
+ if (auto provider = iconProvider()) {
+ QAbstractFileIconProvider::Options providerOptions = provider->options();
+ providerOptions.setFlag(QAbstractFileIconProvider::DontUseCustomDirectoryIcons,
+ options.testFlag(QFileSystemModel::DontUseCustomDirectoryIcons));
+ provider->setOptions(providerOptions);
+ } else {
+ qWarning("Setting QFileSystemModel::DontUseCustomDirectoryIcons has no effect when no provider is used");
+ }
+ }
+}
+
+QFileSystemModel::Options QFileSystemModel::options() const
+{
+ QFileSystemModel::Options result;
+ result.setFlag(DontResolveSymlinks, !resolveSymlinks());
+#if QT_CONFIG(filesystemwatcher)
+ Q_D(const QFileSystemModel);
+ result.setFlag(DontWatchForChanges, !d->fileInfoGatherer->isWatching());
+#else
+ result.setFlag(DontWatchForChanges);
+#endif
+ if (auto provider = iconProvider()) {
+ result.setFlag(DontUseCustomDirectoryIcons,
+ provider->options().testFlag(QAbstractFileIconProvider::DontUseCustomDirectoryIcons));
+ }
+ return result;
+}
+
+/*!
+ Returns the path of the item stored in the model under the
+ \a index given.
+*/
+QString QFileSystemModel::filePath(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ QString fullPath = d->filePath(index);
+ QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);
+ if (dirNode->isSymLink()
+#if QT_CONFIG(filesystemwatcher)
+ && d->fileInfoGatherer->resolveSymlinks()
+#endif
+ && d->resolvedSymLinks.contains(fullPath)
+ && dirNode->isDir()) {
+ QFileInfo fullPathInfo(dirNode->fileInfo());
+ if (!dirNode->hasInformation())
+ fullPathInfo = QFileInfo(fullPath);
+ QString canonicalPath = fullPathInfo.canonicalFilePath();
+ auto *canonicalNode = d->node(fullPathInfo.canonicalFilePath(), false);
+ QFileInfo resolvedInfo = canonicalNode->fileInfo();
+ if (!canonicalNode->hasInformation())
+ resolvedInfo = QFileInfo(canonicalPath);
+ if (resolvedInfo.exists())
+ return resolvedInfo.filePath();
+ }
+ return fullPath;
+}
+
+QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const
+{
+ Q_Q(const QFileSystemModel);
+ Q_UNUSED(q);
+ if (!index.isValid())
+ return QString();
+ Q_ASSERT(index.model() == q);
+
+ QStringList path;
+ QModelIndex idx = index;
+ while (idx.isValid()) {
+ QFileSystemModelPrivate::QFileSystemNode *dirNode = node(idx);
+ if (dirNode)
+ path.prepend(dirNode->fileName);
+ idx = idx.parent();
+ }
+ QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator()));
+#if !defined(Q_OS_WIN)
+ if ((fullPath.size() > 2) && fullPath[0] == u'/' && fullPath[1] == u'/')
+ fullPath = fullPath.mid(1);
+#else
+ if (fullPath.length() == 2 && fullPath.endsWith(u':'))
+ fullPath.append(u'/');
+#endif
+ return fullPath;
+}
+
+/*!
+ Create a directory with the \a name in the \a parent model index.
+*/
+QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent, const QString &name)
+{
+ Q_D(QFileSystemModel);
+ if (!parent.isValid())
+ return parent;
+
+ QDir dir(filePath(parent));
+ if (!dir.mkdir(name))
+ return QModelIndex();
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);
+ d->addNode(parentNode, name, QFileInfo());
+ Q_ASSERT(parentNode->children.contains(name));
+ QFileSystemModelPrivate::QFileSystemNode *node = parentNode->children[name];
+#if QT_CONFIG(filesystemwatcher)
+ node->populate(d->fileInfoGatherer->getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name)));
+#endif
+ d->addVisibleFiles(parentNode, QStringList(name));
+ return d->index(node);
+}
+
+/*!
+ Returns the complete OR-ed together combination of QFile::Permission for the \a index.
+ */
+QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index) const
+{
+ Q_D(const QFileSystemModel);
+ return d->node(index)->permissions();
+}
+
+/*!
+ Sets the directory that is being watched by the model to \a newPath by
+ installing a \l{QFileSystemWatcher}{file system watcher} on it. Any
+ changes to files and directories within this directory will be
+ reflected in the model.
+
+ If the path is changed, the rootPathChanged() signal will be emitted.
+
+ \note This function does not change the structure of the model or
+ modify the data available to views. In other words, the "root" of
+ the model is \e not changed to include only files and directories
+ within the directory specified by \a newPath in the file system.
+ */
+QModelIndex QFileSystemModel::setRootPath(const QString &newPath)
+{
+ Q_D(QFileSystemModel);
+#ifdef Q_OS_WIN
+#ifdef Q_OS_WIN32
+ QString longNewPath = qt_GetLongPathName(newPath);
+#else
+ QString longNewPath = QDir::fromNativeSeparators(newPath);
+#endif
+#else
+ QString longNewPath = newPath;
+#endif
+ //we remove .. and . from the given path if exist
+ if (!newPath.isEmpty())
+ longNewPath = QDir::cleanPath(longNewPath);
+
+ d->setRootPath = true;
+
+ //user don't ask for the root path ("") but the conversion failed
+ if (!newPath.isEmpty() && longNewPath.isEmpty())
+ return d->index(rootPath());
+
+ if (d->rootDir.path() == longNewPath)
+ return d->index(rootPath());
+
+ auto node = d->node(longNewPath);
+ QFileInfo newPathInfo;
+ if (node && node->hasInformation())
+ newPathInfo = node->fileInfo();
+ else
+ newPathInfo = QFileInfo(longNewPath);
+
+ bool showDrives = (longNewPath.isEmpty() || longNewPath == QFileSystemModelPrivate::myComputer());
+ if (!showDrives && !newPathInfo.exists())
+ return d->index(rootPath());
+
+ //We remove the watcher on the previous path
+ if (!rootPath().isEmpty() && rootPath() != "."_L1) {
+ //This remove the watcher for the old rootPath
+#if QT_CONFIG(filesystemwatcher)
+ d->fileInfoGatherer->removePath(rootPath());
+#endif
+ //This line "marks" the node as dirty, so the next fetchMore
+ //call on the path will ask the gatherer to install a watcher again
+ //But it doesn't re-fetch everything
+ d->node(rootPath())->populatedChildren = false;
+ }
+
+ // We have a new valid root path
+ d->rootDir = QDir(longNewPath);
+ QModelIndex newRootIndex;
+ if (showDrives) {
+ // otherwise dir will become '.'
+ d->rootDir.setPath(""_L1);
+ } else {
+ newRootIndex = d->index(d->rootDir.path());
+ }
+ fetchMore(newRootIndex);
+ emit rootPathChanged(longNewPath);
+ d->forceSort = true;
+ d->delayedSort();
+ return newRootIndex;
+}
+
+/*!
+ The currently set root path
+
+ \sa rootDirectory()
+*/
+QString QFileSystemModel::rootPath() const
+{
+ Q_D(const QFileSystemModel);
+ return d->rootDir.path();
+}
+
+/*!
+ The currently set directory
+
+ \sa rootPath()
+*/
+QDir QFileSystemModel::rootDirectory() const
+{
+ Q_D(const QFileSystemModel);
+ QDir dir(d->rootDir);
+ dir.setNameFilters(nameFilters());
+ dir.setFilter(filter());
+ return dir;
+}
+
+/*!
+ Sets the \a provider of file icons for the directory model.
+*/
+void QFileSystemModel::setIconProvider(QAbstractFileIconProvider *provider)
+{
+ Q_D(QFileSystemModel);
+#if QT_CONFIG(filesystemwatcher)
+ d->fileInfoGatherer->setIconProvider(provider);
+#endif
+ d->root.updateIcon(provider, QString());
+}
+
+/*!
+ Returns the file icon provider for this directory model.
+*/
+QAbstractFileIconProvider *QFileSystemModel::iconProvider() const
+{
+#if QT_CONFIG(filesystemwatcher)
+ Q_D(const QFileSystemModel);
+ return d->fileInfoGatherer->iconProvider();
+#else
+ return nullptr;
+#endif
+}
+
+/*!
+ Sets the directory model's filter to that specified by \a filters.
+
+ Note that the filter you set should always include the QDir::AllDirs enum value,
+ otherwise QFileSystemModel won't be able to read the directory structure.
+
+ \sa QDir::Filters
+*/
+void QFileSystemModel::setFilter(QDir::Filters filters)
+{
+ Q_D(QFileSystemModel);
+ if (d->filters == filters)
+ return;
+ const bool changingCaseSensitivity =
+ filters.testFlag(QDir::CaseSensitive) != d->filters.testFlag(QDir::CaseSensitive);
+ d->filters = filters;
+ if (changingCaseSensitivity)
+ d->rebuildNameFilterRegexps();
+ d->forceSort = true;
+ d->delayedSort();
+}
+
+/*!
+ Returns the filter specified for the directory model.
+
+ If a filter has not been set, the default filter is QDir::AllEntries |
+ QDir::NoDotAndDotDot | QDir::AllDirs.
+
+ \sa QDir::Filters
+*/
+QDir::Filters QFileSystemModel::filter() const
+{
+ Q_D(const QFileSystemModel);
+ return d->filters;
+}
+
+/*!
+ \property QFileSystemModel::resolveSymlinks
+ \brief Whether the directory model should resolve symbolic links
+
+ This is only relevant on Windows.
+
+ By default, this property is \c true.
+
+ \sa QFileSystemModel::Options
+*/
+void QFileSystemModel::setResolveSymlinks(bool enable)
+{
+#if QT_CONFIG(filesystemwatcher)
+ Q_D(QFileSystemModel);
+ d->fileInfoGatherer->setResolveSymlinks(enable);
+#else
+ Q_UNUSED(enable);
+#endif
+}
+
+bool QFileSystemModel::resolveSymlinks() const
+{
+#if QT_CONFIG(filesystemwatcher)
+ Q_D(const QFileSystemModel);
+ return d->fileInfoGatherer->resolveSymlinks();
+#else
+ return false;
+#endif
+}
+
+/*!
+ \property QFileSystemModel::readOnly
+ \brief Whether the directory model allows writing to the file system
+
+ If this property is set to false, the directory model will allow renaming, copying
+ and deleting of files and directories.
+
+ This property is \c true by default
+*/
+void QFileSystemModel::setReadOnly(bool enable)
+{
+ Q_D(QFileSystemModel);
+ d->readOnly = enable;
+}
+
+bool QFileSystemModel::isReadOnly() const
+{
+ Q_D(const QFileSystemModel);
+ return d->readOnly;
+}
+
+/*!
+ \property QFileSystemModel::nameFilterDisables
+ \brief Whether files that don't pass the name filter are hidden or disabled
+
+ This property is \c true by default
+*/
+void QFileSystemModel::setNameFilterDisables(bool enable)
+{
+ Q_D(QFileSystemModel);
+ if (d->nameFilterDisables == enable)
+ return;
+ d->nameFilterDisables = enable;
+ d->forceSort = true;
+ d->delayedSort();
+}
+
+bool QFileSystemModel::nameFilterDisables() const
+{
+ Q_D(const QFileSystemModel);
+ return d->nameFilterDisables;
+}
+
+/*!
+ Sets the name \a filters to apply against the existing files.
+*/
+void QFileSystemModel::setNameFilters(const QStringList &filters)
+{
+#if QT_CONFIG(regularexpression)
+ Q_D(QFileSystemModel);
+
+ if (!d->bypassFilters.isEmpty()) {
+ // update the bypass filter to only bypass the stuff that must be kept around
+ d->bypassFilters.clear();
+ // We guarantee that rootPath will stick around
+ QPersistentModelIndex root(index(rootPath()));
+ const QModelIndexList persistentList = persistentIndexList();
+ for (const auto &persistentIndex : persistentList) {
+ QFileSystemModelPrivate::QFileSystemNode *node = d->node(persistentIndex);
+ while (node) {
+ if (d->bypassFilters.contains(node))
+ break;
+ if (node->isDir())
+ d->bypassFilters[node] = true;
+ node = node->parent;
+ }
+ }
+ }
+
+ d->nameFilters = filters;
+ d->rebuildNameFilterRegexps();
+ d->forceSort = true;
+ d->delayedSort();
+#else
+ Q_UNUSED(filters);
+#endif
+}
+
+/*!
+ Returns a list of filters applied to the names in the model.
+*/
+QStringList QFileSystemModel::nameFilters() const
+{
+#if QT_CONFIG(regularexpression)
+ Q_D(const QFileSystemModel);
+ return d->nameFilters;
+#else
+ return QStringList();
+#endif
+}
+
+/*!
+ \reimp
+*/
+bool QFileSystemModel::event(QEvent *event)
+{
+#if QT_CONFIG(filesystemwatcher)
+ Q_D(QFileSystemModel);
+ if (event->type() == QEvent::LanguageChange) {
+ d->root.retranslateStrings(d->fileInfoGatherer->iconProvider(), QString());
+ return true;
+ }
+#endif
+ return QAbstractItemModel::event(event);
+}
+
+bool QFileSystemModel::rmdir(const QModelIndex &aindex)
+{
+ QString path = filePath(aindex);
+ const bool success = QDir().rmdir(path);
+#if QT_CONFIG(filesystemwatcher)
+ if (success) {
+ QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func());
+ d->fileInfoGatherer->removePath(path);
+ }
+#endif
+ return success;
+}
+
+/*!
+ \internal
+
+ Performed quick listing and see if any files have been added or removed,
+ then fetch more information on visible files.
+ */
+void QFileSystemModelPrivate::directoryChanged(const QString &directory, const QStringList &files)
+{
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory, false);
+ if (parentNode->children.size() == 0)
+ return;
+ QStringList toRemove;
+ QStringList newFiles = files;
+ std::sort(newFiles.begin(), newFiles.end());
+ for (auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) {
+ QStringList::iterator iterator = std::lower_bound(newFiles.begin(), newFiles.end(), i.value()->fileName);
+ if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator))
+ toRemove.append(i.value()->fileName);
+ }
+ for (int i = 0 ; i < toRemove.size() ; ++i )
+ removeNode(parentNode, toRemove[i]);
+}
+
+#if defined(Q_OS_WIN)
+static QString volumeName(const QString &path)
+{
+ IShellItem *item = nullptr;
+ const QString native = QDir::toNativeSeparators(path);
+ HRESULT hr = SHCreateItemFromParsingName(reinterpret_cast<const wchar_t *>(native.utf16()),
+ nullptr, IID_IShellItem,
+ reinterpret_cast<void **>(&item));
+ if (FAILED(hr))
+ return QString();
+ LPWSTR name = nullptr;
+ hr = item->GetDisplayName(SIGDN_NORMALDISPLAY, &name);
+ if (FAILED(hr))
+ return QString();
+ QString result = QString::fromWCharArray(name);
+ CoTaskMemFree(name);
+ item->Release();
+ return result;
+}
+#endif // Q_OS_WIN
+
+/*!
+ \internal
+
+ Adds a new file to the children of parentNode
+
+ *WARNING* this will change the count of children
+*/
+QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo& info)
+{
+ // In the common case, itemLocation == count() so check there first
+ QFileSystemModelPrivate::QFileSystemNode *node = new QFileSystemModelPrivate::QFileSystemNode(fileName, parentNode);
+#if QT_CONFIG(filesystemwatcher)
+ node->populate(info);
+#else
+ Q_UNUSED(info);
+#endif
+#if defined(Q_OS_WIN)
+ //The parentNode is "" so we are listing the drives
+ if (parentNode->fileName.isEmpty())
+ node->volumeName = volumeName(fileName);
+#endif
+ Q_ASSERT(!parentNode->children.contains(fileName));
+ parentNode->children.insert(fileName, node);
+ return node;
+}
+
+/*!
+ \internal
+
+ File at parentNode->children(itemLocation) has been removed, remove from the lists
+ and emit signals if necessary
+
+ *WARNING* this will change the count of children and could change visibleChildren
+ */
+void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode, const QString& name)
+{
+ Q_Q(QFileSystemModel);
+ QModelIndex parent = index(parentNode);
+ bool indexHidden = isHiddenByFilter(parentNode, parent);
+
+ int vLocation = parentNode->visibleLocation(name);
+ if (vLocation >= 0 && !indexHidden)
+ q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
+ translateVisibleLocation(parentNode, vLocation));
+ QFileSystemNode * node = parentNode->children.take(name);
+ delete node;
+ // cleanup sort files after removing rather then re-sorting which is O(n)
+ if (vLocation >= 0)
+ parentNode->visibleChildren.removeAt(vLocation);
+ if (vLocation >= 0 && !indexHidden)
+ q->endRemoveRows();
+}
+
+/*!
+ \internal
+
+ File at parentNode->children(itemLocation) was not visible before, but now should be
+ and emit signals if necessary.
+
+ *WARNING* this will change the visible count
+ */
+void QFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles)
+{
+ Q_Q(QFileSystemModel);
+ QModelIndex parent = index(parentNode);
+ bool indexHidden = isHiddenByFilter(parentNode, parent);
+ if (!indexHidden) {
+ q->beginInsertRows(parent, parentNode->visibleChildren.size() , parentNode->visibleChildren.size() + newFiles.size() - 1);
+ }
+
+ if (parentNode->dirtyChildrenIndex == -1)
+ parentNode->dirtyChildrenIndex = parentNode->visibleChildren.size();
+
+ for (const auto &newFile : newFiles) {
+ parentNode->visibleChildren.append(newFile);
+ parentNode->children.value(newFile)->isVisible = true;
+ }
+ if (!indexHidden)
+ q->endInsertRows();
+}
+
+/*!
+ \internal
+
+ File was visible before, but now should NOT be
+
+ *WARNING* this will change the visible count
+ */
+void QFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode, int vLocation)
+{
+ Q_Q(QFileSystemModel);
+ if (vLocation == -1)
+ return;
+ QModelIndex parent = index(parentNode);
+ bool indexHidden = isHiddenByFilter(parentNode, parent);
+ if (!indexHidden)
+ q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
+ translateVisibleLocation(parentNode, vLocation));
+ parentNode->children.value(parentNode->visibleChildren.at(vLocation))->isVisible = false;
+ parentNode->visibleChildren.removeAt(vLocation);
+ if (!indexHidden)
+ q->endRemoveRows();
+}
+
+/*!
+ \internal
+
+ The thread has received new information about files,
+ update and emit dataChanged if it has actually changed.
+ */
+void QFileSystemModelPrivate::fileSystemChanged(const QString &path,
+ const QList<std::pair<QString, QFileInfo>> &updates)
+{
+#if QT_CONFIG(filesystemwatcher)
+ Q_Q(QFileSystemModel);
+ QList<QString> rowsToUpdate;
+ QStringList newFiles;
+ QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path, false);
+ QModelIndex parentIndex = index(parentNode);
+ for (const auto &update : updates) {
+ QString fileName = update.first;
+ Q_ASSERT(!fileName.isEmpty());
+ QExtendedInformation info = fileInfoGatherer->getInfo(update.second);
+ bool previouslyHere = parentNode->children.contains(fileName);
+ if (!previouslyHere) {
+ addNode(parentNode, fileName, info.fileInfo());
+ }
+ QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName);
+ bool isCaseSensitive = parentNode->caseSensitive();
+ if (isCaseSensitive) {
+ if (node->fileName != fileName)
+ continue;
+ } else {
+ if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0)
+ continue;
+ }
+ if (isCaseSensitive) {
+ Q_ASSERT(node->fileName == fileName);
+ } else {
+ node->fileName = fileName;
+ }
+
+ if (*node != info ) {
+ node->populate(info);
+ bypassFilters.remove(node);
+ // brand new information.
+ if (filtersAcceptsNode(node)) {
+ if (!node->isVisible) {
+ newFiles.append(fileName);
+ } else {
+ rowsToUpdate.append(fileName);
+ }
+ } else {
+ if (node->isVisible) {
+ int visibleLocation = parentNode->visibleLocation(fileName);
+ removeVisibleFile(parentNode, visibleLocation);
+ } else {
+ // The file is not visible, don't do anything
+ }
+ }
+ }
+ }
+
+ // bundle up all of the changed signals into as few as possible.
+ std::sort(rowsToUpdate.begin(), rowsToUpdate.end());
+ QString min;
+ QString max;
+ for (const QString &value : std::as_const(rowsToUpdate)) {
+ //##TODO is there a way to bundle signals with QString as the content of the list?
+ /*if (min.isEmpty()) {
+ min = value;
+ if (i != rowsToUpdate.count() - 1)
+ continue;
+ }
+ if (i != rowsToUpdate.count() - 1) {
+ if ((value == min + 1 && max.isEmpty()) || value == max + 1) {
+ max = value;
+ continue;
+ }
+ }*/
+ max = value;
+ min = value;
+ int visibleMin = parentNode->visibleLocation(min);
+ int visibleMax = parentNode->visibleLocation(max);
+ if (visibleMin >= 0
+ && visibleMin < parentNode->visibleChildren.size()
+ && parentNode->visibleChildren.at(visibleMin) == min
+ && visibleMax >= 0) {
+ // don't use NumColumns here, a subclass might override columnCount
+ const int lastColumn = q->columnCount(parentIndex) - 1;
+ const QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMin),
+ QFileSystemModelPrivate::NameColumn, parentIndex);
+ const QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMax),
+ lastColumn, parentIndex);
+ // We document that emitting dataChanged with indexes that don't have the
+ // same parent is undefined behavior.
+ Q_ASSERT(bottom.parent() == top.parent());
+ emit q->dataChanged(top, bottom);
+ }
+
+ /*min = QString();
+ max = QString();*/
+ }
+
+ if (newFiles.size() > 0) {
+ addVisibleFiles(parentNode, newFiles);
+ }
+
+ if (newFiles.size() > 0 || (sortColumn != 0 && rowsToUpdate.size() > 0)) {
+ forceSort = true;
+ delayedSort();
+ }
+#else
+ Q_UNUSED(path);
+ Q_UNUSED(updates);
+#endif // filesystemwatcher
+}
+
+/*!
+ \internal
+*/
+void QFileSystemModelPrivate::resolvedName(const QString &fileName, const QString &resolvedName)
+{
+ resolvedSymLinks[fileName] = resolvedName;
+}
+
+#if QT_CONFIG(filesystemwatcher) && defined(Q_OS_WIN)
+// Remove file system watchers at/below the index and return a list of previously
+// watched files. This should be called prior to operations like rename/remove
+// which might fail due to watchers on platforms like Windows. The watchers
+// should be restored on failure.
+QStringList QFileSystemModelPrivate::unwatchPathsAt(const QModelIndex &index)
+{
+ const QFileSystemModelPrivate::QFileSystemNode *indexNode = node(index);
+ if (indexNode == nullptr)
+ return QStringList();
+ const Qt::CaseSensitivity caseSensitivity = indexNode->caseSensitive()
+ ? Qt::CaseSensitive : Qt::CaseInsensitive;
+ const QString path = indexNode->fileInfo().absoluteFilePath();
+
+ QStringList result;
+ const auto filter = [path, caseSensitivity] (const QString &watchedPath)
+ {
+ const int pathSize = path.size();
+ if (pathSize == watchedPath.size()) {
+ return path.compare(watchedPath, caseSensitivity) == 0;
+ } else if (watchedPath.size() > pathSize) {
+ return watchedPath.at(pathSize) == u'/'
+ && watchedPath.startsWith(path, caseSensitivity);
+ }
+ return false;
+ };
+
+ const QStringList &watchedFiles = fileInfoGatherer->watchedFiles();
+ std::copy_if(watchedFiles.cbegin(), watchedFiles.cend(),
+ std::back_inserter(result), filter);
+
+ const QStringList &watchedDirectories = fileInfoGatherer->watchedDirectories();
+ std::copy_if(watchedDirectories.cbegin(), watchedDirectories.cend(),
+ std::back_inserter(result), filter);
+
+ fileInfoGatherer->unwatchPaths(result);
+ return result;
+}
+#endif // filesystemwatcher && Q_OS_WIN
+
+QFileSystemModelPrivate::QFileSystemModelPrivate()
+#if QT_CONFIG(filesystemwatcher)
+ : fileInfoGatherer(new QFileInfoGatherer)
+#endif // filesystemwatcher
+{
+}
+
+QFileSystemModelPrivate::~QFileSystemModelPrivate()
+{
+#if QT_CONFIG(filesystemwatcher)
+ fileInfoGatherer->requestAbort();
+ if (!fileInfoGatherer->wait(1000)) {
+ // If the thread hangs, perhaps because the network was disconnected
+ // while the gatherer was stat'ing a remote file, then don't block
+ // shutting down the model (which might block a file dialog and the
+ // main thread). Schedule the gatherer for later deletion; it's
+ // destructor will wait for the thread to finish.
+ auto *rawGatherer = fileInfoGatherer.release();
+ rawGatherer->deleteLater();
+ }
+#endif // filesystemwatcher
+}
+
+/*!
+ \internal
+*/
+void QFileSystemModelPrivate::init()
+{
+ Q_Q(QFileSystemModel);
+
+ delayedSortTimer.setSingleShot(true);
+
+ qRegisterMetaType<QList<std::pair<QString, QFileInfo>>>();
+#if QT_CONFIG(filesystemwatcher)
+ QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::newListOfFiles,
+ this, &QFileSystemModelPrivate::directoryChanged);
+ QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::updates,
+ this, &QFileSystemModelPrivate::fileSystemChanged);
+ QObjectPrivate::connect(fileInfoGatherer.get(), &QFileInfoGatherer::nameResolved,
+ this, &QFileSystemModelPrivate::resolvedName);
+ q->connect(fileInfoGatherer.get(), &QFileInfoGatherer::directoryLoaded,
+ q, &QFileSystemModel::directoryLoaded);
+#endif // filesystemwatcher
+ QObjectPrivate::connect(&delayedSortTimer, &QTimer::timeout,
+ this, &QFileSystemModelPrivate::performDelayedSort,
+ Qt::QueuedConnection);
+}
+
+/*!
+ \internal
+
+ Returns \c false if node doesn't pass the filters otherwise true
+
+ QDir::Modified is not supported
+ QDir::Drives is not supported
+*/
+bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) const
+{
+ // When the model is set to only show files, then a node representing a dir
+ // should be hidden regardless of bypassFilters.
+ // QTBUG-74471
+ const bool hideDirs = (filters & (QDir::Dirs | QDir::AllDirs)) == 0;
+ const bool shouldHideDirNode = hideDirs && node->isDir();
+
+ // always accept drives
+ if (node->parent == &root || (!shouldHideDirNode && bypassFilters.contains(node)))
+ return true;
+
+ // If we don't know anything yet don't accept it
+ if (!node->hasInformation())
+ return false;
+
+ const bool filterPermissions = ((filters & QDir::PermissionMask)
+ && (filters & QDir::PermissionMask) != QDir::PermissionMask);
+ const bool hideFiles = !(filters & QDir::Files);
+ const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
+ const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
+ const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
+ const bool hideHidden = !(filters & QDir::Hidden);
+ const bool hideSystem = !(filters & QDir::System);
+ const bool hideSymlinks = (filters & QDir::NoSymLinks);
+ const bool hideDot = (filters & QDir::NoDot);
+ const bool hideDotDot = (filters & QDir::NoDotDot);
+
+ // Note that we match the behavior of entryList and not QFileInfo on this.
+ bool isDot = (node->fileName == "."_L1);
+ bool isDotDot = (node->fileName == ".."_L1);
+ if ( (hideHidden && !(isDot || isDotDot) && node->isHidden())
+ || (hideSystem && node->isSystem())
+ || (hideDirs && node->isDir())
+ || (hideFiles && node->isFile())
+ || (hideSymlinks && node->isSymLink())
+ || (hideReadable && node->isReadable())
+ || (hideWritable && node->isWritable())
+ || (hideExecutable && node->isExecutable())
+ || (hideDot && isDot)
+ || (hideDotDot && isDotDot))
+ return false;
+
+ return nameFilterDisables || passNameFilters(node);
+}
+
+/*
+ \internal
+
+ Returns \c true if node passes the name filters and should be visible.
+ */
+bool QFileSystemModelPrivate::passNameFilters(const QFileSystemNode *node) const
+{
+#if QT_CONFIG(regularexpression)
+ if (nameFilters.isEmpty())
+ return true;
+
+ // Check the name regularexpression filters
+ if (!(node->isDir() && (filters & QDir::AllDirs))) {
+ const auto matchesNodeFileName = [node](const QRegularExpression &re)
+ {
+ return node->fileName.contains(re);
+ };
+ return std::any_of(nameFiltersRegexps.begin(),
+ nameFiltersRegexps.end(),
+ matchesNodeFileName);
+ }
+#else
+ Q_UNUSED(node);
+#endif
+ return true;
+}
+
+#if QT_CONFIG(regularexpression)
+void QFileSystemModelPrivate::rebuildNameFilterRegexps()
+{
+ nameFiltersRegexps.clear();
+ nameFiltersRegexps.reserve(nameFilters.size());
+ const auto cs = (filters & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
+ const auto convertWildcardToRegexp = [cs](const QString &nameFilter)
+ {
+ return QRegularExpression::fromWildcard(nameFilter, cs);
+ };
+ std::transform(nameFilters.constBegin(),
+ nameFilters.constEnd(),
+ std::back_inserter(nameFiltersRegexps),
+ convertWildcardToRegexp);
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qfilesystemmodel.cpp"
diff --git a/src/gui/itemmodels/qfilesystemmodel.h b/src/gui/itemmodels/qfilesystemmodel.h
new file mode 100644
index 0000000000..1fd1041f15
--- /dev/null
+++ b/src/gui/itemmodels/qfilesystemmodel.h
@@ -0,0 +1,150 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFILESYSTEMMODEL_H
+#define QFILESYSTEMMODEL_H
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qdir.h>
+#include <QtGui/qicon.h>
+
+QT_REQUIRE_CONFIG(filesystemmodel);
+
+QT_BEGIN_NAMESPACE
+
+class ExtendedInformation;
+class QFileSystemModelPrivate;
+class QAbstractFileIconProvider;
+
+class Q_GUI_EXPORT QFileSystemModel : public QAbstractItemModel
+{
+ Q_OBJECT
+ Q_PROPERTY(bool resolveSymlinks READ resolveSymlinks WRITE setResolveSymlinks)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ Q_PROPERTY(bool nameFilterDisables READ nameFilterDisables WRITE setNameFilterDisables)
+ Q_PROPERTY(Options options READ options WRITE setOptions)
+
+Q_SIGNALS:
+ void rootPathChanged(const QString &newPath);
+ void fileRenamed(const QString &path, const QString &oldName, const QString &newName);
+ void directoryLoaded(const QString &path);
+
+public:
+ // ### Qt 7: renumber these values to be before Qt::UserRole comment.
+ enum Roles {
+ FileIconRole = Qt::DecorationRole,
+ FileInfoRole = Qt::UserRole - 1,
+ FilePathRole = Qt::UserRole + 1,
+ FileNameRole = Qt::UserRole + 2,
+ FilePermissions = Qt::UserRole + 3,
+ };
+
+ enum Option
+ {
+ DontWatchForChanges = 0x00000001,
+ DontResolveSymlinks = 0x00000002,
+ DontUseCustomDirectoryIcons = 0x00000004
+ };
+ Q_ENUM(Option)
+ Q_DECLARE_FLAGS(Options, Option)
+
+ explicit QFileSystemModel(QObject *parent = nullptr);
+ ~QFileSystemModel();
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex index(const QString &path, int column = 0) const;
+ QModelIndex parent(const QModelIndex &child) const override;
+ using QObject::parent;
+ QModelIndex sibling(int row, int column, const QModelIndex &idx) const override;
+ bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
+ bool canFetchMore(const QModelIndex &parent) const override;
+ void fetchMore(const QModelIndex &parent) override;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant myComputer(int role = Qt::DisplayRole) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+
+ void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
+
+ QStringList mimeTypes() const override;
+ QMimeData *mimeData(const QModelIndexList &indexes) const override;
+ bool dropMimeData(const QMimeData *data, Qt::DropAction action,
+ int row, int column, const QModelIndex &parent) override;
+ Qt::DropActions supportedDropActions() const override;
+ QHash<int, QByteArray> roleNames() const override;
+
+ // QFileSystemModel specific API
+ QModelIndex setRootPath(const QString &path);
+ QString rootPath() const;
+ QDir rootDirectory() const;
+
+ void setIconProvider(QAbstractFileIconProvider *provider);
+ QAbstractFileIconProvider *iconProvider() const;
+
+ void setFilter(QDir::Filters filters);
+ QDir::Filters filter() const;
+
+ void setResolveSymlinks(bool enable);
+ bool resolveSymlinks() const;
+
+ void setReadOnly(bool enable);
+ bool isReadOnly() const;
+
+ void setNameFilterDisables(bool enable);
+ bool nameFilterDisables() const;
+
+ void setNameFilters(const QStringList &filters);
+ QStringList nameFilters() const;
+
+ void setOption(Option option, bool on = true);
+ bool testOption(Option option) const;
+ void setOptions(Options options);
+ Options options() const;
+
+ QString filePath(const QModelIndex &index) const;
+ bool isDir(const QModelIndex &index) const;
+ qint64 size(const QModelIndex &index) const;
+ QString type(const QModelIndex &index) const;
+
+ QDateTime lastModified(const QModelIndex &index) const;
+ QDateTime lastModified(const QModelIndex &index, const QTimeZone &tz) const;
+
+ QModelIndex mkdir(const QModelIndex &parent, const QString &name);
+ bool rmdir(const QModelIndex &index);
+ inline QString fileName(const QModelIndex &index) const;
+ inline QIcon fileIcon(const QModelIndex &index) const;
+ QFile::Permissions permissions(const QModelIndex &index) const;
+ QFileInfo fileInfo(const QModelIndex &index) const;
+ bool remove(const QModelIndex &index);
+
+protected:
+ QFileSystemModel(QFileSystemModelPrivate &, QObject *parent = nullptr);
+ void timerEvent(QTimerEvent *event) override;
+ bool event(QEvent *event) override;
+
+private:
+ Q_DECLARE_PRIVATE(QFileSystemModel)
+ Q_DISABLE_COPY(QFileSystemModel)
+
+ friend class QFileDialogPrivate;
+};
+
+inline QString QFileSystemModel::fileName(const QModelIndex &aindex) const
+{ return aindex.data(Qt::DisplayRole).toString(); }
+inline QIcon QFileSystemModel::fileIcon(const QModelIndex &aindex) const
+{ return qvariant_cast<QIcon>(aindex.data(Qt::DecorationRole)); }
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QFileSystemModel::Options)
+
+QT_END_NAMESPACE
+
+#endif // QFILESYSTEMMODEL_H
diff --git a/src/gui/itemmodels/qfilesystemmodel_p.h b/src/gui/itemmodels/qfilesystemmodel_p.h
new file mode 100644
index 0000000000..e01b0d56e6
--- /dev/null
+++ b/src/gui/itemmodels/qfilesystemmodel_p.h
@@ -0,0 +1,308 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFILESYSTEMMODEL_P_H
+#define QFILESYSTEMMODEL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtguiglobal_p.h>
+#include "qfilesystemmodel.h"
+
+#include <private/qabstractitemmodel_p.h>
+#include <qabstractitemmodel.h>
+#include "qfileinfogatherer_p.h"
+#include <qpair.h>
+#include <qdir.h>
+#include <qicon.h>
+#include <qfileinfo.h>
+#include <qtimer.h>
+#include <qhash.h>
+
+#include <vector>
+
+QT_REQUIRE_CONFIG(filesystemmodel);
+
+QT_BEGIN_NAMESPACE
+
+class ExtendedInformation;
+class QFileSystemModelPrivate;
+class QFileIconProvider;
+
+#if defined(Q_OS_WIN)
+class QFileSystemModelNodePathKey : public QString
+{
+public:
+ QFileSystemModelNodePathKey() {}
+ QFileSystemModelNodePathKey(const QString &other) : QString(other) {}
+ QFileSystemModelNodePathKey(const QFileSystemModelNodePathKey &other) : QString(other) {}
+ bool operator==(const QFileSystemModelNodePathKey &other) const { return !compare(other, Qt::CaseInsensitive); }
+};
+
+Q_DECLARE_TYPEINFO(QFileSystemModelNodePathKey, Q_RELOCATABLE_TYPE);
+
+inline size_t qHash(const QFileSystemModelNodePathKey &key, size_t seed = 0)
+{
+ return qHash(key.toCaseFolded(), seed);
+}
+#else // Q_OS_WIN
+typedef QString QFileSystemModelNodePathKey;
+#endif
+
+class Q_GUI_EXPORT QFileSystemModelPrivate : public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QFileSystemModel)
+
+public:
+ enum {
+ NameColumn,
+ SizeColumn,
+ TypeColumn,
+ TimeColumn,
+ NumColumns = 4
+ };
+
+ class QFileSystemNode
+ {
+ public:
+ Q_DISABLE_COPY_MOVE(QFileSystemNode)
+
+ explicit QFileSystemNode(const QString &filename = QString(), QFileSystemNode *p = nullptr)
+ : fileName(filename), parent(p) {}
+ ~QFileSystemNode() {
+ qDeleteAll(children);
+ delete info;
+ }
+
+ QString fileName;
+#if defined(Q_OS_WIN)
+ QString volumeName;
+#endif
+
+ inline qint64 size() const { if (info && !info->isDir()) return info->size(); return 0; }
+ inline QString type() const { if (info) return info->displayType; return QLatin1StringView(""); }
+ inline QDateTime lastModified(const QTimeZone &tz) const { return info ? info->lastModified(tz) : QDateTime(); }
+ inline QFile::Permissions permissions() const { if (info) return info->permissions(); return { }; }
+ inline bool isReadable() const { return ((permissions() & QFile::ReadUser) != 0); }
+ inline bool isWritable() const { return ((permissions() & QFile::WriteUser) != 0); }
+ inline bool isExecutable() const { return ((permissions() & QFile::ExeUser) != 0); }
+ inline bool isDir() const {
+ if (info)
+ return info->isDir();
+ if (children.size() > 0)
+ return true;
+ return false;
+ }
+ inline QFileInfo fileInfo() const { if (info) return info->fileInfo(); return QFileInfo(); }
+ inline bool isFile() const { if (info) return info->isFile(); return true; }
+ inline bool isSystem() const { if (info) return info->isSystem(); return true; }
+ inline bool isHidden() const { if (info) return info->isHidden(); return false; }
+ inline bool isSymLink(bool ignoreNtfsSymLinks = false) const { return info && info->isSymLink(ignoreNtfsSymLinks); }
+ inline bool caseSensitive() const { if (info) return info->isCaseSensitive(); return false; }
+ inline QIcon icon() const { if (info) return info->icon; return QIcon(); }
+
+ inline bool operator <(const QFileSystemNode &node) const {
+ if (caseSensitive() || node.caseSensitive())
+ return fileName < node.fileName;
+ return QString::compare(fileName, node.fileName, Qt::CaseInsensitive) < 0;
+ }
+ inline bool operator >(const QString &name) const {
+ if (caseSensitive())
+ return fileName > name;
+ return QString::compare(fileName, name, Qt::CaseInsensitive) > 0;
+ }
+ inline bool operator <(const QString &name) const {
+ if (caseSensitive())
+ return fileName < name;
+ return QString::compare(fileName, name, Qt::CaseInsensitive) < 0;
+ }
+ inline bool operator !=(const QExtendedInformation &fileInfo) const {
+ return !operator==(fileInfo);
+ }
+ bool operator ==(const QString &name) const {
+ if (caseSensitive())
+ return fileName == name;
+ return QString::compare(fileName, name, Qt::CaseInsensitive) == 0;
+ }
+ bool operator ==(const QExtendedInformation &fileInfo) const {
+ return info && (*info == fileInfo);
+ }
+
+ inline bool hasInformation() const { return info != nullptr; }
+
+ void populate(const QExtendedInformation &fileInfo) {
+ if (!info)
+ info = new QExtendedInformation(fileInfo.fileInfo());
+ (*info) = fileInfo;
+ }
+
+ // children shouldn't normally be accessed directly, use node()
+ inline int visibleLocation(const QString &childName) {
+ return visibleChildren.indexOf(childName);
+ }
+ void updateIcon(QAbstractFileIconProvider *iconProvider, const QString &path) {
+ if (!iconProvider)
+ return;
+
+ if (info)
+ info->icon = iconProvider->icon(QFileInfo(path));
+
+ for (QFileSystemNode *child : std::as_const(children)) {
+ //On windows the root (My computer) has no path so we don't want to add a / for nothing (e.g. /C:/)
+ if (!path.isEmpty()) {
+ if (path.endsWith(u'/'))
+ child->updateIcon(iconProvider, path + child->fileName);
+ else
+ child->updateIcon(iconProvider, path + u'/' + child->fileName);
+ } else
+ child->updateIcon(iconProvider, child->fileName);
+ }
+ }
+
+ void retranslateStrings(QAbstractFileIconProvider *iconProvider, const QString &path) {
+ if (!iconProvider)
+ return;
+
+ if (info)
+ info->displayType = iconProvider->type(QFileInfo(path));
+ for (QFileSystemNode *child : std::as_const(children)) {
+ //On windows the root (My computer) has no path so we don't want to add a / for nothing (e.g. /C:/)
+ if (!path.isEmpty()) {
+ if (path.endsWith(u'/'))
+ child->retranslateStrings(iconProvider, path + child->fileName);
+ else
+ child->retranslateStrings(iconProvider, path + u'/' + child->fileName);
+ } else
+ child->retranslateStrings(iconProvider, child->fileName);
+ }
+ }
+
+ QHash<QFileSystemModelNodePathKey, QFileSystemNode *> children;
+ QList<QString> visibleChildren;
+ QExtendedInformation *info = nullptr;
+ QFileSystemNode *parent;
+ int dirtyChildrenIndex = -1;
+ bool populatedChildren = false;
+ bool isVisible = false;
+ };
+
+ QFileSystemModelPrivate();
+ ~QFileSystemModelPrivate();
+ void init();
+ /*
+ \internal
+
+ Return true if index which is owned by node is hidden by the filter.
+ */
+ inline bool isHiddenByFilter(QFileSystemNode *indexNode, const QModelIndex &index) const
+ {
+ return (indexNode != &root && !index.isValid());
+ }
+ QFileSystemNode *node(const QModelIndex &index) const;
+ QFileSystemNode *node(const QString &path, bool fetch = true) const;
+ inline QModelIndex index(const QString &path, int column = 0) { return index(node(path), column); }
+ QModelIndex index(const QFileSystemNode *node, int column = 0) const;
+ bool filtersAcceptsNode(const QFileSystemNode *node) const;
+ bool passNameFilters(const QFileSystemNode *node) const;
+ void removeNode(QFileSystemNode *parentNode, const QString &name);
+ QFileSystemNode* addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo &info);
+ void addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles);
+ void removeVisibleFile(QFileSystemNode *parentNode, int visibleLocation);
+ void sortChildren(int column, const QModelIndex &parent);
+
+ inline int translateVisibleLocation(QFileSystemNode *parent, int row) const {
+ if (sortOrder != Qt::AscendingOrder) {
+ if (parent->dirtyChildrenIndex == -1)
+ return parent->visibleChildren.size() - row - 1;
+
+ if (row < parent->dirtyChildrenIndex)
+ return parent->dirtyChildrenIndex - row - 1;
+ }
+
+ return row;
+ }
+
+ inline static QString myComputer() {
+ // ### TODO We should query the system to find out what the string should be
+ // XP == "My Computer",
+ // Vista == "Computer",
+ // OS X == "Computer" (sometime user generated) "Benjamin's PowerBook G4"
+#ifdef Q_OS_WIN
+ return QFileSystemModel::tr("My Computer");
+#else
+ return QFileSystemModel::tr("Computer");
+#endif
+ }
+
+ inline void delayedSort() {
+ if (!delayedSortTimer.isActive())
+ delayedSortTimer.start(0);
+ }
+
+ QIcon icon(const QModelIndex &index) const;
+ QString name(const QModelIndex &index) const;
+ QString displayName(const QModelIndex &index) const;
+ QString filePath(const QModelIndex &index) const;
+ QString size(const QModelIndex &index) const;
+ static QString size(qint64 bytes);
+ QString type(const QModelIndex &index) const;
+ QString time(const QModelIndex &index) const;
+
+ void directoryChanged(const QString &directory, const QStringList &list);
+ void performDelayedSort();
+ void fileSystemChanged(const QString &path, const QList<std::pair<QString, QFileInfo>> &);
+ void resolvedName(const QString &fileName, const QString &resolvedName);
+
+ QDir rootDir;
+#if QT_CONFIG(filesystemwatcher)
+# ifdef Q_OS_WIN
+ QStringList unwatchPathsAt(const QModelIndex &);
+ void watchPaths(const QStringList &paths) { fileInfoGatherer->watchPaths(paths); }
+# endif // Q_OS_WIN
+ std::unique_ptr<QFileInfoGatherer> fileInfoGatherer;
+#endif // filesystemwatcher
+ QTimer delayedSortTimer;
+ QHash<const QFileSystemNode*, bool> bypassFilters;
+#if QT_CONFIG(regularexpression)
+ QStringList nameFilters;
+ std::vector<QRegularExpression> nameFiltersRegexps;
+ void rebuildNameFilterRegexps();
+#endif
+ QHash<QString, QString> resolvedSymLinks;
+
+ QFileSystemNode root;
+
+ struct Fetching {
+ QString dir;
+ QString file;
+ const QFileSystemNode *node;
+ };
+ QList<Fetching> toFetch;
+
+ QBasicTimer fetchingTimer;
+
+ QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs;
+ int sortColumn = 0;
+ Qt::SortOrder sortOrder = Qt::AscendingOrder;
+ bool forceSort = true;
+ bool readOnly = true;
+ bool setRootPath = false;
+ bool nameFilterDisables = true; // false on windows, true on mac and unix
+ // This flag is an optimization for QFileDialog. It enables a sort which is
+ // not recursive, meaning we sort only what we see.
+ bool disableRecursiveSort = false;
+};
+Q_DECLARE_TYPEINFO(QFileSystemModelPrivate::Fetching, Q_RELOCATABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/itemmodels/qstandarditemmodel.cpp b/src/gui/itemmodels/qstandarditemmodel.cpp
index ebb4d641eb..8b3e381431 100644
--- a/src/gui/itemmodels/qstandarditemmodel.cpp
+++ b/src/gui/itemmodels/qstandarditemmodel.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qstandarditemmodel.h"
@@ -44,10 +8,10 @@
#include <QtCore/qmap.h>
#include <QtCore/qpair.h>
#include <QtCore/qvariant.h>
-#include <QtCore/qvector.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qbitarray.h>
#include <QtCore/qmimedata.h>
+#include <QtCore/qiodevice.h>
#include <private/qduplicatetracker_p.h>
#include <private/qstandarditemmodel_p.h>
#include <qdebug.h>
@@ -55,6 +19,11 @@
QT_BEGIN_NAMESPACE
+// Used internally to store the flags
+namespace {
+constexpr auto DataFlagsRole = Qt::ItemDataRole(Qt::UserRole - 1);
+}
+
static inline QString qStandardItemModelDataListMimeType()
{
return QStringLiteral("application/x-qstandarditemmodeldatalist");
@@ -190,7 +159,7 @@ void QStandardItemPrivate::childDeleted(QStandardItem *child)
int index = childIndex(child);
Q_ASSERT(index != -1);
const auto modelIndex = child->index();
- children.replace(index, 0);
+ children.replace(index, nullptr);
emit model->dataChanged(modelIndex, modelIndex);
}
@@ -274,11 +243,11 @@ void QStandardItemPrivate::setItemData(const QMap<int, QVariant> &roles)
std::sort(values.begin(), values.end(), byRole);
/*
- Create a vector of QStandardItemData that will contain the original values
+ Create a list of QStandardItemData that will contain the original values
if the matching role is not contained in roles, the new value if it is and
if the new value is an invalid QVariant, it will be removed.
*/
- QVector<QStandardItemData> newValues;
+ QList<QStandardItemData> newValues;
newValues.reserve(values.size());
roleMapStandardItemDataUnion(roles.keyValueBegin(),
roles.keyValueEnd(),
@@ -288,7 +257,7 @@ void QStandardItemPrivate::setItemData(const QMap<int, QVariant> &roles)
if (newValues != values) {
values.swap(newValues);
if (model) {
- QVector<int> roleKeys;
+ QList<int> roleKeys;
roleKeys.reserve(roles.size() + 1);
bool hasEditRole = false;
bool hasDisplayRole = false;
@@ -311,14 +280,12 @@ void QStandardItemPrivate::setItemData(const QMap<int, QVariant> &roles)
/*!
\internal
*/
-const QMap<int, QVariant> QStandardItemPrivate::itemData() const
+QMap<int, QVariant> QStandardItemPrivate::itemData() const
{
QMap<int, QVariant> result;
- QVector<QStandardItemData>::const_iterator it;
- for (it = values.cbegin(); it != values.cend(); ++it){
- // Qt::UserRole - 1 is used internally to store the flags
- if (it->role != Qt::UserRole - 1)
- result.insert(it->role, it->value);
+ for (const auto &data : values) {
+ if (data.role != DataFlagsRole)
+ result.insert(data.role, data.value);
}
return result;
}
@@ -332,8 +299,8 @@ void QStandardItemPrivate::sortChildren(int column, Qt::SortOrder order)
if (column >= columnCount())
return;
- QVector<QPair<QStandardItem*, int> > sortable;
- QVector<int> unsortable;
+ QList<QPair<QStandardItem*, int> > sortable;
+ QList<int> unsortable;
sortable.reserve(rowCount());
unsortable.reserve(rowCount());
@@ -355,11 +322,11 @@ void QStandardItemPrivate::sortChildren(int column, Qt::SortOrder order)
}
QModelIndexList changedPersistentIndexesFrom, changedPersistentIndexesTo;
- QVector<QStandardItem*> sorted_children(children.count());
+ QList<QStandardItem*> sorted_children(children.size());
for (int i = 0; i < rowCount(); ++i) {
- int r = (i < sortable.count()
+ int r = (i < sortable.size()
? sortable.at(i).second
- : unsortable.at(i - sortable.count()));
+ : unsortable.at(i - sortable.size()));
for (int c = 0; c < columnCount(); ++c) {
QStandardItem *itm = q->child(r, c);
sorted_children[childIndex(i, c)] = itm;
@@ -380,7 +347,7 @@ void QStandardItemPrivate::sortChildren(int column, Qt::SortOrder order)
model->changePersistentIndexList(changedPersistentIndexesFrom, changedPersistentIndexesTo);
}
- QVector<QStandardItem*>::iterator it;
+ QList<QStandardItem*>::iterator it;
for (it = children.begin(); it != children.end(); ++it) {
if (*it)
(*it)->d_func()->sortChildren(column, order);
@@ -406,8 +373,8 @@ void QStandardItemPrivate::setModel(QStandardItemModel *mod)
itm->d_func()->model->d_func()->invalidatePersistentIndex(itm->d_func()->model->indexFromItem(itm));
}
itm->d_func()->model = mod;
- const QVector<QStandardItem*> &childList = itm->d_func()->children;
- for (int i = 0; i < childList.count(); ++i) {
+ const QList<QStandardItem*> &childList = itm->d_func()->children;
+ for (int i = 0; i < childList.size(); ++i) {
QStandardItem *chi = childList.at(i);
if (chi)
stack.push(chi);
@@ -420,9 +387,7 @@ void QStandardItemPrivate::setModel(QStandardItemModel *mod)
\internal
*/
QStandardItemModelPrivate::QStandardItemModelPrivate()
- : root(new QStandardItem),
- itemPrototype(nullptr),
- sortRole(Qt::DisplayRole)
+ : root(new QStandardItem), itemPrototype(nullptr)
{
root->setFlags(Qt::ItemIsDropEnabled);
}
@@ -470,7 +435,7 @@ bool QStandardItemPrivate::insertRows(int row, const QList<QStandardItem*> &item
Q_Q(QStandardItem);
if ((row < 0) || (row > rowCount()) || items.isEmpty())
return false;
- int count = items.count();
+ int count = items.size();
if (model)
model->d_func()->rowsAboutToBeInserted(q, row, row + count - 1);
if (rowCount() == 0) {
@@ -482,9 +447,9 @@ bool QStandardItemPrivate::insertRows(int row, const QList<QStandardItem*> &item
rows += count;
int index = childIndex(row, 0);
if (index != -1)
- children.insert(index, columnCount() * count, 0);
+ children.insert(index, columnCount() * count, nullptr);
}
- for (int i = 0; i < items.count(); ++i) {
+ for (int i = 0; i < items.size(); ++i) {
QStandardItem *item = items.at(i);
item->d_func()->model = model;
item->d_func()->parent = q;
@@ -512,11 +477,11 @@ bool QStandardItemPrivate::insertRows(int row, int count, const QList<QStandardI
rows += count;
int index = childIndex(row, 0);
if (index != -1)
- children.insert(index, columnCount() * count, 0);
+ children.insert(index, columnCount() * count, nullptr);
}
if (!items.isEmpty()) {
int index = childIndex(row, 0);
- int limit = qMin(items.count(), columnCount() * count);
+ int limit = qMin(items.size(), columnCount() * count);
for (int i = 0; i < limit; ++i) {
QStandardItem *item = items.at(i);
if (item) {
@@ -556,12 +521,12 @@ bool QStandardItemPrivate::insertColumns(int column, int count, const QList<QSta
columns += count;
int index = childIndex(0, column);
for (int row = 0; row < rowCount(); ++row) {
- children.insert(index, count, 0);
+ children.insert(index, count, nullptr);
index += columnCount();
}
}
if (!items.isEmpty()) {
- int limit = qMin(items.count(), rowCount() * count);
+ int limit = qMin(items.size(), rowCount() * count);
for (int i = 0; i < limit; ++i) {
QStandardItem *item = items.at(i);
if (item) {
@@ -589,7 +554,7 @@ bool QStandardItemPrivate::insertColumns(int column, int count, const QList<QSta
/*!
\internal
*/
-void QStandardItemModelPrivate::itemChanged(QStandardItem *item, const QVector<int> &roles)
+void QStandardItemModelPrivate::itemChanged(QStandardItem *item, const QList<int> &roles)
{
Q_Q(QStandardItemModel);
Q_ASSERT(item);
@@ -662,7 +627,7 @@ void QStandardItemModelPrivate::rowsInserted(QStandardItem *parent,
{
Q_Q(QStandardItemModel);
if (parent == root.data())
- rowHeaderItems.insert(row, count, 0);
+ rowHeaderItems.insert(row, count, nullptr);
q->endInsertRows();
}
@@ -674,7 +639,7 @@ void QStandardItemModelPrivate::columnsInserted(QStandardItem *parent,
{
Q_Q(QStandardItemModel);
if (parent == root.data())
- columnHeaderItems.insert(column, count, 0);
+ columnHeaderItems.insert(column, count, nullptr);
q->endInsertColumns();
}
@@ -878,7 +843,7 @@ QStandardItem &QStandardItem::operator=(const QStandardItem &other)
QStandardItem::~QStandardItem()
{
Q_D(QStandardItem);
- for (QStandardItem *child : qAsConst(d->children)) {
+ for (QStandardItem *child : std::as_const(d->children)) {
if (child)
child->d_func()->setModel(nullptr);
delete child;
@@ -907,9 +872,15 @@ QStandardItem *QStandardItem::parent() const
Sets the item's data for the given \a role to the specified \a value.
If you subclass QStandardItem and reimplement this function, your
- reimplementation should call emitDataChanged() if you do not call
- the base implementation of setData(). This will ensure that e.g.
- views using the model are notified of the changes.
+ reimplementation should:
+ \list
+ \li call emitDataChanged() if you do not call the base implementation of
+ setData(). This will ensure that e.g. views using the model are notified
+ of the changes
+ \li call the base implementation for roles you don't handle, otherwise
+ setting flags, e.g. by calling setFlags(), setCheckable(), setEditable()
+ etc., will not work.
+ \endlist
\note The default implementation treats Qt::EditRole and Qt::DisplayRole
as referring to the same data.
@@ -920,17 +891,18 @@ void QStandardItem::setData(const QVariant &value, int role)
{
Q_D(QStandardItem);
role = (role == Qt::EditRole) ? Qt::DisplayRole : role;
- const QVector<int> roles((role == Qt::DisplayRole) ?
- QVector<int>({Qt::DisplayRole, Qt::EditRole}) :
- QVector<int>({role}));
- QVector<QStandardItemData>::iterator it;
- for (it = d->values.begin(); it != d->values.end(); ++it) {
+ const QList<int> roles((role == Qt::DisplayRole) ?
+ QList<int>({Qt::DisplayRole, Qt::EditRole}) :
+ QList<int>({role}));
+ for (auto it = d->values.begin(); it != d->values.end(); ++it) {
if ((*it).role == role) {
if (value.isValid()) {
if ((*it).value.userType() == value.userType() && (*it).value == value)
return;
(*it).value = value;
} else {
+ // Don't need to assign proper it after erase() since we
+ // return unconditionally in this code path.
d->values.erase(it);
}
if (d->model)
@@ -955,29 +927,49 @@ void QStandardItem::clearData()
return;
d->values.clear();
if (d->model)
- d->model->d_func()->itemChanged(this, QVector<int>{});
+ d->model->d_func()->itemChanged(this, QList<int>{});
}
/*!
Returns the item's data for the given \a role, or an invalid
QVariant if there is no data for the role.
+ If you reimplement this function, your reimplementation should call
+ the base implementation for roles you don't handle, otherwise getting
+ flags, e.g. by calling flags(), isCheckable(), isEditable() etc.,
+ will not work.
+
\note The default implementation treats Qt::EditRole and Qt::DisplayRole
as referring to the same data.
*/
QVariant QStandardItem::data(int role) const
{
Q_D(const QStandardItem);
- role = (role == Qt::EditRole) ? Qt::DisplayRole : role;
- QVector<QStandardItemData>::const_iterator it;
- for (it = d->values.begin(); it != d->values.end(); ++it) {
- if ((*it).role == role)
- return (*it).value;
+ const int r = (role == Qt::EditRole) ? Qt::DisplayRole : role;
+ for (const auto &value : d->values) {
+ if (value.role == r)
+ return value.value;
}
return QVariant();
}
/*!
+ \since 6.0
+
+ Fills the \a roleDataSpan span with the data from this item.
+
+ The default implementation simply calls data() for each role
+ in the span.
+
+ \sa data()
+*/
+void QStandardItem::multiData(QModelRoleDataSpan roleDataSpan) const
+{
+ for (auto &roleData : roleDataSpan)
+ roleData.setData(data(roleData.role()));
+}
+
+/*!
\since 4.4
Causes the model associated with this item to emit a
@@ -1006,7 +998,7 @@ void QStandardItem::emitDataChanged()
*/
void QStandardItem::setFlags(Qt::ItemFlags flags)
{
- setData((int)flags, Qt::UserRole - 1);
+ setData((int)flags, DataFlagsRole);
}
/*!
@@ -1021,7 +1013,7 @@ void QStandardItem::setFlags(Qt::ItemFlags flags)
*/
Qt::ItemFlags QStandardItem::flags() const
{
- QVariant v = data(Qt::UserRole - 1);
+ QVariant v = data(DataFlagsRole);
if (!v.isValid())
return (Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable
|Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled);
@@ -1610,8 +1602,8 @@ void QStandardItem::insertRow(int row, const QList<QStandardItem*> &items)
Q_D(QStandardItem);
if (row < 0)
return;
- if (columnCount() < items.count())
- setColumnCount(items.count());
+ if (columnCount() < items.size())
+ setColumnCount(items.size());
d->insertRows(row, 1, items);
}
@@ -1639,8 +1631,8 @@ void QStandardItem::insertColumn(int column, const QList<QStandardItem*> &items)
Q_D(QStandardItem);
if (column < 0)
return;
- if (rowCount() < items.count())
- setRowCount(items.count());
+ if (rowCount() < items.size())
+ setRowCount(items.size());
d->insertColumns(column, 1, items);
}
@@ -1879,10 +1871,37 @@ QStandardItem *QStandardItem::takeChild(int row, int column)
QStandardItem *item = nullptr;
int index = d->childIndex(row, column);
if (index != -1) {
+ QModelIndex changedIdx;
item = d->children.at(index);
- if (item)
- item->d_func()->setParentAndModel(nullptr, nullptr);
- d->children.replace(index, 0);
+ if (item) {
+ QStandardItemPrivate *const item_d = item->d_func();
+ if (d->model) {
+ QStandardItemModelPrivate *const model_d = d->model->d_func();
+ const int savedRows = item_d->rows;
+ const int savedCols = item_d->columns;
+ const QVector<QStandardItem*> savedChildren = item_d->children;
+ if (savedRows > 0) {
+ model_d->rowsAboutToBeRemoved(item, 0, savedRows - 1);
+ item_d->rows = 0;
+ item_d->children = QVector<QStandardItem*>(); //slightly faster than clear
+ model_d->rowsRemoved(item, 0, savedRows);
+ }
+ if (savedCols > 0) {
+ model_d->columnsAboutToBeRemoved(item, 0, savedCols - 1);
+ item_d->columns = 0;
+ item_d->children = QVector<QStandardItem*>(); //slightly faster than clear
+ model_d->columnsRemoved(item, 0, savedCols);
+ }
+ item_d->rows = savedRows;
+ item_d->columns = savedCols;
+ item_d->children = savedChildren;
+ changedIdx = d->model->indexFromItem(item);
+ }
+ item_d->setParentAndModel(nullptr, nullptr);
+ }
+ d->children.replace(index, nullptr);
+ if (changedIdx.isValid())
+ d->model->dataChanged(changedIdx, changedIdx);
}
return item;
}
@@ -2196,9 +2215,9 @@ QStandardItemModel::QStandardItemModel(int rows, int columns, QObject *parent)
Q_D(QStandardItemModel);
d->init();
d->root->insertColumns(0, columns);
- d->columnHeaderItems.insert(0, columns, 0);
+ d->columnHeaderItems.insert(0, columns, nullptr);
d->root->insertRows(0, rows);
- d->rowHeaderItems.insert(0, rows, 0);
+ d->rowHeaderItems.insert(0, rows, nullptr);
d->root->d_func()->setModel(this);
}
@@ -2526,9 +2545,9 @@ QStandardItem *QStandardItemModel::verticalHeaderItem(int row) const
void QStandardItemModel::setHorizontalHeaderLabels(const QStringList &labels)
{
Q_D(QStandardItemModel);
- if (columnCount() < labels.count())
- setColumnCount(labels.count());
- for (int i = 0; i < labels.count(); ++i) {
+ if (columnCount() < labels.size())
+ setColumnCount(labels.size());
+ for (int i = 0; i < labels.size(); ++i) {
QStandardItem *item = horizontalHeaderItem(i);
if (!item) {
item = d->createItem();
@@ -2549,9 +2568,9 @@ void QStandardItemModel::setHorizontalHeaderLabels(const QStringList &labels)
void QStandardItemModel::setVerticalHeaderLabels(const QStringList &labels)
{
Q_D(QStandardItemModel);
- if (rowCount() < labels.count())
- setRowCount(labels.count());
- for (int i = 0; i < labels.count(); ++i) {
+ if (rowCount() < labels.size())
+ setRowCount(labels.size());
+ for (int i = 0; i < labels.size(); ++i) {
QStandardItem *item = verticalHeaderItem(i);
if (!item) {
item = d->createItem();
@@ -2755,7 +2774,7 @@ QStandardItem *QStandardItemModel::takeHorizontalHeaderItem(int column)
QStandardItem *headerItem = d->columnHeaderItems.at(column);
if (headerItem) {
headerItem->d_func()->setParentAndModel(nullptr, nullptr);
- d->columnHeaderItems.replace(column, 0);
+ d->columnHeaderItems.replace(column, nullptr);
}
return headerItem;
}
@@ -2777,7 +2796,7 @@ QStandardItem *QStandardItemModel::takeVerticalHeaderItem(int row)
QStandardItem *headerItem = d->rowHeaderItems.at(row);
if (headerItem) {
headerItem->d_func()->setParentAndModel(nullptr, nullptr);
- d->rowHeaderItems.replace(row, 0);
+ d->rowHeaderItems.replace(row, nullptr);
}
return headerItem;
}
@@ -2803,6 +2822,12 @@ void QStandardItemModel::setSortRole(int role)
d->sortRole = role;
}
+QBindable<int> QStandardItemModel::bindableSortRole()
+{
+ Q_D(QStandardItemModel);
+ return &d->sortRole;
+}
+
/*!
\reimp
*/
@@ -2826,6 +2851,18 @@ QVariant QStandardItemModel::data(const QModelIndex &index, int role) const
/*!
\reimp
*/
+void QStandardItemModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
+{
+ // Cannot offer a better implementation; users may be overriding
+ // data(), and thus multiData() may fall out of sync for them.
+ // The base class' implementation will simply call data() in a loop,
+ // so it's fine.
+ QAbstractItemModel::multiData(index, roleDataSpan);
+}
+
+/*!
+ \reimp
+*/
Qt::ItemFlags QStandardItemModel::flags(const QModelIndex &index) const
{
Q_D(const QStandardItemModel);
@@ -3079,21 +3116,21 @@ QStringList QStandardItemModel::mimeTypes() const
*/
QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const
{
- QMimeData *data = QAbstractItemModel::mimeData(indexes);
- if(!data)
+ std::unique_ptr<QMimeData> data(QAbstractItemModel::mimeData(indexes));
+ if (!data)
return nullptr;
const QString format = qStandardItemModelDataListMimeType();
if (!mimeTypes().contains(format))
- return data;
+ return data.release();
QByteArray encoded;
QDataStream stream(&encoded, QIODevice::WriteOnly);
QSet<QStandardItem*> itemsSet;
QStack<QStandardItem*> stack;
- itemsSet.reserve(indexes.count());
- stack.reserve(indexes.count());
- for (int i = 0; i < indexes.count(); ++i) {
+ itemsSet.reserve(indexes.size());
+ stack.reserve(indexes.size());
+ for (int i = 0; i < indexes.size(); ++i) {
if (QStandardItem *item = itemFromIndex(indexes.at(i))) {
itemsSet << item;
stack.push(item);
@@ -3103,7 +3140,7 @@ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const
}
}
- //remove duplicates childrens
+ //remove duplicates children
{
QDuplicateTracker<QStandardItem *> seen;
while (!stack.isEmpty()) {
@@ -3111,8 +3148,8 @@ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const
if (seen.hasSeen(itm))
continue;
- const QVector<QStandardItem*> &childList = itm->d_func()->children;
- for (int i = 0; i < childList.count(); ++i) {
+ const QList<QStandardItem*> &childList = itm->d_func()->children;
+ for (int i = 0; i < childList.size(); ++i) {
QStandardItem *chi = childList.at(i);
if (chi) {
itemsSet.remove(chi);
@@ -3122,8 +3159,8 @@ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const
}
}
- stack.reserve(itemsSet.count());
- for (QStandardItem *item : qAsConst(itemsSet))
+ stack.reserve(itemsSet.size());
+ for (QStandardItem *item : std::as_const(itemsSet))
stack.push(item);
//stream everything recursively
@@ -3132,12 +3169,12 @@ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const
if (itemsSet.contains(item)) //if the item is selection 'top-level', stream its position
stream << item->row() << item->column();
- stream << *item << item->columnCount() << item->d_ptr->children.count();
+ stream << *item << item->columnCount() << int(item->d_ptr->children.size());
stack += item->d_ptr->children;
}
data->setData(format, encoded);
- return data;
+ return data.release();
}
@@ -3196,8 +3233,8 @@ bool QStandardItemModel::dropMimeData(const QMimeData *data, Qt::DropAction acti
int left = INT_MAX;
int bottom = 0;
int right = 0;
- QVector<int> rows, columns;
- QVector<QStandardItem *> items;
+ QList<int> rows, columns;
+ QList<QStandardItem *> items;
while (!stream.atEnd()) {
int r, c;
@@ -3220,16 +3257,16 @@ bool QStandardItemModel::dropMimeData(const QMimeData *data, Qt::DropAction acti
int dragColumnCount = right - left + 1;
// Compute the number of continuous rows upon insertion and modify the rows to match
- QVector<int> rowsToInsert(bottom + 1);
- for (int i = 0; i < rows.count(); ++i)
+ QList<int> rowsToInsert(bottom + 1);
+ for (int i = 0; i < rows.size(); ++i)
rowsToInsert[rows.at(i)] = 1;
- for (int i = 0; i < rowsToInsert.count(); ++i) {
+ for (int i = 0; i < rowsToInsert.size(); ++i) {
if (rowsToInsert.at(i) == 1){
rowsToInsert[i] = dragRowCount;
++dragRowCount;
}
}
- for (int i = 0; i < rows.count(); ++i)
+ for (int i = 0; i < rows.size(); ++i)
rows[i] = top + rowsToInsert.at(rows.at(i));
QBitArray isWrittenTo(dragRowCount * dragColumnCount);
@@ -3249,7 +3286,7 @@ bool QStandardItemModel::dropMimeData(const QMimeData *data, Qt::DropAction acti
if (!parentItem)
parentItem = invisibleRootItem();
- QVector<QPersistentModelIndex> newIndexes(items.size());
+ QList<QPersistentModelIndex> newIndexes(items.size());
// set the data in the table
for (int j = 0; j < items.size(); ++j) {
int relativeRow = rows.at(j) - top;
diff --git a/src/gui/itemmodels/qstandarditemmodel.h b/src/gui/itemmodels/qstandarditemmodel.h
index f7d8ffd51a..a5ae05feeb 100644
--- a/src/gui/itemmodels/qstandarditemmodel.h
+++ b/src/gui/itemmodels/qstandarditemmodel.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSTANDARDITEMMODEL_H
#define QSTANDARDITEMMODEL_H
@@ -66,6 +30,7 @@ public:
virtual ~QStandardItem();
virtual QVariant data(int role = Qt::UserRole + 1) const;
+ virtual void multiData(QModelRoleDataSpan roleDataSpan) const;
virtual void setData(const QVariant &value, int role = Qt::UserRole + 1);
void clearData();
@@ -109,7 +74,7 @@ public:
inline void setFont(const QFont &font);
inline Qt::Alignment textAlignment() const {
- return Qt::Alignment(qvariant_cast<int>(data(Qt::TextAlignmentRole)));
+ return qvariant_cast<Qt::Alignment>(data(Qt::TextAlignmentRole));
}
inline void setTextAlignment(Qt::Alignment textAlignment);
@@ -142,43 +107,43 @@ public:
void setFlags(Qt::ItemFlags flags);
inline bool isEnabled() const {
- return (flags() & Qt::ItemIsEnabled) != 0;
+ return bool(flags() & Qt::ItemIsEnabled);
}
void setEnabled(bool enabled);
inline bool isEditable() const {
- return (flags() & Qt::ItemIsEditable) != 0;
+ return bool(flags() & Qt::ItemIsEditable);
}
void setEditable(bool editable);
inline bool isSelectable() const {
- return (flags() & Qt::ItemIsSelectable) != 0;
+ return bool(flags() & Qt::ItemIsSelectable);
}
void setSelectable(bool selectable);
inline bool isCheckable() const {
- return (flags() & Qt::ItemIsUserCheckable) != 0;
+ return bool(flags() & Qt::ItemIsUserCheckable);
}
void setCheckable(bool checkable);
inline bool isAutoTristate() const {
- return (flags() & Qt::ItemIsAutoTristate) != 0;
+ return bool(flags() & Qt::ItemIsAutoTristate);
}
void setAutoTristate(bool tristate);
inline bool isUserTristate() const {
- return (flags() & Qt::ItemIsUserTristate) != 0;
+ return bool(flags() & Qt::ItemIsUserTristate);
}
void setUserTristate(bool tristate);
#if QT_CONFIG(draganddrop)
inline bool isDragEnabled() const {
- return (flags() & Qt::ItemIsDragEnabled) != 0;
+ return bool(flags() & Qt::ItemIsDragEnabled);
}
void setDragEnabled(bool dragEnabled);
inline bool isDropEnabled() const {
- return (flags() & Qt::ItemIsDropEnabled) != 0;
+ return bool(flags() & Qt::ItemIsDropEnabled);
}
void setDropEnabled(bool dropEnabled);
#endif // QT_CONFIG(draganddrop)
@@ -273,7 +238,7 @@ inline void QStandardItem::setFont(const QFont &afont)
{ setData(afont, Qt::FontRole); }
inline void QStandardItem::setTextAlignment(Qt::Alignment atextAlignment)
-{ setData(int(atextAlignment), Qt::TextAlignmentRole); }
+{ setData(QVariant::fromValue(atextAlignment), Qt::TextAlignmentRole); }
inline void QStandardItem::setBackground(const QBrush &abrush)
{ setData(abrush, Qt::BackgroundRole); }
@@ -313,7 +278,7 @@ class QStandardItemModelPrivate;
class Q_GUI_EXPORT QStandardItemModel : public QAbstractItemModel
{
Q_OBJECT
- Q_PROPERTY(int sortRole READ sortRole WRITE setSortRole)
+ Q_PROPERTY(int sortRole READ sortRole WRITE setSortRole BINDABLE bindableSortRole)
public:
explicit QStandardItemModel(QObject *parent = nullptr);
@@ -331,6 +296,7 @@ public:
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool clearItemData(const QModelIndex &index) override;
@@ -402,13 +368,13 @@ public:
int sortRole() const;
void setSortRole(int role);
+ QBindable<int> bindableSortRole();
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData (const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
Q_SIGNALS:
- // ### Qt 6: add changed roles
void itemChanged(QStandardItem *item);
protected:
diff --git a/src/gui/itemmodels/qstandarditemmodel_p.h b/src/gui/itemmodels/qstandarditemmodel_p.h
index 92eb38b40b..a0c3f8a161 100644
--- a/src/gui/itemmodels/qstandarditemmodel_p.h
+++ b/src/gui/itemmodels/qstandarditemmodel_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSTANDARDITEMMODEL_P_H
#define QSTANDARDITEMMODEL_P_H
@@ -51,6 +15,8 @@
// We mean it.
//
+#include <QtGui/qstandarditemmodel.h>
+
#include <QtGui/private/qtguiglobal_p.h>
#include "private/qabstractitemmodel_p.h"
@@ -68,13 +34,15 @@ class QStandardItemData
{
public:
inline QStandardItemData() : role(-1) {}
- inline QStandardItemData(int r, const QVariant &v) : role(r), value(v) {}
- inline QStandardItemData(const std::pair<const int&, const QVariant&> &p) : role(p.first), value(p.second) {}
+ inline QStandardItemData(int r, const QVariant &v) :
+ role(r == Qt::EditRole ? Qt::DisplayRole : r), value(v) {}
+ inline QStandardItemData(const std::pair<const int&, const QVariant&> &p) :
+ role(p.first == Qt::EditRole ? Qt::DisplayRole : p.first), value(p.second) {}
int role;
QVariant value;
inline bool operator==(const QStandardItemData &other) const { return role == other.role && value == other.value; }
};
-Q_DECLARE_TYPEINFO(QStandardItemData, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QStandardItemData, Q_RELOCATABLE_TYPE);
#ifndef QT_NO_DATASTREAM
@@ -137,7 +105,7 @@ public:
// backwards through the children
int backwardIter = childsLastIndexInParent - 1;
int forwardIter = childsLastIndexInParent;
- Q_FOREVER {
+ for (;;) {
if (forwardIter <= lastChild) {
if (children.at(forwardIter) == child) {
childsLastIndexInParent = forwardIter;
@@ -180,7 +148,7 @@ public:
void changeFlags(bool enable, Qt::ItemFlags f);
void setItemData(const QMap<int, QVariant> &roles);
- const QMap<int, QVariant> itemData() const;
+ QMap<int, QVariant> itemData() const;
bool insertRows(int row, int count, const QList<QStandardItem*> &items);
bool insertRows(int row, const QList<QStandardItem*> &items);
@@ -247,7 +215,7 @@ public:
QHash<int, QByteArray> roleNames;
QScopedPointer<QStandardItem> root;
const QStandardItem *itemPrototype;
- int sortRole;
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QStandardItemModelPrivate, int, sortRole, Qt::DisplayRole)
};
QT_END_NAMESPACE