source: trunk/src/gui/dialogs/qfileinfogatherer.cpp@ 613

Last change on this file since 613 was 613, checked in by Dmitry A. Kuminov, 15 years ago

corelib/gui: Add support for watching drive list changes (represented by the empty or null string) to the polling file system watcher and use this feature in QFileDialog to get instant drive change notifications in "My Computer".

File size: 11.3 KB
Line 
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** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
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
54QT_BEGIN_NAMESPACE
55
56#ifndef QT_NO_FILESYSTEMMODEL
57
58bool QFileInfoGatherer::fetchedRoot = false;
59
60/*!
61 Creates thread
62*/
63QFileInfoGatherer::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(QString)), this, SLOT(list(QString)));
79 connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(updateFile(QString)));
80#endif
81 start(LowPriority);
82}
83
84/*!
85 Destroys thread
86*/
87QFileInfoGatherer::~QFileInfoGatherer()
88{
89 QMutexLocker locker(&mutex);
90 abort = true;
91 condition.wakeOne();
92 locker.unlock();
93 wait();
94}
95
96void 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
105bool QFileInfoGatherer::resolveSymlinks() const
106{
107 return m_resolveSymlinks;
108}
109
110void QFileInfoGatherer::setIconProvider(QFileIconProvider *provider)
111{
112 QMutexLocker locker(&mutex);
113 m_iconProvider = provider;
114}
115
116QFileIconProvider *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*/
126void 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*/
147void 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*/
159void 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*/
173void 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*/
186void QFileInfoGatherer::list(const QString &directoryPath)
187{
188 fetchExtendedInformation(directoryPath, QStringList());
189}
190
191/*
192 Until aborted wait to fetch a directory or files
193*/
194void 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*/
224QFile::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
249QExtendedInformation 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
281QString QFileInfoGatherer::translateDriveName(const QFileInfo &drive) const
282{
283 QString driveName = drive.absoluteFilePath();
284#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_OS2)
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) || defined(Q_OS_OS2)
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 */
299void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &files)
300{
301#ifndef QT_NO_FILESYSTEMWATCHER
302 if (files.isEmpty()
303 && !watcher->directories().contains(path)
304#if !defined(Q_OS_OS2)
305 && !path.isEmpty()
306#endif
307 && !path.startsWith(QLatin1String("//")) /*don't watch UNC path*/) {
308 watcher->addPath(path);
309 }
310#endif
311
312 // List drives
313 if (path.isEmpty()) {
314#if defined Q_AUTOTEST_EXPORT
315 fetchedRoot = true;
316#endif
317 QFileInfoList infoList;
318 if (files.isEmpty()) {
319 infoList = QDir::drives();
320 } else {
321 for (int i = 0; i < files.count(); ++i)
322 infoList << QFileInfo(files.at(i));
323 }
324 for (int i = infoList.count() - 1; i >= 0; --i) {
325 QString driveName = translateDriveName(infoList.at(i));
326 QList<QPair<QString,QFileInfo> > updatedFiles;
327 updatedFiles.append(QPair<QString,QFileInfo>(driveName, infoList.at(i)));
328 emit updates(path, updatedFiles);
329 }
330 return;
331 }
332
333 QTime base = QTime::currentTime();
334 QFileInfo fileInfo;
335 bool firstTime = true;
336 QList<QPair<QString, QFileInfo> > updatedFiles;
337 QStringList filesToCheck = files;
338
339 QString itPath = QDir::fromNativeSeparators(files.isEmpty() ? path : QLatin1String(""));
340 QDirIterator dirIt(itPath, QDir::AllEntries | QDir::System | QDir::Hidden);
341 QStringList allFiles;
342 while(!abort && dirIt.hasNext()) {
343 dirIt.next();
344 fileInfo = dirIt.fileInfo();
345 allFiles.append(fileInfo.fileName());
346 fetch(fileInfo, base, firstTime, updatedFiles, path);
347 }
348 if (!allFiles.isEmpty())
349 emit newListOfFiles(path, allFiles);
350
351 QStringList::const_iterator filesIt = filesToCheck.constBegin();
352 while(!abort && filesIt != filesToCheck.constEnd()) {
353 fileInfo.setFile(path + QDir::separator() + *filesIt);
354 ++filesIt;
355 fetch(fileInfo, base, firstTime, updatedFiles, path);
356 }
357 if (!updatedFiles.isEmpty())
358 emit updates(path, updatedFiles);
359}
360
361void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QTime &base, bool &firstTime, QList<QPair<QString, QFileInfo> > &updatedFiles, const QString &path) {
362 updatedFiles.append(QPair<QString, QFileInfo>(fileInfo.fileName(), fileInfo));
363 QTime current = QTime::currentTime();
364 if ((firstTime && updatedFiles.count() > 100) || base.msecsTo(current) > 1000) {
365 emit updates(path, updatedFiles);
366 updatedFiles.clear();
367 base = current;
368 firstTime = false;
369 }
370}
371
372#endif // QT_NO_FILESYSTEMMODEL
373
374QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.