From 9593df26c4a87130947dbdacf5ddb2f7a3412cbc Mon Sep 17 00:00:00 2001 From: Martin Petersson Date: Mon, 20 Feb 2012 14:56:12 +0100 Subject: FolderListModel: remove widget and QDirModel use FolderListModel used the obsolete QDirModel internally. Because of this it needed widgets to work. I have made a threaded model instead that use QDir internally. Change-Id: Ibd1267a135ee3c6df7bcde420073866b7a76d0d1 Reviewed-by: Alan Alpert --- src/imports/folderlistmodel/fileinfothread.cpp | 280 ++++++++++++ src/imports/folderlistmodel/fileinfothread_p.h | 107 +++++ src/imports/folderlistmodel/fileproperty_p.h | 94 ++++ src/imports/folderlistmodel/folderlistmodel.pro | 9 +- .../qdeclarativefolderlistmodel.cpp | 494 ++++++++++++++------- .../folderlistmodel/qdeclarativefolderlistmodel.h | 60 +-- .../tst_qdeclarativefolderlistmodel.cpp | 4 +- 7 files changed, 855 insertions(+), 193 deletions(-) create mode 100644 src/imports/folderlistmodel/fileinfothread.cpp create mode 100644 src/imports/folderlistmodel/fileinfothread_p.h create mode 100644 src/imports/folderlistmodel/fileproperty_p.h diff --git a/src/imports/folderlistmodel/fileinfothread.cpp b/src/imports/folderlistmodel/fileinfothread.cpp new file mode 100644 index 0000000000..3c4d60bb89 --- /dev/null +++ b/src/imports/folderlistmodel/fileinfothread.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "fileinfothread_p.h" +#include + +#include + + +FileInfoThread::FileInfoThread(QObject *parent) + : QThread(parent), + abort(false), + watcher(0), + sortFlags(QDir::Name), + needUpdate(true), + folderUpdate(false), + sortUpdate(false), + showDirs(true), + showDirsFirst(false), + showDotDot(false), + showOnlyReadable(false) +{ + watcher = new QFileSystemWatcher(this); + connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString))); + connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(updateFile(QString))); + start(LowPriority); +} + +FileInfoThread::~FileInfoThread() +{ + QMutexLocker locker(&mutex); + abort = true; + condition.wakeOne(); + locker.unlock(); + wait(); +} + +void FileInfoThread::clear() +{ + QMutexLocker locker(&mutex); + watcher->removePaths(watcher->files()); + watcher->removePaths(watcher->directories()); +} + +void FileInfoThread::removePath(const QString &path) +{ + QMutexLocker locker(&mutex); + watcher->removePath(path); + currentPath.clear(); +} + +void FileInfoThread::setPath(const QString &path) +{ + Q_ASSERT(!path.isEmpty()); + + QMutexLocker locker(&mutex); + watcher->addPath(path); + currentPath = path; + needUpdate = true; + condition.wakeAll(); +} + +void FileInfoThread::setRootPath(const QString &path) +{ + Q_ASSERT(!path.isEmpty()); + + QMutexLocker locker(&mutex); + rootPath = path; +} + +void FileInfoThread::dirChanged(const QString &directoryPath) +{ + Q_UNUSED(directoryPath); + QMutexLocker locker(&mutex); + folderUpdate = true; + condition.wakeAll(); +} + +void FileInfoThread::setSortFlags(QDir::SortFlags flags) +{ + QMutexLocker locker(&mutex); + sortFlags = flags; + sortUpdate = true; + condition.wakeAll(); +} + +void FileInfoThread::setNameFilters(const QStringList & filters) +{ + QMutexLocker locker(&mutex); + nameFilters = filters; + folderUpdate = true; + condition.wakeAll(); +} + +void FileInfoThread::setShowDirs(bool showFolders) +{ + QMutexLocker locker(&mutex); + showDirs = showFolders; + folderUpdate = true; + condition.wakeAll(); +} + +void FileInfoThread::setShowDirsFirst(bool showDirsFirst) +{ + QMutexLocker locker(&mutex); + showDirsFirst = showDirsFirst; + folderUpdate = true; + condition.wakeAll(); +} + +void FileInfoThread::setShowDotDot(bool on) +{ + QMutexLocker locker(&mutex); + showDotDot = on; + folderUpdate = true; + condition.wakeAll(); +} + +void FileInfoThread::setShowOnlyReadable(bool on) +{ + QMutexLocker locker(&mutex); + showOnlyReadable = on; + folderUpdate = true; + condition.wakeAll(); +} + +void FileInfoThread::updateFile(const QString &path) +{ + Q_UNUSED(path); + QMutexLocker locker(&mutex); + folderUpdate = true; + condition.wakeAll(); +} + +void FileInfoThread::run() +{ + forever { + bool updateFiles = false; + QMutexLocker locker(&mutex); + if (abort) { + return; + } + if (currentPath.isEmpty() || !needUpdate) + condition.wait(&mutex); + + if (abort) { + return; + } + + if (!currentPath.isEmpty()) { + updateFiles = true; + } + if (updateFiles) + getFileInfos(currentPath); + locker.unlock(); + } +} + + +void FileInfoThread::getFileInfos(const QString &path) +{ + QDir::Filters filter; + filter = QDir::Files | QDir::NoDot | QDir::CaseSensitive; + if (showDirs) + filter = filter | QDir::AllDirs | QDir::Drives; + if ((path == rootPath) || !showDotDot) + filter = filter | QDir::NoDotDot; + if (showOnlyReadable) + filter = filter | QDir::Readable; + if (showDirsFirst) + sortFlags = sortFlags | QDir::DirsFirst; + + QDir currentDir(path, QString(), sortFlags); + QFileInfoList fileInfoList; + QList filePropertyList; + + fileInfoList = currentDir.entryInfoList(nameFilters, filter, sortFlags); + + if (!fileInfoList.isEmpty()) { + foreach (QFileInfo info, fileInfoList) { + //qDebug() << "Adding file : " << info.fileName() << "to list "; + filePropertyList << FileProperty(info); + } + if (folderUpdate) { + int fromIndex = 0; + int toIndex = currentFileList.size()-1; + findChangeRange(filePropertyList, fromIndex, toIndex); + folderUpdate = false; + currentFileList = filePropertyList; + //qDebug() << "emit directoryUpdated : " << fromIndex << " " << toIndex; + emit directoryUpdated(path, filePropertyList, fromIndex, toIndex); + } else { + currentFileList = filePropertyList; + if (sortUpdate) { + emit sortFinished(filePropertyList); + sortUpdate = false; + } else + emit directoryChanged(path, filePropertyList); + } + } else { + // The directory is empty + if (folderUpdate) { + int fromIndex = 0; + int toIndex = currentFileList.size()-1; + folderUpdate = false; + currentFileList.clear(); + emit directoryUpdated(path, filePropertyList, fromIndex, toIndex); + } else { + currentFileList.clear(); + emit directoryChanged(path, filePropertyList); + } + } + needUpdate = false; +} + +void FileInfoThread::findChangeRange(const QList &list, int &fromIndex, int &toIndex) +{ + if (currentFileList.size() == 0) { + fromIndex = 0; + toIndex = list.size(); + return; + } + + int i; + int listSize = list.size() < currentFileList.size() ? list.size() : currentFileList.size(); + bool changeFound = false; + + for (i=0; i < listSize; i++) { + if (list.at(i) != currentFileList.at(i)) { + changeFound = true; + break; + } + } + + if (changeFound) + fromIndex = i; + else + fromIndex = i-1; + + // For now I let the rest of the list be updated.. + toIndex = list.size() > currentFileList.size() ? list.size() - 1 : currentFileList.size() - 1; +} diff --git a/src/imports/folderlistmodel/fileinfothread_p.h b/src/imports/folderlistmodel/fileinfothread_p.h new file mode 100644 index 0000000000..a5be6e6fcc --- /dev/null +++ b/src/imports/folderlistmodel/fileinfothread_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FILEINFOTHREAD_P_H +#define FILEINFOTHREAD_P_H + +#include +#include +#include +#include +#include +#include + +#include "fileproperty_p.h" + +class FileInfoThread : public QThread +{ + Q_OBJECT + +Q_SIGNALS: + void directoryChanged(const QString &directory, const QList &list) const; + void directoryUpdated(const QString &directory, const QList &list, int fromIndex, int toIndex) const; + void sortFinished(const QList &list) const; + +public: + FileInfoThread(QObject *parent = 0); + ~FileInfoThread(); + + void clear(); + void removePath(const QString &path); + void setPath(const QString &path); + void setRootPath(const QString &path); + void setSortFlags(QDir::SortFlags flags); + void setNameFilters(const QStringList & nameFilters); + void setShowDirs(bool showFolders); + void setShowDirsFirst(bool showDirsFirst); + void setShowDotDot(bool on); + void setShowOnlyReadable(bool on); + +public Q_SLOTS: + void dirChanged(const QString &directoryPath); + void updateFile(const QString &path); + +protected: + void run(); + void getFileInfos(const QString &path); + void findChangeRange(const QList &list, int &fromIndex, int &toIndex); + +private: + QMutex mutex; + QWaitCondition condition; + volatile bool abort; + + QFileSystemWatcher *watcher; + QList currentFileList; + QDir::SortFlags sortFlags; + QString currentPath; + QString rootPath; + QStringList nameFilters; + bool needUpdate; + bool folderUpdate; + bool sortUpdate; + bool showDirs; + bool showDirsFirst; + bool showDotDot; + bool showOnlyReadable; +}; + +#endif // FILEINFOTHREAD_P_H diff --git a/src/imports/folderlistmodel/fileproperty_p.h b/src/imports/folderlistmodel/fileproperty_p.h new file mode 100644 index 0000000000..690581a9a3 --- /dev/null +++ b/src/imports/folderlistmodel/fileproperty_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FILEPROPERTY_P_H +#define FILEPROPERTY_P_H + +#include +#include + +class FileProperty +{ +public: + FileProperty(const QFileInfo &info) + { + mFileName = info.fileName(); + mFilePath = info.filePath(); + mBaseName = info.baseName(); + mSize = info.size(); + mSuffix = info.completeSuffix(); + mIsDir = info.isDir(); + mIsFile = info.isFile(); + mLastModified = info.lastModified(); + mLastRead = info.lastRead(); + } + ~FileProperty() + {} + + inline QString fileName() const { return mFileName; } + inline QString filePath() const { return mFilePath; } + inline QString baseName() const { return mBaseName; } + inline qint64 size() const { return mSize; } + inline QString suffix() const { return mSuffix; } + inline bool isDir() const { return mIsDir; } + inline bool isFile() const { return mIsFile; } + inline QDateTime lastModified() const { return mLastModified; } + inline QDateTime lastRead() const { return mLastRead; } + + inline bool operator !=(const FileProperty &fileInfo) const { + return !operator==(fileInfo); + } + bool operator ==(const FileProperty &property) const { + return ((mFileName == property.mFileName) && (isDir() == property.isDir())); + } + +private: + QString mFileName; + QString mFilePath; + QString mBaseName; + QString mSuffix; + qint64 mSize; + bool mIsDir; + bool mIsFile; + QDateTime mLastModified; + QDateTime mLastRead; +}; +#endif // FILEPROPERTY_P_H diff --git a/src/imports/folderlistmodel/folderlistmodel.pro b/src/imports/folderlistmodel/folderlistmodel.pro index 26efc654b8..9727b513e6 100644 --- a/src/imports/folderlistmodel/folderlistmodel.pro +++ b/src/imports/folderlistmodel/folderlistmodel.pro @@ -2,10 +2,13 @@ TARGET = qmlfolderlistmodelplugin TARGETPATH = Qt/labs/folderlistmodel include(../qimportbase.pri) -QT += widgets declarative +QT += declarative -SOURCES += qdeclarativefolderlistmodel.cpp plugin.cpp -HEADERS += qdeclarativefolderlistmodel.h +SOURCES += qdeclarativefolderlistmodel.cpp plugin.cpp \ + fileinfothread.cpp +HEADERS += qdeclarativefolderlistmodel.h \ + fileproperty_p.h \ + fileinfothread_p.h DESTDIR = $$QT.declarative.imports/$$TARGETPATH target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH diff --git a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp index 870479afad..fa1a181e1f 100644 --- a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp @@ -41,25 +41,71 @@ //![code] #include "qdeclarativefolderlistmodel.h" -#include +#include "fileinfothread_p.h" +#include "fileproperty_p.h" #include #include -#ifndef QT_NO_DIRMODEL - QT_BEGIN_NAMESPACE class QDeclarativeFolderListModelPrivate { + Q_DECLARE_PUBLIC(QDeclarativeFolderListModel) + public: - QDeclarativeFolderListModelPrivate() - : sortField(QDeclarativeFolderListModel::Name), sortReversed(false), count(0), showDirs(true), showDots(false), showOnlyReadable(false), insideRefresh(false) { + QDeclarativeFolderListModelPrivate(QDeclarativeFolderListModel *q) + : q_ptr(q), + sortField(QDeclarativeFolderListModel::Name), sortReversed(false), showDirs(true), showDirsFirst(false), showDots(false), showOnlyReadable(false) + { nameFilters << QLatin1String("*"); } - void updateSorting() { - QDir::SortFlags flags = 0; - switch(sortField) { + + QDeclarativeFolderListModel *q_ptr; + QUrl currentDir; + QUrl rootDir; + FileInfoThread fileInfoThread; + QList data; + QHash roleNames; + QDeclarativeFolderListModel::SortField sortField; + QStringList nameFilters; + bool sortReversed; + bool showDirs; + bool showDirsFirst; + bool showDots; + bool showOnlyReadable; + + ~QDeclarativeFolderListModelPrivate() {} + void init(); + void updateSorting(); + + // private slots + void _q_directoryChanged(const QString &directory, const QList &list); + void _q_directoryUpdated(const QString &directory, const QList &list, int fromIndex, int toIndex); + void _q_sortFinished(const QList &list); +}; + + +void QDeclarativeFolderListModelPrivate::init() +{ + Q_Q(QDeclarativeFolderListModel); + qRegisterMetaType >("QList"); + q->connect(&fileInfoThread, SIGNAL(directoryChanged(QString, QList)), + q, SLOT(_q_directoryChanged(QString, QList))); + q->connect(&fileInfoThread, SIGNAL(directoryUpdated(QString, QList, int, int)), + q, SLOT(_q_directoryUpdated(QString, QList, int, int))); + q->connect(&fileInfoThread, SIGNAL(sortFinished(QList)), + q, SLOT(_q_sortFinished(QList))); +} + + +void QDeclarativeFolderListModelPrivate::updateSorting() +{ + Q_Q(QDeclarativeFolderListModel); + + QDir::SortFlags flags = 0; + + switch (sortField) { case QDeclarativeFolderListModel::Unsorted: flags |= QDir::Unsorted; break; @@ -75,26 +121,80 @@ public: case QDeclarativeFolderListModel::Type: flags |= QDir::Type; break; - } + default: + break; + } + + emit q->layoutAboutToBeChanged(); + + if (sortReversed) + flags |= QDir::Reversed; + + fileInfoThread.setSortFlags(flags); +} + +void QDeclarativeFolderListModelPrivate::_q_directoryChanged(const QString &directory, const QList &list) +{ + Q_Q(QDeclarativeFolderListModel); + Q_UNUSED(directory); + + data = list; + q->endResetModel(); + emit q->rowCountChanged(); + emit q->folderChanged(); +} - if (sortReversed) - flags |= QDir::Reversed; - model.setSorting(flags); +void QDeclarativeFolderListModelPrivate::_q_directoryUpdated(const QString &directory, const QList &list, int fromIndex, int toIndex) +{ + Q_Q(QDeclarativeFolderListModel); + Q_UNUSED(directory); + + QModelIndex parent; + if (data.size() > list.size()) { + //File(s) removed. Since I do not know how many + //or where I need to update the whole list from the first item. + data = list; + q->beginRemoveRows(parent, fromIndex, toIndex); + q->endRemoveRows(); + q->beginInsertRows(parent, fromIndex, list.size()-1); + q->endInsertRows(); + emit q->rowCountChanged(); + } else if (data.size() < list.size()) { + //qDebug() << "File added. FromIndex: " << fromIndex << " toIndex: " << toIndex << " list size: " << list.size(); + //File(s) added. Calculate how many and insert + //from the first changed one. + toIndex = fromIndex + (list.size() - data.size()-1); + q->beginInsertRows(parent, fromIndex, toIndex); + q->endInsertRows(); + data = list; + emit q->rowCountChanged(); + QModelIndex modelIndexFrom = q->createIndex(fromIndex, 0); + QModelIndex modelIndexTo = q->createIndex(toIndex, 0); + emit q->dataChanged(modelIndexFrom, modelIndexTo); + } else { + //qDebug() << "File has been updated"; + QModelIndex modelIndexFrom = q->createIndex(fromIndex, 0); + QModelIndex modelIndexTo = q->createIndex(toIndex, 0); + data = list; + emit q->dataChanged(modelIndexFrom, modelIndexTo); } +} + +void QDeclarativeFolderListModelPrivate::_q_sortFinished(const QList &list) +{ + Q_Q(QDeclarativeFolderListModel); + + QModelIndex parent; + q->beginRemoveRows(parent, 0, data.size()-1); + data.clear(); + q->endRemoveRows(); + + q->beginInsertRows(parent, 0, list.size()-1); + data = list; + q->endInsertRows(); +} - QDirModel model; - QUrl folder; - QStringList nameFilters; - QModelIndex folderIndex; - QDeclarativeFolderListModel::SortField sortField; - bool sortReversed; - int count; - bool showDirs; - bool showDots; - bool showOnlyReadable; - bool insideRefresh; -}; /*! \qmlclass FolderListModel QDeclarativeFolderListModel @@ -115,8 +215,14 @@ public: Components access names and paths via the following roles: \list - \o fileName - \o filePath + \o \c fileName + \o \c filePath + \o \c fileBaseName + \o \c fileSuffix + \o \c fileSize + \o \c fileModified + \o \c fileAccessed + \o \c fileIsDir \endlist Additionally a file entry can be differentiated from a folder entry via the @@ -157,39 +263,62 @@ public: */ QDeclarativeFolderListModel::QDeclarativeFolderListModel(QObject *parent) - : QAbstractListModel(parent) + : QAbstractListModel(parent), d_ptr(new QDeclarativeFolderListModelPrivate(this)) { - QHash roles; - roles[FileNameRole] = "fileName"; - roles[FilePathRole] = "filePath"; - setRoleNames(roles); - - d = new QDeclarativeFolderListModelPrivate; - d->model.setFilter(QDir::AllDirs | QDir::Files | QDir::Drives | QDir::NoDotAndDotDot); - connect(&d->model, SIGNAL(rowsInserted(const QModelIndex&,int,int)) - , this, SLOT(inserted(const QModelIndex&,int,int))); - connect(&d->model, SIGNAL(rowsRemoved(const QModelIndex&,int,int)) - , this, SLOT(removed(const QModelIndex&,int,int))); - connect(&d->model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)) - , this, SLOT(handleDataChanged(const QModelIndex&,const QModelIndex&))); - connect(&d->model, SIGNAL(modelReset()), this, SLOT(refresh())); - connect(&d->model, SIGNAL(layoutChanged()), this, SLOT(refresh())); + Q_D(QDeclarativeFolderListModel); + d->roleNames[FileNameRole] = "fileName"; + d->roleNames[FilePathRole] = "filePath"; + d->roleNames[FileBaseNameRole] = "fileBaseName"; + d->roleNames[FileSuffixRole] = "fileSuffix"; + d->roleNames[FileSizeRole] = "fileSize"; + d->roleNames[FileLastModifiedRole] = "fileModified"; + d->roleNames[FileLastReadRole] = "fileAccessed"; + d->roleNames[FileIsDirRole] = "fileIsDir"; + setRoleNames(d->roleNames); + + d->init(); } QDeclarativeFolderListModel::~QDeclarativeFolderListModel() { - delete d; } QVariant QDeclarativeFolderListModel::data(const QModelIndex &index, int role) const { + Q_D(const QDeclarativeFolderListModel); QVariant rv; - QModelIndex modelIndex = d->model.index(index.row(), 0, d->folderIndex); - if (modelIndex.isValid()) { - if (role == FileNameRole) - rv = d->model.data(modelIndex, QDirModel::FileNameRole).toString(); - else if (role == FilePathRole) - rv = QUrl::fromLocalFile(d->model.data(modelIndex, QDirModel::FilePathRole).toString()); + + if (index.row() >= d->data.size()) + return rv; + + switch (role) + { + case FileNameRole: + rv = d->data.at(index.row()).fileName(); + break; + case FilePathRole: + rv = d->data.at(index.row()).filePath(); + break; + case FileBaseNameRole: + rv = d->data.at(index.row()).baseName(); + break; + case FileSuffixRole: + rv = d->data.at(index.row()).suffix(); + break; + case FileSizeRole: + rv = d->data.at(index.row()).size(); + break; + case FileLastModifiedRole: + rv = d->data.at(index.row()).lastModified().date().toString(Qt::ISODate) + " " + d->data.at(index.row()).lastModified().time().toString(); + break; + case FileLastReadRole: + rv = d->data.at(index.row()).lastRead().date().toString(Qt::ISODate) + " " + d->data.at(index.row()).lastRead().time().toString(); + break; + case FileIsDirRole: + rv = d->data.at(index.row()).isDir(); + break; + default: + break; } return rv; } @@ -202,8 +331,14 @@ QVariant QDeclarativeFolderListModel::data(const QModelIndex &index, int role) c */ int QDeclarativeFolderListModel::rowCount(const QModelIndex &parent) const { + Q_D(const QDeclarativeFolderListModel); Q_UNUSED(parent); - return d->count; + return d->data.size(); +} + +QModelIndex QDeclarativeFolderListModel::index(int row, int , const QModelIndex &) const +{ + return createIndex(row, 0); } /*! @@ -219,46 +354,70 @@ int QDeclarativeFolderListModel::rowCount(const QModelIndex &parent) const */ QUrl QDeclarativeFolderListModel::folder() const { - return d->folder; + Q_D(const QDeclarativeFolderListModel); + return d->currentDir; } void QDeclarativeFolderListModel::setFolder(const QUrl &folder) { - if (folder == d->folder) + Q_D(QDeclarativeFolderListModel); + + if (folder == d->currentDir) return; - QModelIndex index = d->model.index(folder.toLocalFile()); // This can modify the filtering rules. - if ((index.isValid() && d->model.isDir(index)) || folder.toLocalFile().isEmpty()) { - d->folder = folder; - QMetaObject::invokeMethod(this, "resetFiltering", Qt::QueuedConnection); // resetFiltering will invoke refresh(). - emit folderChanged(); + QString resolvedPath = QDir::cleanPath(folder.path()); + + beginResetModel(); + + //Remove the old path for the file system watcher + if (!d->currentDir.isEmpty()) + d->fileInfoThread.removePath(d->currentDir.path()); + + d->currentDir = folder; + + QFileInfo info(resolvedPath); + if (!info.exists() || !info.isDir()) { + d->data.clear(); + endResetModel(); + emit rowCountChanged(); + return; } + + d->fileInfoThread.setPath(resolvedPath); +} + + +/*! + \qmlproperty string QDeclarativeFolderListModel::rootFolder + + When the rootFolder is set, then this folder will + be threated as the root in the file system, so that + you can only travers sub folders from this rootFolder. +*/ +QUrl QDeclarativeFolderListModel::rootFolder() const +{ + Q_D(const QDeclarativeFolderListModel); + return d->rootDir; } -void QDeclarativeFolderListModel::resetFiltering() +void QDeclarativeFolderListModel::setRootFolder(const QUrl &path) { - // ensure that we reset the filtering rules, because the QDirModel::index() - // function isn't quite as const as it claims to be. - QDir::Filters filt = d->model.filter(); + Q_D(QDeclarativeFolderListModel); - if (d->showDirs) - filt |= (QDir::AllDirs | QDir::Drives); - else - filt &= ~(QDir::AllDirs | QDir::Drives); + if (path.isEmpty()) + return; - if (d->showDots) - filt &= ~QDir::NoDotAndDotDot; - else - filt |= QDir::NoDotAndDotDot; + QString resolvedPath = QDir::cleanPath(path.path()); - if (d->showOnlyReadable) - filt |= QDir::Readable; - else - filt &= ~QDir::Readable; + QFileInfo info(resolvedPath); + if (!info.exists() || !info.isDir()) + return; - d->model.setFilter(filt); // this causes a refresh(). + d->fileInfoThread.setRootPath(resolvedPath); + d->rootDir = path; } + /*! \qmlproperty url FolderListModel::parentFolder @@ -266,7 +425,9 @@ void QDeclarativeFolderListModel::resetFiltering() */ QUrl QDeclarativeFolderListModel::parentFolder() const { - QString localFile = d->folder.toLocalFile(); + Q_D(const QDeclarativeFolderListModel); + + QString localFile = d->currentDir.toLocalFile(); if (!localFile.isEmpty()) { QDir dir(localFile); #if defined(Q_OS_WIN) @@ -277,10 +438,10 @@ QUrl QDeclarativeFolderListModel::parentFolder() const dir.cdUp(); localFile = dir.path(); } else { - int pos = d->folder.path().lastIndexOf(QLatin1Char('/')); + int pos = d->currentDir.path().lastIndexOf(QLatin1Char('/')); if (pos == -1) return QUrl(); - localFile = d->folder.path().left(pos); + localFile = d->currentDir.path().left(pos); } return QUrl::fromLocalFile(localFile); } @@ -303,13 +464,15 @@ QUrl QDeclarativeFolderListModel::parentFolder() const */ QStringList QDeclarativeFolderListModel::nameFilters() const { + Q_D(const QDeclarativeFolderListModel); return d->nameFilters; } void QDeclarativeFolderListModel::setNameFilters(const QStringList &filters) { + Q_D(QDeclarativeFolderListModel); + d->fileInfoThread.setNameFilters(filters); d->nameFilters = filters; - d->model.setNameFilters(d->nameFilters); } void QDeclarativeFolderListModel::classBegin() @@ -318,11 +481,10 @@ void QDeclarativeFolderListModel::classBegin() void QDeclarativeFolderListModel::componentComplete() { - if (!d->folder.isValid() || d->folder.toLocalFile().isEmpty() || !QDir().exists(d->folder.toLocalFile())) - setFolder(QUrl(QLatin1String("file://")+QDir::currentPath())); + Q_D(QDeclarativeFolderListModel); - if (!d->folderIndex.isValid()) - QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection); + if (!d->currentDir.isValid() || d->currentDir.toLocalFile().isEmpty() || !QDir().exists(d->currentDir.toLocalFile())) + setFolder(QUrl(QLatin1String("file://")+QDir::currentPath())); } /*! @@ -331,9 +493,9 @@ void QDeclarativeFolderListModel::componentComplete() The \a sortField property contains field to use for sorting. sortField may be one of: \list - \o Unsorted - no sorting is applied. The order is system default. + \o Unsorted - no sorting is applied. \o Name - sort by filename - \o Time - sort by time modified + \o LastModified - sort by time modified \o Size - sort by file size \o Type - sort by file type (extension) \endlist @@ -342,17 +504,25 @@ void QDeclarativeFolderListModel::componentComplete() */ QDeclarativeFolderListModel::SortField QDeclarativeFolderListModel::sortField() const { + Q_D(const QDeclarativeFolderListModel); return d->sortField; } void QDeclarativeFolderListModel::setSortField(SortField field) { + Q_D(QDeclarativeFolderListModel); if (field != d->sortField) { d->sortField = field; d->updateSorting(); } } +int QDeclarativeFolderListModel::roleFromString(const QString &roleName) const +{ + Q_D(const QDeclarativeFolderListModel); + return d->roleNames.key(roleName.toLatin1(), -1); +} + /*! \qmlproperty bool FolderListModel::sortReversed @@ -362,11 +532,14 @@ void QDeclarativeFolderListModel::setSortField(SortField field) */ bool QDeclarativeFolderListModel::sortReversed() const { + Q_D(const QDeclarativeFolderListModel); return d->sortReversed; } void QDeclarativeFolderListModel::setSortReversed(bool rev) { + Q_D(QDeclarativeFolderListModel); + if (rev != d->sortReversed) { d->sortReversed = rev; d->updateSorting(); @@ -382,91 +555,66 @@ void QDeclarativeFolderListModel::setSortReversed(bool rev) bool QDeclarativeFolderListModel::isFolder(int index) const { if (index != -1) { - QModelIndex idx = d->model.index(index, 0, d->folderIndex); - if (idx.isValid()) - return d->model.isDir(idx); + QModelIndex idx = createIndex(index, 0); + if (idx.isValid()) { + QVariant var = data(idx, FileIsDirRole); + if (var.isValid()) + return var.toBool(); + } } return false; } -void QDeclarativeFolderListModel::refresh() -{ - if (d->insideRefresh) - return; - d->insideRefresh = true; +/*! + \qmlproperty bool FolderListModel::showDirs - d->folderIndex = QModelIndex(); - if (d->count) { - emit beginRemoveRows(QModelIndex(), 0, d->count-1); - d->count = 0; - emit endRemoveRows(); - } + If true, directories are included in the model; otherwise only files + are included. - d->folderIndex = d->model.index(d->folder.toLocalFile()); - int newcount = d->model.rowCount(d->folderIndex); - if (newcount) { - emit beginInsertRows(QModelIndex(), 0, newcount-1); - d->count = newcount; - emit endInsertRows(); - } + By default, this property is true. - d->insideRefresh = false; // finished refreshing. -} + Note that the nameFilters are not applied to directories. -void QDeclarativeFolderListModel::inserted(const QModelIndex &index, int start, int end) + \sa showDotAndDotDot +*/ +bool QDeclarativeFolderListModel::showDirs() const { - if (index == d->folderIndex) { - emit beginInsertRows(QModelIndex(), start, end); - d->count = d->model.rowCount(d->folderIndex); - emit endInsertRows(); - } + Q_D(const QDeclarativeFolderListModel); + return d->showDirs; } -void QDeclarativeFolderListModel::removed(const QModelIndex &index, int start, int end) +void QDeclarativeFolderListModel::setShowDirs(bool on) { - if (index == d->folderIndex) { - emit beginRemoveRows(QModelIndex(), start, end); - d->count = d->model.rowCount(d->folderIndex); - emit endRemoveRows(); - } -} + Q_D(QDeclarativeFolderListModel); -void QDeclarativeFolderListModel::handleDataChanged(const QModelIndex &start, const QModelIndex &end) -{ - if (start.parent() == d->folderIndex) - emit dataChanged(index(start.row(),0), index(end.row(),0)); + d->fileInfoThread.setShowDirs(on); + d->showDirs = on; } /*! - \qmlproperty bool FolderListModel::showDirs + \qmlproperty bool FolderListModel::showDirsFirst - If true, directories are included in the model; otherwise only files - are included. + If true, if directories are included in the model they will + always be shown first, then the files. - By default, this property is true. - - Note that the nameFilters are not applied to directories. + By default, this property is false. - \sa showDotAndDotDot */ -bool QDeclarativeFolderListModel::showDirs() const +bool QDeclarativeFolderListModel::showDirsFirst() const { - return d->model.filter() & QDir::AllDirs; + Q_D(const QDeclarativeFolderListModel); + return d->showDirsFirst; } -void QDeclarativeFolderListModel::setShowDirs(bool on) +void QDeclarativeFolderListModel::setShowDirsFirst(bool on) { - if (!(d->model.filter() & QDir::AllDirs) == !on) - return; - if (on) { - d->showDirs = true; - d->model.setFilter(d->model.filter() | QDir::AllDirs | QDir::Drives); - } else { - d->showDirs = false; - d->model.setFilter(d->model.filter() & ~(QDir::AllDirs | QDir::Drives)); - } + Q_D(QDeclarativeFolderListModel); + + d->fileInfoThread.setShowDirsFirst(on); + d->showDirsFirst = on; } + /*! \qmlproperty bool FolderListModel::showDotAndDotDot @@ -479,19 +627,16 @@ void QDeclarativeFolderListModel::setShowDirs(bool on) */ bool QDeclarativeFolderListModel::showDotAndDotDot() const { - return !(d->model.filter() & QDir::NoDotAndDotDot); + Q_D(const QDeclarativeFolderListModel); + return d->showDots; } void QDeclarativeFolderListModel::setShowDotAndDotDot(bool on) { - if (!(d->model.filter() & QDir::NoDotAndDotDot) == on) - return; - if (on) { - d->showDots = true; - d->model.setFilter(d->model.filter() & ~QDir::NoDotAndDotDot); - } else { - d->showDots = false; - d->model.setFilter(d->model.filter() | QDir::NoDotAndDotDot); + Q_D(QDeclarativeFolderListModel); + + if (on != d->showDots) { + d->fileInfoThread.setShowDotDot(on); } } @@ -507,23 +652,46 @@ void QDeclarativeFolderListModel::setShowDotAndDotDot(bool on) */ bool QDeclarativeFolderListModel::showOnlyReadable() const { - return d->model.filter() & QDir::Readable; + Q_D(const QDeclarativeFolderListModel); + return d->showOnlyReadable; } void QDeclarativeFolderListModel::setShowOnlyReadable(bool on) { - if (!(d->model.filter() & QDir::Readable) == !on) - return; - if (on) { - d->showOnlyReadable = true; - d->model.setFilter(d->model.filter() | QDir::Readable); - } else { - d->showOnlyReadable = false; - d->model.setFilter(d->model.filter() & ~QDir::Readable); + Q_D(QDeclarativeFolderListModel); + + if (on != d->showOnlyReadable) { + d->fileInfoThread.setShowOnlyReadable(on); } } +/*! + \qmlmethod QVariant QDeclarativeFolderListModel::get(int idx, const QString &property) const + + Get the folder property for the given index. The following properties + are available. + + \list + \o \c fileName + \o \c filePath + \o \c fileBaseName + \o \c fileSuffix + \o \c fileSize + \o \c fileModified + \o \c fileAccessed + \o \c fileIsDir + \endlist +*/ +QVariant QDeclarativeFolderListModel::get(int idx, const QString &property) const +{ + int role = roleFromString(property); + if (role >= 0 && idx >= 0) + return data(index(idx, 0), role); + else + return QVariant(); +} + +#include "moc_qdeclarativefolderlistmodel.cpp" + //![code] QT_END_NAMESPACE - -#endif // QT_NO_DIRMODEL diff --git a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h index 5f9cb0e81a..27a7bc00a1 100644 --- a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h +++ b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h @@ -47,8 +47,6 @@ #include #include -#ifndef QT_NO_DIRMODEL - QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -68,14 +66,16 @@ class QDeclarativeFolderListModel : public QAbstractListModel, public QDeclarati //![class props] Q_PROPERTY(QUrl folder READ folder WRITE setFolder NOTIFY folderChanged) + Q_PROPERTY(QUrl rootFolder READ rootFolder WRITE setRootFolder) Q_PROPERTY(QUrl parentFolder READ parentFolder NOTIFY folderChanged) Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters) Q_PROPERTY(SortField sortField READ sortField WRITE setSortField) Q_PROPERTY(bool sortReversed READ sortReversed WRITE setSortReversed) Q_PROPERTY(bool showDirs READ showDirs WRITE setShowDirs) + Q_PROPERTY(bool showDirsFirst READ showDirsFirst WRITE setShowDirsFirst) Q_PROPERTY(bool showDotAndDotDot READ showDotAndDotDot WRITE setShowDotAndDotDot) Q_PROPERTY(bool showOnlyReadable READ showOnlyReadable WRITE setShowOnlyReadable) - Q_PROPERTY(int count READ count) + Q_PROPERTY(int count READ count NOTIFY rowCountChanged) //![class props] //![abslistmodel] @@ -83,10 +83,20 @@ public: QDeclarativeFolderListModel(QObject *parent = 0); ~QDeclarativeFolderListModel(); - enum Roles { FileNameRole = Qt::UserRole+1, FilePathRole = Qt::UserRole+2 }; - - int rowCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; + enum Roles { + FileNameRole = Qt::UserRole + 1, + FilePathRole = Qt::UserRole + 2, + FileBaseNameRole = Qt::UserRole + 3, + FileSuffixRole = Qt::UserRole + 4, + FileSizeRole = Qt::UserRole + 5, + FileLastModifiedRole = Qt::UserRole + 6, + FileLastReadRole = Qt::UserRole +7, + FileIsDirRole = Qt::UserRole + 8 + }; + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; //![abslistmodel] //![count] @@ -96,6 +106,8 @@ public: //![prop funcs] QUrl folder() const; void setFolder(const QUrl &folder); + QUrl rootFolder() const; + void setRootFolder(const QUrl &path); QUrl parentFolder() const; @@ -111,49 +123,47 @@ public: void setSortReversed(bool rev); bool showDirs() const; - void setShowDirs(bool); + void setShowDirs(bool showDirs); + bool showDirsFirst() const; + void setShowDirsFirst(bool showDirsFirst); bool showDotAndDotDot() const; - void setShowDotAndDotDot(bool); + void setShowDotAndDotDot(bool on); bool showOnlyReadable() const; - void setShowOnlyReadable(bool); + void setShowOnlyReadable(bool on); //![prop funcs] -//![isfolder] Q_INVOKABLE bool isFolder(int index) const; -//![isfolder] + Q_INVOKABLE QVariant get(int idx, const QString &property) const; //![parserstatus] virtual void classBegin(); virtual void componentComplete(); //![parserstatus] + int roleFromString(const QString &roleName) const; + //![notifier] Q_SIGNALS: void folderChanged(); + void rowCountChanged() const; //![notifier] //![class end] -private Q_SLOTS: - void refresh(); - void resetFiltering(); - void inserted(const QModelIndex &index, int start, int end); - void removed(const QModelIndex &index, int start, int end); - void handleDataChanged(const QModelIndex &start, const QModelIndex &end); + private: Q_DISABLE_COPY(QDeclarativeFolderListModel) - QDeclarativeFolderListModelPrivate *d; + Q_DECLARE_PRIVATE(QDeclarativeFolderListModel) + QScopedPointer d_ptr; + + Q_PRIVATE_SLOT(d_func(), void _q_directoryChanged(const QString &directory, const QList &list)) + Q_PRIVATE_SLOT(d_func(), void _q_directoryUpdated(const QString &directory, const QList &list, int fromIndex, int toIndex)) + Q_PRIVATE_SLOT(d_func(), void _q_sortFinished(const QList &list)) }; //![class end] QT_END_NAMESPACE -//![qml decl] -QML_DECLARE_TYPE(QDeclarativeFolderListModel) -//![qml decl] - QT_END_HEADER -#endif // QT_NO_DIRMODEL - #endif // QDECLARATIVEFOLDERLISTMODEL_H diff --git a/tests/auto/declarative/qdeclarativefolderlistmodel/tst_qdeclarativefolderlistmodel.cpp b/tests/auto/declarative/qdeclarativefolderlistmodel/tst_qdeclarativefolderlistmodel.cpp index 07eb6e87e1..708f3c90bb 100644 --- a/tests/auto/declarative/qdeclarativefolderlistmodel/tst_qdeclarativefolderlistmodel.cpp +++ b/tests/auto/declarative/qdeclarativefolderlistmodel/tst_qdeclarativefolderlistmodel.cpp @@ -170,8 +170,8 @@ void tst_qdeclarativefolderlistmodel::refresh() flm->setProperty("sortReversed", true); - QCOMPARE(removeStart, 0); - QCOMPARE(removeEnd, count-1); + QTRY_COMPARE(removeStart, 0); + QTRY_COMPARE(removeEnd, count-1); // wait for refresh } QTEST_MAIN(tst_qdeclarativefolderlistmodel) -- cgit v1.2.3