/****************************************************************************+ −
**+ −
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).+ −
** All rights reserved.+ −
** Contact: Nokia Corporation (qt-info@nokia.com)+ −
**+ −
** This file is part of the QtGui module of the Qt Toolkit.+ −
**+ −
** $QT_BEGIN_LICENSE:LGPL$+ −
** No Commercial Usage+ −
** This file contains pre-release code and may not be distributed.+ −
** You may use this file in accordance with the terms and conditions+ −
** contained in the Technology Preview License Agreement accompanying+ −
** this package.+ −
**+ −
** GNU Lesser General Public License Usage+ −
** Alternatively, 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.+ −
**+ −
** If you have questions regarding the use of this file, please contact+ −
** Nokia at qt-info@nokia.com.+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
** $QT_END_LICENSE$+ −
**+ −
****************************************************************************/+ −
+ −
#include "qfileinfogatherer_p.h"+ −
#include <qdebug.h>+ −
#include <qfsfileengine.h>+ −
#include <qdiriterator.h>+ −
#ifndef Q_OS_WIN+ −
# include <unistd.h>+ −
# include <sys/types.h>+ −
#endif+ −
#if defined(Q_OS_VXWORKS)+ −
# include "qplatformdefs.h"+ −
#endif+ −
+ −
QT_BEGIN_NAMESPACE+ −
+ −
#ifndef QT_NO_FILESYSTEMMODEL+ −
+ −
#ifdef QT_BUILD_INTERNAL+ −
static bool fetchedRoot = false;+ −
Q_AUTOTEST_EXPORT void qt_test_resetFetchedRoot()+ −
{+ −
fetchedRoot = false;+ −
}+ −
+ −
Q_AUTOTEST_EXPORT bool qt_test_isFetchedRoot()+ −
{+ −
return fetchedRoot;+ −
}+ −
#endif+ −
+ −
/*!+ −
Creates thread+ −
*/+ −
QFileInfoGatherer::QFileInfoGatherer(QObject *parent)+ −
: QThread(parent), abort(false),+ −
#ifndef QT_NO_FILESYSTEMWATCHER+ −
watcher(0),+ −
#endif+ −
m_resolveSymlinks(false), m_iconProvider(&defaultProvider)+ −
{+ −
#ifndef Q_OS_WIN+ −
userId = getuid();+ −
groupId = getgid();+ −
#else+ −
m_resolveSymlinks = true;+ −
#endif+ −
#ifndef QT_NO_FILESYSTEMWATCHER+ −
watcher = new QFileSystemWatcher(this);+ −
connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(list(QString)));+ −
connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(updateFile(QString)));+ −
#endif+ −
start(LowPriority);+ −
}+ −
+ −
/*!+ −
Destroys thread+ −
*/+ −
QFileInfoGatherer::~QFileInfoGatherer()+ −
{+ −
QMutexLocker locker(&mutex);+ −
abort = true;+ −
condition.wakeOne();+ −
locker.unlock();+ −
wait();+ −
}+ −
+ −
void QFileInfoGatherer::setResolveSymlinks(bool enable)+ −
{+ −
Q_UNUSED(enable);+ −
#ifdef Q_OS_WIN+ −
QMutexLocker locker(&mutex);+ −
m_resolveSymlinks = enable;+ −
#endif+ −
}+ −
+ −
bool QFileInfoGatherer::resolveSymlinks() const+ −
{+ −
return m_resolveSymlinks;+ −
}+ −
+ −
void QFileInfoGatherer::setIconProvider(QFileIconProvider *provider)+ −
{+ −
QMutexLocker locker(&mutex);+ −
m_iconProvider = provider;+ −
}+ −
+ −
QFileIconProvider *QFileInfoGatherer::iconProvider() const+ −
{+ −
return m_iconProvider;+ −
}+ −
+ −
/*!+ −
Fetch extended information for all \a files in \a path+ −
+ −
\sa updateFile(), update(), resolvedName()+ −
*/+ −
void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStringList &files)+ −
{+ −
QMutexLocker locker(&mutex);+ −
// See if we already have this dir/file in our que+ −
int loc = this->path.lastIndexOf(path);+ −
while (loc > 0) {+ −
if (this->files.at(loc) == files) {+ −
return;+ −
}+ −
loc = this->path.lastIndexOf(path, loc - 1);+ −
}+ −
this->path.push(path);+ −
this->files.push(files);+ −
condition.wakeAll();+ −
}+ −
+ −
/*!+ −
Fetch extended information for all \a filePath+ −
+ −
\sa fetchExtendedInformation()+ −
*/+ −
void QFileInfoGatherer::updateFile(const QString &filePath)+ −
{+ −
QString dir = filePath.mid(0, filePath.lastIndexOf(QDir::separator()));+ −
QString fileName = filePath.mid(dir.length() + 1);+ −
fetchExtendedInformation(dir, QStringList(fileName));+ −
}+ −
+ −
/*+ −
List all files in \a directoryPath+ −
+ −
\sa listed()+ −
*/+ −
void QFileInfoGatherer::clear()+ −
{+ −
#ifndef QT_NO_FILESYSTEMWATCHER+ −
QMutexLocker locker(&mutex);+ −
watcher->removePaths(watcher->files());+ −
watcher->removePaths(watcher->directories());+ −
#endif+ −
}+ −
+ −
/*+ −
Remove a \a path from the watcher+ −
+ −
\sa listed()+ −
*/+ −
void QFileInfoGatherer::removePath(const QString &path)+ −
{+ −
#ifndef QT_NO_FILESYSTEMWATCHER+ −
QMutexLocker locker(&mutex);+ −
watcher->removePath(path);+ −
#endif+ −
}+ −
+ −
/*+ −
List all files in \a directoryPath+ −
+ −
\sa listed()+ −
*/+ −
void QFileInfoGatherer::list(const QString &directoryPath)+ −
{+ −
fetchExtendedInformation(directoryPath, QStringList());+ −
}+ −
+ −
/*+ −
Until aborted wait to fetch a directory or files+ −
*/+ −
void QFileInfoGatherer::run()+ −
{+ −
forever {+ −
bool updateFiles = false;+ −
QMutexLocker locker(&mutex);+ −
if (abort) {+ −
return;+ −
}+ −
if (this->path.isEmpty())+ −
condition.wait(&mutex);+ −
QString path;+ −
QStringList list;+ −
if (!this->path.isEmpty()) {+ −
path = this->path.first();+ −
list = this->files.first();+ −
this->path.pop_front();+ −
this->files.pop_front();+ −
updateFiles = true;+ −
}+ −
locker.unlock();+ −
if (updateFiles)+ −
getFileInfos(path, list);+ −
}+ −
}+ −
+ −
QExtendedInformation QFileInfoGatherer::getInfo(const QFileInfo &fileInfo) const+ −
{+ −
QExtendedInformation info(fileInfo);+ −
info.icon = m_iconProvider->icon(fileInfo);+ −
info.displayType = m_iconProvider->type(fileInfo);+ −
#ifndef QT_NO_FILESYSTEMWATCHER+ −
// ### Not ready to listen all modifications+ −
#if 0+ −
// Enable the next two commented out lines to get updates when the file sizes change...+ −
if (!fileInfo.exists() && !fileInfo.isSymLink()) {+ −
info.size = -1;+ −
//watcher->removePath(fileInfo.absoluteFilePath());+ −
} else {+ −
if (!fileInfo.absoluteFilePath().isEmpty() && fileInfo.exists() && fileInfo.isReadable()+ −
&& !watcher->files().contains(fileInfo.absoluteFilePath())) {+ −
//watcher->addPath(fileInfo.absoluteFilePath());+ −
}+ −
}+ −
#endif+ −
#endif+ −
+ −
if (fileInfo.isSymLink() && m_resolveSymlinks) {+ −
QFileInfo resolvedInfo(fileInfo.symLinkTarget());+ −
resolvedInfo = resolvedInfo.canonicalFilePath();+ −
if (resolvedInfo.exists()) {+ −
emit nameResolved(fileInfo.filePath(), resolvedInfo.fileName());+ −
}+ −
}+ −
return info;+ −
}+ −
+ −
QString QFileInfoGatherer::translateDriveName(const QFileInfo &drive) const+ −
{+ −
QString driveName = drive.absoluteFilePath();+ −
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)+ −
if (driveName.startsWith(QLatin1Char('/'))) // UNC host+ −
return drive.fileName();+ −
#endif+ −
#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN)+ −
if (driveName.endsWith(QLatin1Char('/')))+ −
driveName.chop(1);+ −
#endif+ −
return driveName;+ −
}+ −
+ −
/*+ −
Get specific file info's, batch the files so update when we have 100+ −
items and every 200ms after that+ −
*/+ −
void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &files)+ −
{+ −
#ifndef QT_NO_FILESYSTEMWATCHER+ −
if (files.isEmpty()+ −
&& !watcher->directories().contains(path)+ −
&& !path.isEmpty()+ −
&& !path.startsWith(QLatin1String("//")) /*don't watch UNC path*/) {+ −
watcher->addPath(path);+ −
}+ −
#endif+ −
+ −
// List drives+ −
if (path.isEmpty()) {+ −
#ifdef QT_BUILD_INTERNAL+ −
fetchedRoot = true;+ −
#endif+ −
QFileInfoList infoList;+ −
if (files.isEmpty()) {+ −
infoList = QDir::drives();+ −
} else {+ −
for (int i = 0; i < files.count(); ++i)+ −
infoList << QFileInfo(files.at(i));+ −
}+ −
for (int i = infoList.count() - 1; i >= 0; --i) {+ −
QString driveName = translateDriveName(infoList.at(i));+ −
QList<QPair<QString,QFileInfo> > updatedFiles;+ −
updatedFiles.append(QPair<QString,QFileInfo>(driveName, infoList.at(i)));+ −
emit updates(path, updatedFiles);+ −
}+ −
return;+ −
}+ −
+ −
QElapsedTimer base;+ −
base.start();+ −
QFileInfo fileInfo;+ −
bool firstTime = true;+ −
QList<QPair<QString, QFileInfo> > updatedFiles;+ −
QStringList filesToCheck = files;+ −
+ −
QString itPath = QDir::fromNativeSeparators(files.isEmpty() ? path : QLatin1String(""));+ −
QDirIterator dirIt(itPath, QDir::AllEntries | QDir::System | QDir::Hidden);+ −
QStringList allFiles;+ −
while(!abort && dirIt.hasNext()) {+ −
dirIt.next();+ −
fileInfo = dirIt.fileInfo();+ −
allFiles.append(fileInfo.fileName());+ −
fetch(fileInfo, base, firstTime, updatedFiles, path);+ −
}+ −
if (!allFiles.isEmpty())+ −
emit newListOfFiles(path, allFiles);+ −
+ −
QStringList::const_iterator filesIt = filesToCheck.constBegin();+ −
while(!abort && filesIt != filesToCheck.constEnd()) {+ −
fileInfo.setFile(path + QDir::separator() + *filesIt);+ −
++filesIt;+ −
fetch(fileInfo, base, firstTime, updatedFiles, path);+ −
}+ −
if (!updatedFiles.isEmpty())+ −
emit updates(path, updatedFiles);+ −
emit directoryLoaded(path);+ −
}+ −
+ −
void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QElapsedTimer &base, bool &firstTime, QList<QPair<QString, QFileInfo> > &updatedFiles, const QString &path) {+ −
updatedFiles.append(QPair<QString, QFileInfo>(fileInfo.fileName(), fileInfo));+ −
QElapsedTimer current;+ −
current.start();+ −
if ((firstTime && updatedFiles.count() > 100) || base.msecsTo(current) > 1000) {+ −
emit updates(path, updatedFiles);+ −
updatedFiles.clear();+ −
base = current;+ −
firstTime = false;+ −
}+ −
}+ −
+ −
#endif // QT_NO_FILESYSTEMMODEL+ −
+ −
QT_END_NAMESPACE+ −