src/gui/dialogs/qfileinfogatherer.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qfileinfogatherer_p.h"
       
    43 #include <qdebug.h>
       
    44 #include <qfsfileengine.h>
       
    45 #include <qdiriterator.h>
       
    46 #ifndef Q_OS_WIN
       
    47 #  include <unistd.h>
       
    48 #  include <sys/types.h>
       
    49 #endif
       
    50 #if defined(Q_OS_VXWORKS)
       
    51 #  include "qplatformdefs.h"
       
    52 #endif
       
    53 
       
    54 QT_BEGIN_NAMESPACE
       
    55 
       
    56 #ifndef QT_NO_FILESYSTEMMODEL
       
    57 
       
    58 bool QFileInfoGatherer::fetchedRoot = false;
       
    59 
       
    60 /*!
       
    61     Creates thread
       
    62 */
       
    63 QFileInfoGatherer::QFileInfoGatherer(QObject *parent)
       
    64     : QThread(parent), abort(false),
       
    65 #ifndef QT_NO_FILESYSTEMWATCHER
       
    66       watcher(0),
       
    67 #endif
       
    68       m_resolveSymlinks(false), m_iconProvider(&defaultProvider)
       
    69 {
       
    70 #ifndef Q_OS_WIN
       
    71     userId = getuid();
       
    72     groupId = getgid();
       
    73 #else
       
    74     m_resolveSymlinks = true;
       
    75 #endif
       
    76 #ifndef QT_NO_FILESYSTEMWATCHER
       
    77     watcher = new QFileSystemWatcher(this);
       
    78     connect(watcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(list(const QString &)));
       
    79     connect(watcher, SIGNAL(fileChanged(const QString &)), this, SLOT(updateFile(const QString &)));
       
    80 #endif
       
    81     start(LowPriority);
       
    82 }
       
    83 
       
    84 /*!
       
    85     Destroys thread
       
    86 */
       
    87 QFileInfoGatherer::~QFileInfoGatherer()
       
    88 {
       
    89     QMutexLocker locker(&mutex);
       
    90     abort = true;
       
    91     condition.wakeOne();
       
    92     locker.unlock();
       
    93     wait();
       
    94 }
       
    95 
       
    96 void QFileInfoGatherer::setResolveSymlinks(bool enable)
       
    97 {
       
    98     Q_UNUSED(enable);
       
    99 #ifdef Q_OS_WIN
       
   100     QMutexLocker locker(&mutex);
       
   101     m_resolveSymlinks = enable;
       
   102 #endif
       
   103 }
       
   104 
       
   105 bool QFileInfoGatherer::resolveSymlinks() const
       
   106 {
       
   107     return m_resolveSymlinks;
       
   108 }
       
   109 
       
   110 void QFileInfoGatherer::setIconProvider(QFileIconProvider *provider)
       
   111 {
       
   112     QMutexLocker locker(&mutex);
       
   113     m_iconProvider = provider;
       
   114 }
       
   115 
       
   116 QFileIconProvider *QFileInfoGatherer::iconProvider() const
       
   117 {
       
   118     return m_iconProvider;
       
   119 }
       
   120 
       
   121 /*!
       
   122     Fetch extended information for all \a files in \a path
       
   123 
       
   124     \sa updateFile(), update(), resolvedName()
       
   125 */
       
   126 void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStringList &files)
       
   127 {
       
   128     QMutexLocker locker(&mutex);
       
   129     // See if we already have this dir/file in our que
       
   130     int loc = this->path.lastIndexOf(path);
       
   131     while (loc > 0)  {
       
   132         if (this->files.at(loc) == files) {
       
   133             return;
       
   134         }
       
   135         loc = this->path.lastIndexOf(path, loc - 1);
       
   136     }
       
   137     this->path.push(path);
       
   138     this->files.push(files);
       
   139     condition.wakeAll();
       
   140 }
       
   141 
       
   142 /*!
       
   143     Fetch extended information for all \a filePath
       
   144 
       
   145     \sa fetchExtendedInformation()
       
   146 */
       
   147 void QFileInfoGatherer::updateFile(const QString &filePath)
       
   148 {
       
   149     QString dir = filePath.mid(0, filePath.lastIndexOf(QDir::separator()));
       
   150     QString fileName = filePath.mid(dir.length() + 1);
       
   151     fetchExtendedInformation(dir, QStringList(fileName));
       
   152 }
       
   153 
       
   154 /*
       
   155     List all files in \a directoryPath
       
   156 
       
   157     \sa listed()
       
   158 */
       
   159 void QFileInfoGatherer::clear()
       
   160 {
       
   161 #ifndef QT_NO_FILESYSTEMWATCHER
       
   162     QMutexLocker locker(&mutex);
       
   163     watcher->removePaths(watcher->files());
       
   164     watcher->removePaths(watcher->directories());
       
   165 #endif
       
   166 }
       
   167 
       
   168 /*
       
   169     Remove a \a path from the watcher
       
   170 
       
   171     \sa listed()
       
   172 */
       
   173 void QFileInfoGatherer::removePath(const QString &path)
       
   174 {
       
   175 #ifndef QT_NO_FILESYSTEMWATCHER
       
   176     QMutexLocker locker(&mutex);
       
   177     watcher->removePath(path);
       
   178 #endif
       
   179 }
       
   180 
       
   181 /*
       
   182     List all files in \a directoryPath
       
   183 
       
   184     \sa listed()
       
   185 */
       
   186 void QFileInfoGatherer::list(const QString &directoryPath)
       
   187 {
       
   188     fetchExtendedInformation(directoryPath, QStringList());
       
   189 }
       
   190 
       
   191 /*
       
   192     Until aborted wait to fetch a directory or files
       
   193 */
       
   194 void QFileInfoGatherer::run()
       
   195 {
       
   196     forever {
       
   197         bool updateFiles = false;
       
   198         QMutexLocker locker(&mutex);
       
   199         if (abort) {
       
   200             return;
       
   201         }
       
   202         if (this->path.isEmpty())
       
   203             condition.wait(&mutex);
       
   204         QString path;
       
   205         QStringList list;
       
   206         if (!this->path.isEmpty()) {
       
   207             path = this->path.first();
       
   208             list = this->files.first();
       
   209             this->path.pop_front();
       
   210             this->files.pop_front();
       
   211             updateFiles = true;
       
   212         }
       
   213         locker.unlock();
       
   214         if (updateFiles)
       
   215             getFileInfos(path, list);
       
   216     }
       
   217 }
       
   218 
       
   219 /*
       
   220     QFileInfo::permissions is different depending upon your platform.
       
   221 
       
   222     "normalize this" so they can mean the same to us.
       
   223 */
       
   224 QFile::Permissions QFileInfoGatherer::translatePermissions(const QFileInfo &fileInfo) const {
       
   225     QFile::Permissions permissions = fileInfo.permissions();
       
   226 #ifdef Q_OS_WIN
       
   227     return permissions;
       
   228 #else
       
   229     QFile::Permissions p = permissions;
       
   230     p &= ~(QFile::ReadUser|QFile::WriteUser|QFile::ExeUser);
       
   231     if (                                     permissions & QFile::ReadOther
       
   232         || (fileInfo.ownerId() == userId  && permissions & QFile::ReadOwner)
       
   233         || (fileInfo.groupId() == groupId && permissions & QFile::ReadGroup))
       
   234         p |= QFile::ReadUser;
       
   235 
       
   236     if (                                     permissions & QFile::WriteOther
       
   237         || (fileInfo.ownerId() == userId  && permissions & QFile::WriteOwner)
       
   238         || (fileInfo.groupId() == groupId && permissions & QFile::WriteGroup))
       
   239         p |= QFile::WriteUser;
       
   240 
       
   241     if (                                     permissions & QFile::ExeOther
       
   242         || (fileInfo.ownerId() == userId  && permissions & QFile::ExeOwner)
       
   243         || (fileInfo.groupId() == groupId && permissions & QFile::ExeGroup))
       
   244         p |= QFile::ExeUser;
       
   245     return p;
       
   246 #endif
       
   247 }
       
   248 
       
   249 QExtendedInformation QFileInfoGatherer::getInfo(const QFileInfo &fileInfo) const
       
   250 {
       
   251     QExtendedInformation info(fileInfo);
       
   252     info.icon = m_iconProvider->icon(fileInfo);
       
   253     info.setPermissions(translatePermissions(fileInfo));
       
   254     info.displayType = m_iconProvider->type(fileInfo);
       
   255 #ifndef QT_NO_FILESYSTEMWATCHER
       
   256     // ### Not ready to listen all modifications
       
   257     #if 0
       
   258         // Enable the next two commented out lines to get updates when the file sizes change...
       
   259         if (!fileInfo.exists() && !fileInfo.isSymLink()) {
       
   260             info.size = -1;
       
   261             //watcher->removePath(fileInfo.absoluteFilePath());
       
   262         } else {
       
   263             if (!fileInfo.absoluteFilePath().isEmpty() && fileInfo.exists() && fileInfo.isReadable()
       
   264                 && !watcher->files().contains(fileInfo.absoluteFilePath())) {
       
   265                 //watcher->addPath(fileInfo.absoluteFilePath());
       
   266             }
       
   267         }
       
   268     #endif
       
   269 #endif
       
   270 
       
   271     if (fileInfo.isSymLink() && m_resolveSymlinks) {
       
   272         QFileInfo resolvedInfo(fileInfo.symLinkTarget());
       
   273         resolvedInfo = resolvedInfo.canonicalFilePath();
       
   274         if (resolvedInfo.exists()) {
       
   275             emit nameResolved(fileInfo.filePath(), resolvedInfo.fileName());
       
   276         }
       
   277     }
       
   278     return info;
       
   279 }
       
   280 
       
   281 QString QFileInfoGatherer::translateDriveName(const QFileInfo &drive) const
       
   282 {
       
   283     QString driveName = drive.absoluteFilePath();
       
   284 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
       
   285     if (driveName.startsWith(QLatin1Char('/'))) // UNC host
       
   286         return drive.fileName();
       
   287 #endif
       
   288 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN)
       
   289     if (driveName.endsWith(QLatin1Char('/')))
       
   290         driveName.chop(1);
       
   291 #endif
       
   292     return driveName;
       
   293 }
       
   294 
       
   295 /*
       
   296     Get specific file info's, batch the files so update when we have 100
       
   297     items and every 200ms after that
       
   298  */
       
   299 void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &files)
       
   300 {
       
   301 #ifndef QT_NO_FILESYSTEMWATCHER
       
   302     if (files.isEmpty()
       
   303         && !watcher->directories().contains(path)
       
   304         && !path.isEmpty()
       
   305         && !path.startsWith(QLatin1String("//")) /*don't watch UNC path*/) {
       
   306         watcher->addPath(path);
       
   307     }
       
   308 #endif
       
   309 
       
   310     // List drives
       
   311     if (path.isEmpty()) {
       
   312 #if defined Q_AUTOTEST_EXPORT
       
   313         fetchedRoot = true;
       
   314 #endif
       
   315         QFileInfoList infoList;
       
   316         if (files.isEmpty()) {
       
   317             infoList = QDir::drives();
       
   318         } else {
       
   319             for (int i = 0; i < files.count(); ++i)
       
   320                 infoList << QFileInfo(files.at(i));
       
   321         }
       
   322         for (int i = infoList.count() - 1; i >= 0; --i) {
       
   323             QString driveName = translateDriveName(infoList.at(i));
       
   324             QList<QPair<QString,QFileInfo> > updatedFiles;
       
   325             updatedFiles.append(QPair<QString,QFileInfo>(driveName, infoList.at(i)));
       
   326             emit updates(path, updatedFiles);
       
   327         }
       
   328         return;
       
   329     }
       
   330 
       
   331     QTime base = QTime::currentTime();
       
   332     QFileInfo fileInfo;
       
   333     bool firstTime = true;
       
   334     QList<QPair<QString, QFileInfo> > updatedFiles;
       
   335     QStringList filesToCheck = files;
       
   336 
       
   337     QString itPath = QDir::fromNativeSeparators(files.isEmpty() ? path : QLatin1String(""));
       
   338     QDirIterator dirIt(itPath, QDir::AllEntries | QDir::System | QDir::Hidden);
       
   339     QStringList allFiles;
       
   340     while(!abort && dirIt.hasNext()) {
       
   341         dirIt.next();
       
   342         fileInfo = dirIt.fileInfo();
       
   343         allFiles.append(fileInfo.fileName());
       
   344 	fetch(fileInfo, base, firstTime, updatedFiles, path);
       
   345     }
       
   346     if (!allFiles.isEmpty())
       
   347         emit newListOfFiles(path, allFiles);
       
   348 
       
   349     QStringList::const_iterator filesIt = filesToCheck.constBegin();
       
   350     while(!abort && filesIt != filesToCheck.constEnd()) {
       
   351         fileInfo.setFile(path + QDir::separator() + *filesIt);
       
   352         ++filesIt;
       
   353         fetch(fileInfo, base, firstTime, updatedFiles, path);
       
   354     }
       
   355     if (!updatedFiles.isEmpty())
       
   356         emit updates(path, updatedFiles);
       
   357 }
       
   358 
       
   359 void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QTime &base, bool &firstTime, QList<QPair<QString, QFileInfo> > &updatedFiles, const QString &path) {
       
   360     updatedFiles.append(QPair<QString, QFileInfo>(fileInfo.fileName(), fileInfo));
       
   361     QTime current = QTime::currentTime();
       
   362     if ((firstTime && updatedFiles.count() > 100) || base.msecsTo(current) > 1000) {
       
   363         emit updates(path, updatedFiles);
       
   364         updatedFiles.clear();
       
   365         base = current;
       
   366         firstTime = false;
       
   367     }
       
   368 }
       
   369 
       
   370 #endif // QT_NO_FILESYSTEMMODEL
       
   371 
       
   372 QT_END_NAMESPACE