/******************************************************************************** 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"#endifQT_BEGIN_NAMESPACE#ifndef QT_NO_FILESYSTEMMODEL#ifdef QT_BUILD_INTERNALstatic 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_FILESYSTEMMODELQT_END_NAMESPACE