diff options
Diffstat (limited to 'src/gui/itemmodels')
-rw-r--r-- | src/gui/itemmodels/qfileinfogatherer.cpp | 192 | ||||
-rw-r--r-- | src/gui/itemmodels/qfileinfogatherer_p.h | 61 | ||||
-rw-r--r-- | src/gui/itemmodels/qfilesystemmodel.cpp | 398 | ||||
-rw-r--r-- | src/gui/itemmodels/qfilesystemmodel.h | 54 | ||||
-rw-r--r-- | src/gui/itemmodels/qfilesystemmodel_p.h | 95 | ||||
-rw-r--r-- | src/gui/itemmodels/qstandarditemmodel.cpp | 180 | ||||
-rw-r--r-- | src/gui/itemmodels/qstandarditemmodel.h | 60 | ||||
-rw-r--r-- | src/gui/itemmodels/qstandarditemmodel_p.h | 50 |
8 files changed, 484 insertions, 606 deletions
diff --git a/src/gui/itemmodels/qfileinfogatherer.cpp b/src/gui/itemmodels/qfileinfogatherer.cpp index ec4ae2269b..41fb0a0db5 100644 --- a/src/gui/itemmodels/qfileinfogatherer.cpp +++ b/src/gui/itemmodels/qfileinfogatherer.cpp @@ -1,45 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets 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) 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 <qdiriterator.h> +#include <qdirlisting.h> +#include <private/qabstractfileiconprovider_p.h> #include <private/qfileinfo_p.h> #ifndef Q_OS_WIN # include <unistd.h> @@ -51,8 +17,10 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + #ifdef QT_BUILD_INTERNAL -static QBasicAtomicInt fetchedRoot = Q_BASIC_ATOMIC_INITIALIZER(false); +Q_CONSTINIT static QBasicAtomicInt fetchedRoot = Q_BASIC_ATOMIC_INITIALIZER(false); Q_AUTOTEST_EXPORT void qt_test_resetFetchedRoot() { fetchedRoot.storeRelaxed(false); @@ -68,9 +36,9 @@ static QString translateDriveName(const QFileInfo &drive) { QString driveName = drive.absoluteFilePath(); #ifdef Q_OS_WIN - if (driveName.startsWith(QLatin1Char('/'))) // UNC host + if (driveName.startsWith(u'/')) // UNC host return drive.fileName(); - if (driveName.endsWith(QLatin1Char('/'))) + if (driveName.endsWith(u'/')) driveName.chop(1); #endif // Q_OS_WIN return driveName; @@ -91,11 +59,40 @@ QFileInfoGatherer::QFileInfoGatherer(QObject *parent) */ QFileInfoGatherer::~QFileInfoGatherer() { - abort.storeRelaxed(true); + 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(); - locker.unlock(); - wait(); } void QFileInfoGatherer::setResolveSymlinks(bool enable) @@ -117,7 +114,7 @@ void QFileInfoGatherer::driveRemoved() const QFileInfoList driveInfoList = QDir::drives(); for (const QFileInfo &fi : driveInfoList) drives.append(translateDriveName(fi)); - newListOfFiles(QString(), drives); + emit newListOfFiles(QString(), drives); } bool QFileInfoGatherer::resolveSymlinks() const @@ -148,21 +145,24 @@ void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStr { QMutexLocker locker(&mutex); // See if we already have this dir/file in our queue - int loc = this->path.lastIndexOf(path); - while (loc > 0) { - if (this->files.at(loc) == files) { + qsizetype loc = 0; + while ((loc = this->path.lastIndexOf(path, loc - 1)) != -1) { + if (this->files.at(loc) == files) return; - } - loc = this->path.lastIndexOf(path, loc - 1); } + +#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(QLatin1String("//")) /*don't watch UNC path*/) { + && !path.startsWith("//"_L1) /*don't watch UNC path*/) { if (!watchedDirectories().contains(path)) watchPaths(QStringList(path)); } @@ -176,8 +176,8 @@ void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStr */ void QFileInfoGatherer::updateFile(const QString &filePath) { - QString dir = filePath.mid(0, filePath.lastIndexOf(QLatin1Char('/'))); - QString fileName = filePath.mid(dir.length() + 1); + QString dir = filePath.mid(0, filePath.lastIndexOf(u'/')); + QString fileName = filePath.mid(dir.size() + 1); fetchExtendedInformation(dir, QStringList(fileName)); } @@ -250,16 +250,23 @@ bool QFileInfoGatherer::isWatching() const 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) { - if (!v) { - delete m_watcher; - m_watcher = nullptr; - } m_watching = v; + if (!m_watching) + delete std::exchange(m_watcher, nullptr); } #else Q_UNUSED(v); @@ -311,17 +318,24 @@ void QFileInfoGatherer::list(const QString &directoryPath) void QFileInfoGatherer::run() { forever { + // Disallow termination while we are holding a mutex or can be + // woken up cleanly. + setTerminationEnabled(false); QMutexLocker locker(&mutex); - while (!abort.loadRelaxed() && path.isEmpty()) + while (!isInterruptionRequested() && path.isEmpty()) condition.wait(&mutex); - if (abort.loadRelaxed()) + if (isInterruptionRequested()) return; - const QString thisPath = qAsConst(path).front(); + const QString thisPath = std::as_const(path).front(); path.pop_front(); - const QStringList thisList = qAsConst(files).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); } } @@ -329,8 +343,12 @@ void QFileInfoGatherer::run() QExtendedInformation QFileInfoGatherer::getInfo(const QFileInfo &fileInfo) const { QExtendedInformation info(fileInfo); - info.icon = m_iconProvider->icon(fileInfo); - info.displayType = m_iconProvider->type(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"); @@ -370,21 +388,23 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil #ifdef QT_BUILD_INTERNAL fetchedRoot.storeRelaxed(true); #endif - QFileInfoList infoList; + QList<std::pair<QString, QFileInfo>> updatedFiles; + auto addToUpdatedFiles = [&updatedFiles](QFileInfo &&fileInfo) { + fileInfo.stat(); + updatedFiles.emplace_back(std::pair{translateDriveName(fileInfo), fileInfo}); + }; + if (files.isEmpty()) { - infoList = QDir::drives(); + // 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 { - infoList.reserve(files.count()); - for (const auto &file : files) - infoList << QFileInfo(file); - } - QList<QPair<QString, QFileInfo>> updatedFiles; - updatedFiles.reserve(infoList.count()); - for (int i = infoList.count() - 1; i >= 0; --i) { - QFileInfo driveInfo = infoList.at(i); - driveInfo.stat(); - QString driveName = translateDriveName(driveInfo); - updatedFiles.append(QPair<QString,QFileInfo>(driveName, driveInfo)); + updatedFiles.reserve(files.size()); + for (auto rit = files.crbegin(), rend = files.crend(); rit != rend; ++rit) + addToUpdatedFiles(QFileInfo(*rit)); } emit updates(path, updatedFiles); return; @@ -394,14 +414,16 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil base.start(); QFileInfo fileInfo; bool firstTime = true; - QList<QPair<QString, QFileInfo>> updatedFiles; + QList<std::pair<QString, QFileInfo>> updatedFiles; QStringList filesToCheck = files; QStringList allFiles; if (files.isEmpty()) { - QDirIterator dirIt(path, QDir::AllEntries | QDir::System | QDir::Hidden); - while (!abort.loadRelaxed() && dirIt.hasNext()) { - fileInfo = dirIt.nextFileInfo(); + 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); @@ -411,7 +433,7 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil emit newListOfFiles(path, allFiles); QStringList::const_iterator filesIt = filesToCheck.constBegin(); - while (!abort.loadRelaxed() && filesIt != filesToCheck.constEnd()) { + while (!isInterruptionRequested() && filesIt != filesToCheck.constEnd()) { fileInfo.setFile(path + QDir::separator() + *filesIt); ++filesIt; fileInfo.stat(); @@ -423,12 +445,12 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil } void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QElapsedTimer &base, bool &firstTime, - QList<QPair<QString, QFileInfo>> &updatedFiles, const QString &path) + QList<std::pair<QString, QFileInfo>> &updatedFiles, const QString &path) { - updatedFiles.append(QPair<QString, QFileInfo>(fileInfo.fileName(), fileInfo)); + updatedFiles.emplace_back(std::pair(fileInfo.fileName(), fileInfo)); QElapsedTimer current; current.start(); - if ((firstTime && updatedFiles.count() > 100) || base.msecsTo(current) > 1000) { + if ((firstTime && updatedFiles.size() > 100) || base.msecsTo(current) > 1000) { emit updates(path, updatedFiles); updatedFiles.clear(); base = current; diff --git a/src/gui/itemmodels/qfileinfogatherer_p.h b/src/gui/itemmodels/qfileinfogatherer_p.h index 374331a588..3d5f59c22e 100644 --- a/src/gui/itemmodels/qfileinfogatherer_p.h +++ b/src/gui/itemmodels/qfileinfogatherer_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 QtWidgets 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 QFILEINFOGATHERER_H #define QFILEINFOGATHERER_H @@ -60,7 +24,6 @@ #include <qfilesystemwatcher.h> #endif #include <qabstractfileiconprovider.h> -#include <qpair.h> #include <qstack.h> #include <qdatetime.h> #include <qdir.h> @@ -68,6 +31,8 @@ #include <private/qfilesystemengine_p.h> +#include <utility> + QT_REQUIRE_CONFIG(filesystemmodel); QT_BEGIN_NAMESPACE @@ -87,7 +52,7 @@ public: return mFileInfo == fileInfo.mFileInfo && displayType == fileInfo.displayType && permissions() == fileInfo.permissions() - && lastModified() == fileInfo.lastModified(); + && lastModified(QTimeZone::UTC) == fileInfo.lastModified(QTimeZone::UTC); } #ifndef QT_NO_FSFILEENGINE @@ -117,7 +82,7 @@ public: { if (ignoreNtfsSymLinks) { #ifdef Q_OS_WIN - return !mFileInfo.suffix().compare(QLatin1String("lnk"), Qt::CaseInsensitive); + return !mFileInfo.suffix().compare(QLatin1StringView("lnk"), Qt::CaseInsensitive); #endif } return mFileInfo.isSymLink(); @@ -131,8 +96,8 @@ public: return mFileInfo; } - QDateTime lastModified() const { - return mFileInfo.lastModified(); + QDateTime lastModified(const QTimeZone &tz) const { + return mFileInfo.lastModified(tz); } qint64 size() const { @@ -160,7 +125,7 @@ class Q_GUI_EXPORT QFileInfoGatherer : public QThread Q_OBJECT Q_SIGNALS: - void updates(const QString &directory, const QList<QPair<QString, QFileInfo>> &updates); + 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); @@ -184,6 +149,8 @@ public: QAbstractFileIconProvider *iconProvider() const; bool resolveSymlinks() const; + void requestAbort(); + public Q_SLOTS: void list(const QString &directoryPath); void fetchExtendedInformation(const QString &path, const QStringList &files); @@ -195,12 +162,15 @@ 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<QPair<QString, QFileInfo>> &updatedFiles, const QString &path); + QList<std::pair<QString, QFileInfo>> &updatedFiles, const QString &path); private: void createWatcher(); @@ -211,7 +181,6 @@ private: QStack<QString> path; QStack<QStringList> files; // end protected by mutex - QAtomicInt abort; #if QT_CONFIG(filesystemwatcher) QFileSystemWatcher *m_watcher = nullptr; diff --git a/src/gui/itemmodels/qfilesystemmodel.cpp b/src/gui/itemmodels/qfilesystemmodel.cpp index 0ee97d06be..290891322f 100644 --- a/src/gui/itemmodels/qfilesystemmodel.cpp +++ b/src/gui/itemmodels/qfilesystemmodel.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets 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) 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" @@ -59,12 +23,15 @@ 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 */ /*! @@ -177,7 +144,7 @@ QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const \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 in the directory \a path. + renamed to \a newName. The file is located in the directory \a path. */ /*! @@ -274,7 +241,7 @@ QModelIndex QFileSystemModel::index(int row, int column, const QModelIndex &pare */ QModelIndex QFileSystemModel::sibling(int row, int column, const QModelIndex &idx) const { - if (row == idx.row() && column < QFileSystemModelPrivate::NumColumns) { + if (row == idx.row() && column < columnCount(idx.parent())) { // cheap sibling operation: just adjust the column: return createIndex(row, column, idx.internalPointer()); } else { @@ -314,17 +281,17 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QM static QString qt_GetLongPathName(const QString &strShortPath) { if (strShortPath.isEmpty() - || strShortPath == QLatin1String(".") || strShortPath == QLatin1String("..")) + || strShortPath == "."_L1 || strShortPath == ".."_L1) return strShortPath; - if (strShortPath.length() == 2 && strShortPath.endsWith(QLatin1Char(':'))) + if (strShortPath.length() == 2 && strShortPath.endsWith(u':')) return strShortPath.toUpper(); const QString absPath = QDir(strShortPath).absolutePath(); - if (absPath.startsWith(QLatin1String("//")) - || absPath.startsWith(QLatin1String("\\\\"))) // unc + if (absPath.startsWith("//"_L1) + || absPath.startsWith("\\\\"_L1)) // unc return QDir::fromNativeSeparators(absPath); - if (absPath.startsWith(QLatin1Char('/'))) + if (absPath.startsWith(u'/')) return QString(); - const QString inputString = QLatin1String("\\\\?\\") + QDir::toNativeSeparators(absPath); + const QString inputString = "\\\\?\\"_L1 + QDir::toNativeSeparators(absPath); QVarLengthArray<TCHAR, MAX_PATH> buffer(MAX_PATH); DWORD result = ::GetLongPathName((wchar_t*)inputString.utf16(), buffer.data(), @@ -354,7 +321,7 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QS { Q_Q(const QFileSystemModel); Q_UNUSED(q); - if (path.isEmpty() || path == myComputer() || path.startsWith(QLatin1Char(':'))) + 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 @@ -370,33 +337,33 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QS absolutePath = QDir(longPath).absolutePath(); // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const? - QStringList pathElements = absolutePath.split(QLatin1Char('/'), Qt::SkipEmptyParts); + QStringList pathElements = absolutePath.split(u'/', Qt::SkipEmptyParts); if ((pathElements.isEmpty()) #if !defined(Q_OS_WIN) - && QDir::fromNativeSeparators(longPath) != QLatin1String("/") + && QDir::fromNativeSeparators(longPath) != "/"_L1 #endif ) return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); QModelIndex index = QModelIndex(); // start with "My Computer" QString elementPath; - QChar separator = QLatin1Char('/'); + QChar separator = u'/'; QString trailingSeparator; #if defined(Q_OS_WIN) - if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path - QString host = QLatin1String("\\\\") + pathElements.constFirst(); + if (absolutePath.startsWith("//"_L1)) { // UNC path + QString host = "\\\\"_L1 + pathElements.constFirst(); if (absolutePath == QDir::fromNativeSeparators(host)) - absolutePath.append(QLatin1Char('/')); - if (longPath.endsWith(QLatin1Char('/')) && !absolutePath.endsWith(QLatin1Char('/'))) - absolutePath.append(QLatin1Char('/')); - if (absolutePath.endsWith(QLatin1Char('/'))) - trailingSeparator = QLatin1String("\\"); + 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(QLatin1Char('/'))) + if (pathElements.count() == 1 && !absolutePath.endsWith(u'/')) return rootNode; QFileInfo info(host); if (!info.exists()) @@ -409,31 +376,31 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QS r = translateVisibleLocation(rootNode, r); index = q->index(r, 0, QModelIndex()); pathElements.pop_front(); - separator = QLatin1Char('\\'); + separator = u'\\'; elementPath = host; elementPath.append(separator); } else { - if (!pathElements.at(0).contains(QLatin1Char(':'))) { + if (!pathElements.at(0).contains(u':')) { QString rootPath = QDir(longPath).rootPath(); pathElements.prepend(rootPath); } - if (pathElements.at(0).endsWith(QLatin1Char('/'))) + 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] == QLatin1Char('/')) - pathElements.prepend(QLatin1String("/")); + if (absolutePath[0] == u'/') + pathElements.prepend("/"_L1); #endif QFileSystemModelPrivate::QFileSystemNode *parent = node(index); - for (int i = 0; i < pathElements.count(); ++i) { + 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.count() - 1) + if (i == pathElements.size() - 1) elementPath.append(trailingSeparator); #ifdef Q_OS_WIN // On Windows, "filename " and "filename" are equivalent and @@ -443,7 +410,7 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QS // 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(QLatin1Char('.')) || element.endsWith(QLatin1Char(' '))) + 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()) @@ -454,7 +421,7 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QS // 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.count() == 0) + if ((parent->children.size() == 0) || (parent->caseSensitive() && parent->children.value(element)->fileName != element) || (!parent->caseSensitive() @@ -472,7 +439,7 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QS QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this); node = p->addNode(parent, element,info); #if QT_CONFIG(filesystemwatcher) - node->populate(fileInfoGatherer.getInfo(info)); + node->populate(fileInfoGatherer->getInfo(info)); #endif } else { node = parent->children.value(element); @@ -510,10 +477,10 @@ void QFileSystemModel::timerEvent(QTimerEvent *event) if (event->timerId() == d->fetchingTimer.timerId()) { d->fetchingTimer.stop(); #if QT_CONFIG(filesystemwatcher) - for (int i = 0; i < d->toFetch.count(); ++i) { + 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, + d->fileInfoGatherer->fetchExtendedInformation(d->toFetch.at(i).dir, QStringList(d->toFetch.at(i).file)); } else { // qDebug("yah!, you saved a little gerbil soul"); @@ -563,14 +530,38 @@ QString QFileSystemModel::type(const QModelIndex &index) const } /*! - Returns the date and time when \a index was last modified. + 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(); + return d->node(index)->lastModified(tz); } /*! @@ -660,7 +651,7 @@ void QFileSystemModel::fetchMore(const QModelIndex &parent) return; indexNode->populatedChildren = true; #if QT_CONFIG(filesystemwatcher) - d->fileInfoGatherer.list(filePath(parent)); + d->fileInfoGatherer->list(filePath(parent)); #endif } @@ -674,10 +665,10 @@ int QFileSystemModel::rowCount(const QModelIndex &parent) const return 0; if (!parent.isValid()) - return d->root.visibleChildren.count(); + return d->root.visibleChildren.size(); const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent); - return parentNode->visibleChildren.count(); + return parentNode->visibleChildren.size(); } /*! @@ -703,7 +694,9 @@ QVariant QFileSystemModel::myComputer(int role) const return QFileSystemModelPrivate::myComputer(); #if QT_CONFIG(filesystemwatcher) case Qt::DecorationRole: - return d->fileInfoGatherer.iconProvider()->icon(QAbstractFileIconProvider::Computer); + if (auto *provider = d->fileInfoGatherer->iconProvider()) + return provider->icon(QAbstractFileIconProvider::Computer); + break; #endif } return QVariant(); @@ -720,15 +713,15 @@ QVariant QFileSystemModel::data(const QModelIndex &index, int role) const switch (role) { case Qt::EditRole: - if (index.column() == 0) + if (index.column() == QFileSystemModelPrivate::NameColumn) return d->name(index); Q_FALLTHROUGH(); case Qt::DisplayRole: switch (index.column()) { - case 0: return d->displayName(index); - case 1: return d->size(index); - case 2: return d->type(index); - case 3: return d->time(index); + 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; @@ -738,22 +731,23 @@ QVariant QFileSystemModel::data(const QModelIndex &index, int role) const return filePath(index); case FileNameRole: return d->name(index); + case FileInfoRole: + return QVariant::fromValue(fileInfo(index)); case Qt::DecorationRole: - if (index.column() == 0) { + if (index.column() == QFileSystemModelPrivate::NameColumn) { QIcon icon = d->icon(index); #if QT_CONFIG(filesystemwatcher) if (icon.isNull()) { - if (d->node(index)->isDir()) - icon = d->fileInfoGatherer.iconProvider()->icon(QAbstractFileIconProvider::Folder); - else - icon = d->fileInfoGatherer.iconProvider()->icon(QAbstractFileIconProvider::File); + 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() == 1) + if (index.column() == QFileSystemModelPrivate::SizeColumn) return QVariant(Qt::AlignTrailing | Qt::AlignVCenter); break; case FilePermissions: @@ -774,9 +768,9 @@ QString QFileSystemModelPrivate::size(const QModelIndex &index) const const QFileSystemNode *n = node(index); if (n->isDir()) { #ifdef Q_OS_MAC - return QLatin1String("--"); + return "--"_L1; #else - return QLatin1String(""); + return ""_L1; #endif // Windows - "" // OS X - "--" @@ -799,7 +793,7 @@ QString QFileSystemModelPrivate::time(const QModelIndex &index) const if (!index.isValid()) return QString(); #if QT_CONFIG(datestring) - return QLocale::system().toString(node(index)->lastModified(), QLocale::ShortFormat); + return QLocale::system().toString(node(index)->lastModified(QTimeZone::LocalTime), QLocale::ShortFormat); #else Q_UNUSED(index); return QString(); @@ -826,7 +820,7 @@ QString QFileSystemModelPrivate::name(const QModelIndex &index) const QFileSystemNode *dirNode = node(index); if ( #if QT_CONFIG(filesystemwatcher) - fileInfoGatherer.resolveSymlinks() && + fileInfoGatherer->resolveSymlinks() && #endif !resolvedSymLinks.isEmpty() && dirNode->isSymLink(/* ignoreNtfsSymLinks = */ true)) { QString fullPath = QDir::fromNativeSeparators(filePath(index)); @@ -907,14 +901,14 @@ bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, in int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName); parentNode->visibleChildren.removeAt(visibleLocation); - QScopedPointer<QFileSystemModelPrivate::QFileSystemNode> nodeToRename(parentNode->children.take(oldName)); + 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))); + nodeToRename->populate(d->fileInfoGatherer->getInfo(QFileInfo(parentPath, newName))); #endif nodeToRename->isVisible = true; - parentNode->children[newName] = nodeToRename.take(); + parentNode->children[newName] = nodeToRename.release(); parentNode->visibleChildren.insert(visibleLocation, newName); d->delayedSort(); @@ -947,23 +941,27 @@ QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, QString returnValue; switch (section) { - case 0: returnValue = tr("Name"); - break; - case 1: returnValue = tr("Size"); - break; - case 2: returnValue = + 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"); + tr("Kind", "Match OS X Finder"); #else - tr("Type", "All other platforms"); + tr("Type", "All other platforms"); #endif - break; + break; // Windows - Type // OS X - Kind // Konqueror - File Type // Nautilus - Type - case 3: returnValue = tr("Date Modified"); - break; + case QFileSystemModelPrivate::TimeColumn: + returnValue = tr("Date Modified"); + break; default: return QVariant(); } return returnValue; @@ -987,14 +985,15 @@ Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const } 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; - else - flags |= Qt::ItemNeverHasChildren; } return flags; } @@ -1002,7 +1001,7 @@ Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const /*! \internal */ -void QFileSystemModelPrivate::_q_performDelayedSort() +void QFileSystemModelPrivate::performDelayedSort() { Q_Q(QFileSystemModel); q->sort(sortColumn, sortOrder); @@ -1026,7 +1025,7 @@ public: const QFileSystemModelPrivate::QFileSystemNode *r) const { switch (sortColumn) { - case 0: { + case QFileSystemModelPrivate::NameColumn: { #ifndef Q_OS_MAC // place directories before files bool left = l->isDir(); @@ -1036,7 +1035,7 @@ public: #endif return naturalCompare.compare(l->fileName, r->fileName) < 0; } - case 1: + case QFileSystemModelPrivate::SizeColumn: { // Directories go first bool left = l->isDir(); @@ -1050,7 +1049,7 @@ public: return sizeDifference < 0; } - case 2: + case QFileSystemModelPrivate::TypeColumn: { int compare = naturalCompare.compare(l->type(), r->type()); if (compare == 0) @@ -1058,12 +1057,14 @@ public: return compare < 0; } - case 3: + case QFileSystemModelPrivate::TimeColumn: { - if (l->lastModified() == r->lastModified()) + 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 l->lastModified() < r->lastModified(); + return left < right; } } Q_ASSERT(false); @@ -1091,7 +1092,7 @@ void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent { Q_Q(QFileSystemModel); QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent); - if (indexNode->children.count() == 0) + if (indexNode->children.size() == 0) return; QList<QFileSystemModelPrivate::QFileSystemNode *> values; @@ -1109,11 +1110,10 @@ void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent indexNode->visibleChildren.clear(); //No more dirty item we reset our internal dirty index indexNode->dirtyChildrenIndex = -1; - const int numValues = values.count(); - indexNode->visibleChildren.reserve(numValues); - for (int i = 0; i < numValues; ++i) { - indexNode->visibleChildren.append(values.at(i)->fileName); - values.at(i)->isVisible = true; + indexNode->visibleChildren.reserve(values.size()); + for (QFileSystemNode *node : std::as_const(values)) { + indexNode->visibleChildren.append(node->fileName); + node->isVisible = true; } if (!disableRecursiveSort) { @@ -1139,10 +1139,8 @@ void QFileSystemModel::sort(int column, Qt::SortOrder order) emit layoutAboutToBeChanged(); QModelIndexList oldList = persistentIndexList(); QList<QPair<QFileSystemModelPrivate::QFileSystemNode *, int>> oldNodes; - const int nodeCount = oldList.count(); - oldNodes.reserve(nodeCount); - for (int i = 0; i < nodeCount; ++i) { - const QModelIndex &oldNode = oldList.at(i); + oldNodes.reserve(oldList.size()); + for (const QModelIndex &oldNode : oldList) { QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldNode), oldNode.column()); oldNodes.append(pair); } @@ -1156,12 +1154,10 @@ void QFileSystemModel::sort(int column, Qt::SortOrder order) d->sortOrder = order; QModelIndexList newList; - const int numOldNodes = oldNodes.size(); - newList.reserve(numOldNodes); - for (int i = 0; i < numOldNodes; ++i) { - const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &oldNode = oldNodes.at(i); - newList.append(d->index(oldNode.first, oldNode.second)); - } + newList.reserve(oldNodes.size()); + for (const auto &[node, col]: std::as_const(oldNodes)) + newList.append(d->index(node, col)); + changePersistentIndexList(oldList, newList); emit layoutChanged(); } @@ -1172,7 +1168,7 @@ void QFileSystemModel::sort(int column, Qt::SortOrder order) */ QStringList QFileSystemModel::mimeTypes() const { - return QStringList(QLatin1String("text/uri-list")); + return QStringList("text/uri-list"_L1); } /*! @@ -1188,7 +1184,7 @@ 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() == 0) + if ((*it).column() == QFileSystemModelPrivate::NameColumn) urls << QUrl::fromLocalFile(filePath(*it)); QMimeData *data = new QMimeData(); data->setUrls(urls); @@ -1262,6 +1258,7 @@ QHash<int, QByteArray> QFileSystemModel::roleNames() const 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; } @@ -1334,7 +1331,7 @@ void QFileSystemModel::setOptions(Options options) #if QT_CONFIG(filesystemwatcher) Q_D(QFileSystemModel); if (changed.testFlag(DontWatchForChanges)) - d->fileInfoGatherer.setWatching(!options.testFlag(DontWatchForChanges)); + d->fileInfoGatherer->setWatching(!options.testFlag(DontWatchForChanges)); #endif if (changed.testFlag(DontUseCustomDirectoryIcons)) { @@ -1355,7 +1352,7 @@ QFileSystemModel::Options QFileSystemModel::options() const result.setFlag(DontResolveSymlinks, !resolveSymlinks()); #if QT_CONFIG(filesystemwatcher) Q_D(const QFileSystemModel); - result.setFlag(DontWatchForChanges, !d->fileInfoGatherer.isWatching()); + result.setFlag(DontWatchForChanges, !d->fileInfoGatherer->isWatching()); #else result.setFlag(DontWatchForChanges); #endif @@ -1377,7 +1374,7 @@ QString QFileSystemModel::filePath(const QModelIndex &index) const QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index); if (dirNode->isSymLink() #if QT_CONFIG(filesystemwatcher) - && d->fileInfoGatherer.resolveSymlinks() + && d->fileInfoGatherer->resolveSymlinks() #endif && d->resolvedSymLinks.contains(fullPath) && dirNode->isDir()) { @@ -1413,11 +1410,11 @@ QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const } QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator())); #if !defined(Q_OS_WIN) - if ((fullPath.length() > 2) && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/')) + if ((fullPath.size() > 2) && fullPath[0] == u'/' && fullPath[1] == u'/') fullPath = fullPath.mid(1); #else - if (fullPath.length() == 2 && fullPath.endsWith(QLatin1Char(':'))) - fullPath.append(QLatin1Char('/')); + if (fullPath.length() == 2 && fullPath.endsWith(u':')) + fullPath.append(u'/'); #endif return fullPath; } @@ -1439,7 +1436,7 @@ QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent, const QString &na 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))); + node->populate(d->fileInfoGatherer->getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name))); #endif d->addVisibleFiles(parentNode, QStringList(name)); return d->index(node); @@ -1504,10 +1501,10 @@ QModelIndex QFileSystemModel::setRootPath(const QString &newPath) return d->index(rootPath()); //We remove the watcher on the previous path - if (!rootPath().isEmpty() && rootPath() != QLatin1String(".")) { + if (!rootPath().isEmpty() && rootPath() != "."_L1) { //This remove the watcher for the old rootPath #if QT_CONFIG(filesystemwatcher) - d->fileInfoGatherer.removePath(rootPath()); + 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 @@ -1520,7 +1517,7 @@ QModelIndex QFileSystemModel::setRootPath(const QString &newPath) QModelIndex newRootIndex; if (showDrives) { // otherwise dir will become '.' - d->rootDir.setPath(QLatin1String("")); + d->rootDir.setPath(""_L1); } else { newRootIndex = d->index(d->rootDir.path()); } @@ -1563,7 +1560,7 @@ void QFileSystemModel::setIconProvider(QAbstractFileIconProvider *provider) { Q_D(QFileSystemModel); #if QT_CONFIG(filesystemwatcher) - d->fileInfoGatherer.setIconProvider(provider); + d->fileInfoGatherer->setIconProvider(provider); #endif d->root.updateIcon(provider, QString()); } @@ -1575,9 +1572,9 @@ QAbstractFileIconProvider *QFileSystemModel::iconProvider() const { #if QT_CONFIG(filesystemwatcher) Q_D(const QFileSystemModel); - return d->fileInfoGatherer.iconProvider(); + return d->fileInfoGatherer->iconProvider(); #else - return 0; + return nullptr; #endif } @@ -1631,7 +1628,7 @@ void QFileSystemModel::setResolveSymlinks(bool enable) { #if QT_CONFIG(filesystemwatcher) Q_D(QFileSystemModel); - d->fileInfoGatherer.setResolveSymlinks(enable); + d->fileInfoGatherer->setResolveSymlinks(enable); #else Q_UNUSED(enable); #endif @@ -1641,7 +1638,7 @@ bool QFileSystemModel::resolveSymlinks() const { #if QT_CONFIG(filesystemwatcher) Q_D(const QFileSystemModel); - return d->fileInfoGatherer.resolveSymlinks(); + return d->fileInfoGatherer->resolveSymlinks(); #else return false; #endif @@ -1746,7 +1743,7 @@ bool QFileSystemModel::event(QEvent *event) #if QT_CONFIG(filesystemwatcher) Q_D(QFileSystemModel); if (event->type() == QEvent::LanguageChange) { - d->root.retranslateStrings(d->fileInfoGatherer.iconProvider(), QString()); + d->root.retranslateStrings(d->fileInfoGatherer->iconProvider(), QString()); return true; } #endif @@ -1760,7 +1757,7 @@ bool QFileSystemModel::rmdir(const QModelIndex &aindex) #if QT_CONFIG(filesystemwatcher) if (success) { QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func()); - d->fileInfoGatherer.removePath(path); + d->fileInfoGatherer->removePath(path); } #endif return success; @@ -1772,10 +1769,10 @@ bool QFileSystemModel::rmdir(const QModelIndex &aindex) Performed quick listing and see if any files have been added or removed, then fetch more information on visible files. */ -void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files) +void QFileSystemModelPrivate::directoryChanged(const QString &directory, const QStringList &files) { QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory, false); - if (parentNode->children.count() == 0) + if (parentNode->children.size() == 0) return; QStringList toRemove; QStringList newFiles = files; @@ -1785,7 +1782,7 @@ void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, cons if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator)) toRemove.append(i.value()->fileName); } - for (int i = 0 ; i < toRemove.count() ; ++i ) + for (int i = 0 ; i < toRemove.size() ; ++i ) removeNode(parentNode, toRemove[i]); } @@ -1877,11 +1874,11 @@ void QFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode, const QModelIndex parent = index(parentNode); bool indexHidden = isHiddenByFilter(parentNode, parent); if (!indexHidden) { - q->beginInsertRows(parent, parentNode->visibleChildren.count() , parentNode->visibleChildren.count() + newFiles.count() - 1); + q->beginInsertRows(parent, parentNode->visibleChildren.size() , parentNode->visibleChildren.size() + newFiles.size() - 1); } if (parentNode->dirtyChildrenIndex == -1) - parentNode->dirtyChildrenIndex = parentNode->visibleChildren.count(); + parentNode->dirtyChildrenIndex = parentNode->visibleChildren.size(); for (const auto &newFile : newFiles) { parentNode->visibleChildren.append(newFile); @@ -1920,8 +1917,8 @@ void QFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode, int The thread has received new information about files, update and emit dataChanged if it has actually changed. */ -void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, - const QList<QPair<QString, QFileInfo>> &updates) +void QFileSystemModelPrivate::fileSystemChanged(const QString &path, + const QList<std::pair<QString, QFileInfo>> &updates) { #if QT_CONFIG(filesystemwatcher) Q_Q(QFileSystemModel); @@ -1932,7 +1929,7 @@ void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, for (const auto &update : updates) { QString fileName = update.first; Q_ASSERT(!fileName.isEmpty()); - QExtendedInformation info = fileInfoGatherer.getInfo(update.second); + QExtendedInformation info = fileInfoGatherer->getInfo(update.second); bool previouslyHere = parentNode->children.contains(fileName); if (!previouslyHere) { addNode(parentNode, fileName, info.fileInfo()); @@ -1977,7 +1974,7 @@ void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, std::sort(rowsToUpdate.begin(), rowsToUpdate.end()); QString min; QString max; - for (const QString &value : qAsConst(rowsToUpdate)) { + 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; @@ -1995,23 +1992,30 @@ void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, int visibleMin = parentNode->visibleLocation(min); int visibleMax = parentNode->visibleLocation(max); if (visibleMin >= 0 - && visibleMin < parentNode->visibleChildren.count() + && visibleMin < parentNode->visibleChildren.size() && parentNode->visibleChildren.at(visibleMin) == min && visibleMax >= 0) { - QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMin), 0, parentIndex); - QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMax), 3, parentIndex); - emit q->dataChanged(bottom, top); + // 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.count() > 0) { + if (newFiles.size() > 0) { addVisibleFiles(parentNode, newFiles); } - if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) { + if (newFiles.size() > 0 || (sortColumn != 0 && rowsToUpdate.size() > 0)) { forceSort = true; delayedSort(); } @@ -2024,7 +2028,7 @@ void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, /*! \internal */ -void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName) +void QFileSystemModelPrivate::resolvedName(const QString &fileName, const QString &resolvedName) { resolvedSymLinks[fileName] = resolvedName; } @@ -2050,25 +2054,48 @@ QStringList QFileSystemModelPrivate::unwatchPathsAt(const QModelIndex &index) if (pathSize == watchedPath.size()) { return path.compare(watchedPath, caseSensitivity) == 0; } else if (watchedPath.size() > pathSize) { - return watchedPath.at(pathSize) == QLatin1Char('/') + return watchedPath.at(pathSize) == u'/' && watchedPath.startsWith(path, caseSensitivity); } return false; }; - const QStringList &watchedFiles = fileInfoGatherer.watchedFiles(); + const QStringList &watchedFiles = fileInfoGatherer->watchedFiles(); std::copy_if(watchedFiles.cbegin(), watchedFiles.cend(), std::back_inserter(result), filter); - const QStringList &watchedDirectories = fileInfoGatherer.watchedDirectories(); + const QStringList &watchedDirectories = fileInfoGatherer->watchedDirectories(); std::copy_if(watchedDirectories.cbegin(), watchedDirectories.cend(), std::back_inserter(result), filter); - fileInfoGatherer.unwatchPaths(result); + 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 */ @@ -2078,18 +2105,20 @@ void QFileSystemModelPrivate::init() delayedSortTimer.setSingleShot(true); - qRegisterMetaType<QList<QPair<QString, QFileInfo>>>(); + qRegisterMetaType<QList<std::pair<QString, QFileInfo>>>(); #if QT_CONFIG(filesystemwatcher) - q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(QString,QStringList)), - q, SLOT(_q_directoryChanged(QString,QStringList))); - q->connect(&fileInfoGatherer, SIGNAL(updates(QString, QList<QPair<QString, QFileInfo>>)), q, - SLOT(_q_fileSystemChanged(QString, QList<QPair<QString, QFileInfo>>))); - q->connect(&fileInfoGatherer, SIGNAL(nameResolved(QString,QString)), - q, SLOT(_q_resolvedName(QString,QString))); - q->connect(&fileInfoGatherer, SIGNAL(directoryLoaded(QString)), - q, SIGNAL(directoryLoaded(QString))); + 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 - q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection); + QObjectPrivate::connect(&delayedSortTimer, &QTimer::timeout, + this, &QFileSystemModelPrivate::performDelayedSort, + Qt::QueuedConnection); } /*! @@ -2102,8 +2131,14 @@ void QFileSystemModelPrivate::init() */ 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 || bypassFilters.contains(node)) + if (node->parent == &root || (!shouldHideDirNode && bypassFilters.contains(node))) return true; // If we don't know anything yet don't accept it @@ -2112,7 +2147,6 @@ bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) co const bool filterPermissions = ((filters & QDir::PermissionMask) && (filters & QDir::PermissionMask) != QDir::PermissionMask); - const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs)); const bool hideFiles = !(filters & QDir::Files); const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable)); const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable)); @@ -2124,8 +2158,8 @@ bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) co const bool hideDotDot = (filters & QDir::NoDotDot); // Note that we match the behavior of entryList and not QFileInfo on this. - bool isDot = (node->fileName == QLatin1String(".")); - bool isDotDot = (node->fileName == QLatin1String("..")); + bool isDot = (node->fileName == "."_L1); + bool isDotDot = (node->fileName == ".."_L1); if ( (hideHidden && !(isDot || isDotDot) && node->isHidden()) || (hideSystem && node->isSystem()) || (hideDirs && node->isDir()) diff --git a/src/gui/itemmodels/qfilesystemmodel.h b/src/gui/itemmodels/qfilesystemmodel.h index a79c4c14df..1fd1041f15 100644 --- a/src/gui/itemmodels/qfilesystemmodel.h +++ b/src/gui/itemmodels/qfilesystemmodel.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets 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 QFILESYSTEMMODEL_H #define QFILESYSTEMMODEL_H @@ -45,7 +9,6 @@ #include <QtCore/qpair.h> #include <QtCore/qdir.h> #include <QtGui/qicon.h> -#include <QtCore/qdiriterator.h> QT_REQUIRE_CONFIG(filesystemmodel); @@ -69,11 +32,13 @@ Q_SIGNALS: 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 + FilePermissions = Qt::UserRole + 3, }; enum Option @@ -149,7 +114,9 @@ public: 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); @@ -168,13 +135,6 @@ private: Q_DECLARE_PRIVATE(QFileSystemModel) Q_DISABLE_COPY(QFileSystemModel) - Q_PRIVATE_SLOT(d_func(), void _q_directoryChanged(const QString &directory, const QStringList &list)) - Q_PRIVATE_SLOT(d_func(), void _q_performDelayedSort()) - Q_PRIVATE_SLOT(d_func(), - void _q_fileSystemChanged(const QString &path, - const QList<QPair<QString, QFileInfo>> &)) - Q_PRIVATE_SLOT(d_func(), void _q_resolvedName(const QString &fileName, const QString &resolvedName)) - friend class QFileDialogPrivate; }; diff --git a/src/gui/itemmodels/qfilesystemmodel_p.h b/src/gui/itemmodels/qfilesystemmodel_p.h index 70468460e8..e01b0d56e6 100644 --- a/src/gui/itemmodels/qfilesystemmodel_p.h +++ b/src/gui/itemmodels/qfilesystemmodel_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 QtWidgets 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 QFILESYSTEMMODEL_P_H #define QFILESYSTEMMODEL_P_H @@ -86,7 +50,10 @@ public: Q_DECLARE_TYPEINFO(QFileSystemModelNodePathKey, Q_RELOCATABLE_TYPE); -inline size_t qHash(const QFileSystemModelNodePathKey &key) { return qHash(key.toCaseFolded()); } +inline size_t qHash(const QFileSystemModelNodePathKey &key, size_t seed = 0) +{ + return qHash(key.toCaseFolded(), seed); +} #else // Q_OS_WIN typedef QString QFileSystemModelNodePathKey; #endif @@ -96,7 +63,13 @@ class Q_GUI_EXPORT QFileSystemModelPrivate : public QAbstractItemModelPrivate Q_DECLARE_PUBLIC(QFileSystemModel) public: - enum { NumColumns = 4 }; + enum { + NameColumn, + SizeColumn, + TypeColumn, + TimeColumn, + NumColumns = 4 + }; class QFileSystemNode { @@ -116,8 +89,8 @@ public: #endif inline qint64 size() const { if (info && !info->isDir()) return info->size(); return 0; } - inline QString type() const { if (info) return info->displayType; return QLatin1String(""); } - inline QDateTime lastModified() const { if (info) return info->lastModified(); return QDateTime(); } + 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); } @@ -125,7 +98,7 @@ public: inline bool isDir() const { if (info) return info->isDir(); - if (children.count() > 0) + if (children.size() > 0) return true; return false; } @@ -177,30 +150,37 @@ public: 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 : qAsConst(children)) { + + 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(QLatin1Char('/'))) + if (path.endsWith(u'/')) child->updateIcon(iconProvider, path + child->fileName); else - child->updateIcon(iconProvider, path + QLatin1Char('/') + child->fileName); + 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 : qAsConst(children)) { + 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(QLatin1Char('/'))) + if (path.endsWith(u'/')) child->retranslateStrings(iconProvider, path + child->fileName); else - child->retranslateStrings(iconProvider, path + QLatin1Char('/') + child->fileName); + child->retranslateStrings(iconProvider, path + u'/' + child->fileName); } else child->retranslateStrings(iconProvider, child->fileName); } @@ -215,7 +195,8 @@ public: bool isVisible = false; }; - QFileSystemModelPrivate() = default; + QFileSystemModelPrivate(); + ~QFileSystemModelPrivate(); void init(); /* \internal @@ -241,7 +222,7 @@ public: inline int translateVisibleLocation(QFileSystemNode *parent, int row) const { if (sortOrder != Qt::AscendingOrder) { if (parent->dirtyChildrenIndex == -1) - return parent->visibleChildren.count() - row - 1; + return parent->visibleChildren.size() - row - 1; if (row < parent->dirtyChildrenIndex) return parent->dirtyChildrenIndex - row - 1; @@ -276,18 +257,18 @@ public: QString type(const QModelIndex &index) const; QString time(const QModelIndex &index) const; - void _q_directoryChanged(const QString &directory, const QStringList &list); - void _q_performDelayedSort(); - void _q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo>> &); - void _q_resolvedName(const QString &fileName, const QString &resolvedName); + 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); } + void watchPaths(const QStringList &paths) { fileInfoGatherer->watchPaths(paths); } # endif // Q_OS_WIN - QFileInfoGatherer fileInfoGatherer; + std::unique_ptr<QFileInfoGatherer> fileInfoGatherer; #endif // filesystemwatcher QTimer delayedSortTimer; QHash<const QFileSystemNode*, bool> bypassFilters; diff --git a/src/gui/itemmodels/qstandarditemmodel.cpp b/src/gui/itemmodels/qstandarditemmodel.cpp index ed3ffe0460..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" @@ -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"); @@ -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; - QList<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; } @@ -355,11 +322,11 @@ void QStandardItemPrivate::sortChildren(int column, Qt::SortOrder order) } QModelIndexList changedPersistentIndexesFrom, changedPersistentIndexesTo; - QList<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; @@ -407,7 +374,7 @@ void QStandardItemPrivate::setModel(QStandardItemModel *mod) } itm->d_func()->model = mod; const QList<QStandardItem*> &childList = itm->d_func()->children; - for (int i = 0; i < childList.count(); ++i) { + for (int i = 0; i < childList.size(); ++i) { QStandardItem *chi = childList.at(i); if (chi) stack.push(chi); @@ -468,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,7 +449,7 @@ bool QStandardItemPrivate::insertRows(int row, const QList<QStandardItem*> &item if (index != -1) 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; @@ -514,7 +481,7 @@ bool QStandardItemPrivate::insertRows(int row, int count, const QList<QStandardI } 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) { @@ -559,7 +526,7 @@ bool QStandardItemPrivate::insertColumns(int column, int count, const QList<QSta } } 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) { @@ -876,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; @@ -905,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. @@ -961,6 +934,11 @@ void QStandardItem::clearData() 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. */ @@ -1020,7 +998,7 @@ void QStandardItem::emitDataChanged() */ void QStandardItem::setFlags(Qt::ItemFlags flags) { - setData((int)flags, Qt::UserRole - 1); + setData((int)flags, DataFlagsRole); } /*! @@ -1035,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); @@ -1624,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); } @@ -1653,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); } @@ -1895,28 +1873,30 @@ QStandardItem *QStandardItem::takeChild(int row, int column) if (index != -1) { QModelIndex changedIdx; item = d->children.at(index); - if (item && d->model) { + if (item) { QStandardItemPrivate *const item_d = item->d_func(); - const int savedRows = item_d->rows; - const int savedCols = item_d->columns; - const QVector<QStandardItem*> savedChildren = item_d->children; - if (savedRows > 0) { - d->model->d_func()->rowsAboutToBeRemoved(item, 0, savedRows - 1); - item_d->rows = 0; - item_d->children = QVector<QStandardItem*>(); //slightly faster than clear - d->model->d_func()->rowsRemoved(item, 0, savedRows); - } - if (savedCols > 0) { - d->model->d_func()->columnsAboutToBeRemoved(item, 0, savedCols - 1); - item_d->columns = 0; - if (!item_d->children.isEmpty()) + 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 - d->model->d_func()->columnsRemoved(item, 0, savedCols); + 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->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); @@ -2565,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(); @@ -2588,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(); @@ -3136,21 +3116,21 @@ QStringList QStandardItemModel::mimeTypes() const */ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const { - QMimeData *data = QAbstractItemModel::mimeData(indexes); + 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); @@ -3169,7 +3149,7 @@ QMimeData *QStandardItemModel::mimeData(const QModelIndexList &indexes) const continue; const QList<QStandardItem*> &childList = itm->d_func()->children; - for (int i = 0; i < childList.count(); ++i) { + for (int i = 0; i < childList.size(); ++i) { QStandardItem *chi = childList.at(i); if (chi) { itemsSet.remove(chi); @@ -3179,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 @@ -3189,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() << int(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(); } @@ -3278,15 +3258,15 @@ bool QStandardItemModel::dropMimeData(const QMimeData *data, Qt::DropAction acti // Compute the number of continuous rows upon insertion and modify the rows to match QList<int> rowsToInsert(bottom + 1); - for (int i = 0; i < rows.count(); ++i) + 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); diff --git a/src/gui/itemmodels/qstandarditemmodel.h b/src/gui/itemmodels/qstandarditemmodel.h index 66ae3a27cd..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 @@ -110,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); @@ -143,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) @@ -274,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); } diff --git a/src/gui/itemmodels/qstandarditemmodel_p.h b/src/gui/itemmodels/qstandarditemmodel_p.h index 2ce640679a..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,8 +34,10 @@ 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; } @@ -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); |