author | Eckhart Koeppen <eckhart.koppen@nokia.com> |
Wed, 21 Apr 2010 20:15:53 +0300 | |
branch | RCL_3 |
changeset 13 | c0432d11811c |
parent 4 | 3b1da2848fc7 |
permissions | -rw-r--r-- |
0 | 1 |
/**************************************************************************** |
2 |
** |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
3
diff
changeset
|
3 |
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
0 | 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 "qfilesystemmodel_p.h" |
|
43 |
#include "qfilesystemmodel.h" |
|
44 |
#include <qlocale.h> |
|
45 |
#include <qmime.h> |
|
46 |
#include <qurl.h> |
|
47 |
#include <qdebug.h> |
|
48 |
#include <qmessagebox.h> |
|
49 |
#include <qapplication.h> |
|
50 |
||
51 |
#ifdef Q_OS_WIN |
|
52 |
#include <qt_windows.h> |
|
53 |
#endif |
|
54 |
||
55 |
QT_BEGIN_NAMESPACE |
|
56 |
||
57 |
#ifndef QT_NO_FILESYSTEMMODEL |
|
58 |
||
59 |
/*! |
|
60 |
\enum QFileSystemModel::Roles |
|
61 |
\value FileIconRole |
|
62 |
\value FilePathRole |
|
63 |
\value FileNameRole |
|
64 |
\value FilePermissions |
|
65 |
*/ |
|
66 |
||
67 |
/*! |
|
68 |
\class QFileSystemModel |
|
69 |
\since 4.4 |
|
70 |
||
71 |
\brief The QFileSystemModel class provides a data model for the local filesystem. |
|
72 |
||
73 |
\ingroup model-view |
|
74 |
||
75 |
This class provides access to the local filesystem, providing functions |
|
76 |
for renaming and removing files and directories, and for creating new |
|
77 |
directories. In the simplest case, it can be used with a suitable display |
|
78 |
widget as part of a browser or filter. |
|
79 |
||
80 |
QFileSystemModel will not fetch any files or directories until setRootPath |
|
81 |
is called. This will prevent any unnecessary querying on the file system |
|
82 |
until that point such as listing the drives on Windows. |
|
83 |
||
84 |
Unlike the QDirModel, QFileSystemModel uses a separate thread to populate |
|
85 |
itself so it will not cause the main thread to hang as the file system |
|
86 |
is being queried. Calls to rowCount() will return 0 until the model |
|
87 |
populates a directory. |
|
88 |
||
89 |
QFileSystemModel keeps a cache with file information. The cache is |
|
90 |
automatically kept up to date using the QFileSystemWatcher. |
|
91 |
||
92 |
QFileSystemModel can be accessed using the standard interface provided by |
|
93 |
QAbstractItemModel, but it also provides some convenience functions that are |
|
94 |
specific to a directory model. |
|
95 |
The fileInfo(), isDir(), name(), and path() functions provide information |
|
96 |
about the underlying files and directories related to items in the model. |
|
97 |
Directories can be created and removed using mkdir(), rmdir(). |
|
98 |
||
99 |
\note QFileSystemModel requires an instance of a GUI application. |
|
100 |
||
101 |
\sa {Model Classes} |
|
102 |
*/ |
|
103 |
||
104 |
/*! |
|
105 |
\fn bool QFileSystemModel::rmdir(const QModelIndex &index) const |
|
106 |
||
107 |
Removes the directory corresponding to the model item \a index in the |
|
108 |
file system model and \bold{deletes the corresponding directory from the |
|
109 |
file system}, returning true if successful. If the directory cannot be |
|
110 |
removed, false is returned. |
|
111 |
||
112 |
\warning This function deletes directories from the file system; it does |
|
113 |
\bold{not} move them to a location where they can be recovered. |
|
114 |
||
115 |
\sa remove() |
|
116 |
*/ |
|
117 |
||
118 |
/*! |
|
119 |
\fn QIcon QFileSystemModel::fileName(const QModelIndex &index) const |
|
120 |
||
121 |
Returns the file name for the item stored in the model under the given |
|
122 |
\a index. |
|
123 |
*/ |
|
124 |
||
125 |
/*! |
|
126 |
\fn QIcon QFileSystemModel::fileIcon(const QModelIndex &index) const |
|
127 |
||
128 |
Returns the icon for the item stored in the model under the given |
|
129 |
\a index. |
|
130 |
*/ |
|
131 |
||
132 |
/*! |
|
133 |
\fn QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const |
|
134 |
||
135 |
Returns the QFileInfo for the item stored in the model under the given |
|
136 |
\a index. |
|
137 |
*/ |
|
138 |
||
139 |
/*! |
|
140 |
\fn void QFileSystemModel::rootPathChanged(const QString &newPath); |
|
141 |
||
142 |
This signal is emitted whenever the root path has been changed to a \a newPath. |
|
143 |
*/ |
|
144 |
||
145 |
/*! |
|
146 |
\fn void QFileSystemModel::fileRenamed(const QString &path, const QString &oldName, const QString &newName) |
|
147 |
||
148 |
This signal is emitted whenever a file with the \a oldName is successfully |
|
149 |
renamed to \a newName. The file is located in in the directory \a path. |
|
150 |
*/ |
|
151 |
||
152 |
/*! |
|
153 |
\fn bool QFileSystemModel::remove(const QModelIndex &index) const |
|
154 |
||
155 |
Removes the model item \a index from the file system model and \bold{deletes the |
|
156 |
corresponding file from the file system}, returning true if successful. If the |
|
157 |
item cannot be removed, false is returned. |
|
158 |
||
159 |
\warning This function deletes files from the file system; it does \bold{not} |
|
160 |
move them to a location where they can be recovered. |
|
161 |
||
162 |
\sa rmdir() |
|
163 |
*/ |
|
164 |
||
165 |
bool QFileSystemModel::remove(const QModelIndex &aindex) const |
|
166 |
{ |
|
167 |
//### TODO optim |
|
168 |
QString path = filePath(aindex); |
|
169 |
QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func()); |
|
170 |
d->fileInfoGatherer.removePath(path); |
|
171 |
QDirIterator it(path, |
|
172 |
QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot, |
|
173 |
QDirIterator::Subdirectories); |
|
174 |
QStringList children; |
|
175 |
while (it.hasNext()) |
|
176 |
children.prepend(it.next()); |
|
177 |
children.append(path); |
|
178 |
||
179 |
bool error = false; |
|
180 |
for (int i = 0; i < children.count(); ++i) { |
|
181 |
QFileInfo info(children.at(i)); |
|
182 |
QModelIndex modelIndex = index(children.at(i)); |
|
183 |
if (info.isDir()) { |
|
184 |
QDir dir; |
|
185 |
if (children.at(i) != path) |
|
186 |
error |= remove(modelIndex); |
|
187 |
error |= rmdir(modelIndex); |
|
188 |
} else { |
|
189 |
error |= QFile::remove(filePath(modelIndex)); |
|
190 |
} |
|
191 |
} |
|
192 |
return error; |
|
193 |
} |
|
194 |
||
195 |
/*! |
|
196 |
Constructs a file system model with the given \a parent. |
|
197 |
*/ |
|
198 |
QFileSystemModel::QFileSystemModel(QObject *parent) |
|
199 |
: QAbstractItemModel(*new QFileSystemModelPrivate, parent) |
|
200 |
{ |
|
201 |
Q_D(QFileSystemModel); |
|
202 |
d->init(); |
|
203 |
} |
|
204 |
||
205 |
/*! |
|
206 |
\internal |
|
207 |
*/ |
|
208 |
QFileSystemModel::QFileSystemModel(QFileSystemModelPrivate &dd, QObject *parent) |
|
209 |
: QAbstractItemModel(dd, parent) |
|
210 |
{ |
|
211 |
Q_D(QFileSystemModel); |
|
212 |
d->init(); |
|
213 |
} |
|
214 |
||
215 |
/*! |
|
216 |
Destroys this file system model. |
|
217 |
*/ |
|
218 |
QFileSystemModel::~QFileSystemModel() |
|
219 |
{ |
|
220 |
} |
|
221 |
||
222 |
/*! |
|
223 |
\reimp |
|
224 |
*/ |
|
225 |
QModelIndex QFileSystemModel::index(int row, int column, const QModelIndex &parent) const |
|
226 |
{ |
|
227 |
Q_D(const QFileSystemModel); |
|
228 |
if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent)) |
|
229 |
return QModelIndex(); |
|
230 |
||
231 |
// get the parent node |
|
232 |
QFileSystemModelPrivate::QFileSystemNode *parentNode = (d->indexValid(parent) ? d->node(parent) : |
|
233 |
const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&d->root)); |
|
234 |
Q_ASSERT(parentNode); |
|
235 |
||
236 |
// now get the internal pointer for the index |
|
237 |
QString childName = parentNode->visibleChildren[d->translateVisibleLocation(parentNode, row)]; |
|
238 |
const QFileSystemModelPrivate::QFileSystemNode *indexNode = parentNode->children.value(childName); |
|
239 |
Q_ASSERT(indexNode); |
|
240 |
||
241 |
return createIndex(row, column, const_cast<QFileSystemModelPrivate::QFileSystemNode*>(indexNode)); |
|
242 |
} |
|
243 |
||
244 |
/*! |
|
245 |
\overload |
|
246 |
||
247 |
Returns the model item index for the given \a path and \a column. |
|
248 |
*/ |
|
249 |
QModelIndex QFileSystemModel::index(const QString &path, int column) const |
|
250 |
{ |
|
251 |
Q_D(const QFileSystemModel); |
|
252 |
QFileSystemModelPrivate::QFileSystemNode *node = d->node(path, false); |
|
253 |
QModelIndex idx = d->index(node); |
|
254 |
if (idx.column() != column) |
|
255 |
idx = idx.sibling(idx.row(), column); |
|
256 |
return idx; |
|
257 |
} |
|
258 |
||
259 |
/*! |
|
260 |
\internal |
|
261 |
||
262 |
Return the QFileSystemNode that goes to index. |
|
263 |
*/ |
|
264 |
QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QModelIndex &index) const |
|
265 |
{ |
|
266 |
if (!index.isValid()) |
|
267 |
return const_cast<QFileSystemNode*>(&root); |
|
268 |
QFileSystemModelPrivate::QFileSystemNode *indexNode = static_cast<QFileSystemModelPrivate::QFileSystemNode*>(index.internalPointer()); |
|
269 |
Q_ASSERT(indexNode); |
|
270 |
return indexNode; |
|
271 |
} |
|
272 |
||
273 |
#ifdef Q_OS_WIN |
|
274 |
static QString qt_GetLongPathName(const QString &strShortPath) |
|
275 |
{ |
|
276 |
QString longPath; |
|
277 |
int i = 0; |
|
278 |
if (strShortPath == QLatin1String(".") |
|
279 |
|| (strShortPath.startsWith(QLatin1String("//"))) |
|
280 |
|| (strShortPath.startsWith(QLatin1String("\\\\")))) // unc |
|
281 |
return strShortPath; |
|
282 |
QString::const_iterator it = strShortPath.constBegin(); |
|
283 |
QString::const_iterator constEnd = strShortPath.constEnd(); |
|
284 |
do { |
|
285 |
bool isSep = (*it == QLatin1Char('\\') || *it == QLatin1Char('/')); |
|
286 |
if (isSep || it == constEnd) { |
|
287 |
QString section = (it == constEnd ? strShortPath : strShortPath.left(i)); |
|
288 |
// FindFirstFile does not handle volumes ("C:"), so we have to catch that ourselves. |
|
289 |
if (section.endsWith(QLatin1Char(':'))) { |
|
290 |
longPath.append(section.toUpper()); |
|
291 |
} else { |
|
292 |
HANDLE h; |
|
293 |
#ifndef Q_OS_WINCE |
|
294 |
//We add the extend length prefix to handle long path |
|
295 |
QString longSection = QLatin1String("\\\\?\\")+QDir::toNativeSeparators(section); |
|
296 |
#else |
|
297 |
QString longSection = QDir::toNativeSeparators(section); |
|
298 |
#endif |
|
299 |
WIN32_FIND_DATA findData; |
|
300 |
h = ::FindFirstFile((wchar_t*)longSection.utf16(), &findData); |
|
301 |
if (h != INVALID_HANDLE_VALUE) { |
|
302 |
longPath.append(QString::fromWCharArray(findData.cFileName)); |
|
303 |
::FindClose(h); |
|
304 |
} else { |
|
305 |
longPath.append(section); |
|
306 |
break; |
|
307 |
} |
|
308 |
} |
|
309 |
if (it != constEnd) |
|
310 |
longPath.append(*it); |
|
311 |
else |
|
312 |
break; |
|
313 |
} |
|
314 |
++it; |
|
315 |
if (isSep && it == constEnd) // break out if the last character is a separator |
|
316 |
break; |
|
317 |
++i; |
|
318 |
} while (true); |
|
319 |
return longPath; |
|
320 |
} |
|
321 |
#endif |
|
322 |
||
323 |
/*! |
|
324 |
\internal |
|
325 |
||
326 |
Given a path return the matching QFileSystemNode or &root if invalid |
|
327 |
*/ |
|
328 |
QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QString &path, bool fetch) const |
|
329 |
{ |
|
330 |
Q_Q(const QFileSystemModel); |
|
331 |
Q_UNUSED(q); |
|
332 |
if (path.isEmpty() || path == myComputer() || path.startsWith(QLatin1Char(':'))) |
|
333 |
return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); |
|
334 |
||
335 |
// Construct the nodes up to the new root path if they need to be built |
|
336 |
QString absolutePath; |
|
337 |
#ifdef Q_OS_WIN |
|
338 |
QString longPath = qt_GetLongPathName(path); |
|
339 |
#else |
|
340 |
QString longPath = path; |
|
341 |
#endif |
|
342 |
if (longPath == rootDir.path()) |
|
343 |
absolutePath = rootDir.absolutePath(); |
|
344 |
else |
|
345 |
absolutePath = QDir(longPath).absolutePath(); |
|
346 |
||
347 |
// ### TODO can we use bool QAbstractFileEngine::caseSensitive() const? |
|
348 |
QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts); |
|
349 |
if ((pathElements.isEmpty()) |
|
350 |
#if (!defined(Q_OS_WIN) || defined(Q_OS_WINCE)) && !defined(Q_OS_SYMBIAN) |
|
351 |
&& QDir::fromNativeSeparators(longPath) != QLatin1String("/") |
|
352 |
#endif |
|
353 |
) |
|
354 |
return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); |
|
355 |
QModelIndex index = QModelIndex(); // start with "My Computer" |
|
356 |
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) |
|
357 |
if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path |
|
358 |
QString host = QLatin1String("\\\\") + pathElements.first(); |
|
359 |
if (absolutePath == QDir::fromNativeSeparators(host)) |
|
360 |
absolutePath.append(QLatin1Char('/')); |
|
361 |
if (longPath.endsWith(QLatin1Char('/')) && !absolutePath.endsWith(QLatin1Char('/'))) |
|
362 |
absolutePath.append(QLatin1Char('/')); |
|
363 |
int r = 0; |
|
364 |
QFileSystemModelPrivate::QFileSystemNode *rootNode = const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); |
|
365 |
if (!root.children.contains(host.toLower())) { |
|
366 |
if (pathElements.count() == 1 && !absolutePath.endsWith(QLatin1Char('/'))) |
|
367 |
return rootNode; |
|
368 |
QFileInfo info(host); |
|
369 |
if (!info.exists()) |
|
370 |
return rootNode; |
|
371 |
QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this); |
|
372 |
p->addNode(rootNode, host,info); |
|
373 |
p->addVisibleFiles(rootNode, QStringList(host)); |
|
374 |
} |
|
375 |
r = rootNode->visibleLocation(host); |
|
376 |
r = translateVisibleLocation(rootNode, r); |
|
377 |
index = q->index(r, 0, QModelIndex()); |
|
378 |
pathElements.pop_front(); |
|
379 |
} else |
|
380 |
#endif |
|
381 |
||
382 |
#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) |
|
383 |
{ |
|
384 |
if (!pathElements.at(0).contains(QLatin1String(":"))) { |
|
385 |
// The reason we express it like this instead of with anonymous, temporary |
|
386 |
// variables, is to workaround a compiler crash with Q_CC_NOKIAX86. |
|
387 |
QString rootPath = QDir(longPath).rootPath(); |
|
388 |
pathElements.prepend(rootPath); |
|
389 |
} |
|
390 |
if (pathElements.at(0).endsWith(QLatin1Char('/'))) |
|
391 |
pathElements[0].chop(1); |
|
392 |
} |
|
393 |
#else |
|
394 |
// add the "/" item, since it is a valid path element on Unix |
|
395 |
if (absolutePath[0] == QLatin1Char('/')) |
|
396 |
pathElements.prepend(QLatin1String("/")); |
|
397 |
#endif |
|
398 |
||
399 |
QFileSystemModelPrivate::QFileSystemNode *parent = node(index); |
|
400 |
||
401 |
for (int i = 0; i < pathElements.count(); ++i) { |
|
402 |
QString element = pathElements.at(i); |
|
403 |
#ifdef Q_OS_WIN |
|
404 |
// On Windows, "filename......." and "filename" are equivalent Task #133928 |
|
405 |
while (element.endsWith(QLatin1Char('.'))) |
|
406 |
element.chop(1); |
|
407 |
#endif |
|
408 |
bool alreadyExisted = parent->children.contains(element); |
|
409 |
||
410 |
// we couldn't find the path element, we create a new node since we |
|
411 |
// _know_ that the path is valid |
|
412 |
if (alreadyExisted) { |
|
413 |
if ((parent->children.count() == 0) |
|
414 |
|| (parent->caseSensitive() |
|
415 |
&& parent->children.value(element)->fileName != element) |
|
416 |
|| (!parent->caseSensitive() |
|
417 |
&& parent->children.value(element)->fileName.toLower() != element.toLower())) |
|
418 |
alreadyExisted = false; |
|
419 |
} |
|
420 |
||
421 |
QFileSystemModelPrivate::QFileSystemNode *node; |
|
422 |
if (!alreadyExisted) { |
|
423 |
// Someone might call ::index("file://cookie/monster/doesn't/like/veggies"), |
|
424 |
// a path that doesn't exists, I.E. don't blindly create directories. |
|
425 |
QFileInfo info(absolutePath); |
|
426 |
if (!info.exists()) |
|
427 |
return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); |
|
428 |
QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this); |
|
429 |
node = p->addNode(parent, element,info); |
|
430 |
#ifndef QT_NO_FILESYSTEMWATCHER |
|
431 |
node->populate(fileInfoGatherer.getInfo(info)); |
|
432 |
#endif |
|
433 |
} else { |
|
434 |
node = parent->children.value(element); |
|
435 |
} |
|
436 |
||
437 |
Q_ASSERT(node); |
|
438 |
if (!node->isVisible) { |
|
439 |
// It has been filtered out |
|
440 |
if (alreadyExisted && node->hasInformation() && !fetch) |
|
441 |
return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); |
|
442 |
||
443 |
QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this); |
|
444 |
p->addVisibleFiles(parent, QStringList(element)); |
|
445 |
if (!p->bypassFilters.contains(node)) |
|
446 |
p->bypassFilters[node] = 1; |
|
447 |
QString dir = q->filePath(this->index(parent)); |
|
448 |
if (!node->hasInformation() && fetch) { |
|
449 |
Fetching f; |
|
450 |
f.dir = dir; |
|
451 |
f.file = element; |
|
452 |
f.node = node; |
|
453 |
p->toFetch.append(f); |
|
454 |
p->fetchingTimer.start(0, const_cast<QFileSystemModel*>(q)); |
|
455 |
} |
|
456 |
} |
|
457 |
parent = node; |
|
458 |
} |
|
459 |
||
460 |
return parent; |
|
461 |
} |
|
462 |
||
463 |
/*! |
|
464 |
\reimp |
|
465 |
*/ |
|
466 |
void QFileSystemModel::timerEvent(QTimerEvent *event) |
|
467 |
{ |
|
468 |
Q_D(QFileSystemModel); |
|
469 |
if (event->timerId() == d->fetchingTimer.timerId()) { |
|
470 |
d->fetchingTimer.stop(); |
|
471 |
#ifndef QT_NO_FILESYSTEMWATCHER |
|
472 |
for (int i = 0; i < d->toFetch.count(); ++i) { |
|
473 |
const QFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node; |
|
474 |
if (!node->hasInformation()) { |
|
475 |
d->fileInfoGatherer.fetchExtendedInformation(d->toFetch.at(i).dir, |
|
476 |
QStringList(d->toFetch.at(i).file)); |
|
477 |
} else { |
|
478 |
// qDebug() << "yah!, you saved a little gerbil soul"; |
|
479 |
} |
|
480 |
} |
|
481 |
#endif |
|
482 |
d->toFetch.clear(); |
|
483 |
} |
|
484 |
} |
|
485 |
||
486 |
/*! |
|
487 |
Returns true if the model item \a index represents a directory; |
|
488 |
otherwise returns false. |
|
489 |
*/ |
|
490 |
bool QFileSystemModel::isDir(const QModelIndex &index) const |
|
491 |
{ |
|
492 |
// This function is for public usage only because it could create a file info |
|
493 |
Q_D(const QFileSystemModel); |
|
494 |
if (!index.isValid()) |
|
495 |
return true; |
|
496 |
QFileSystemModelPrivate::QFileSystemNode *n = d->node(index); |
|
497 |
if (n->hasInformation()) |
|
498 |
return n->isDir(); |
|
499 |
return fileInfo(index).isDir(); |
|
500 |
} |
|
501 |
||
502 |
/*! |
|
503 |
Returns the size in bytes of \a index. If the file does not exist, 0 is returned. |
|
504 |
*/ |
|
505 |
qint64 QFileSystemModel::size(const QModelIndex &index) const |
|
506 |
{ |
|
507 |
Q_D(const QFileSystemModel); |
|
508 |
if (!index.isValid()) |
|
509 |
return 0; |
|
510 |
return d->node(index)->size(); |
|
511 |
} |
|
512 |
||
513 |
/*! |
|
514 |
Returns the type of file \a index such as "Directory" or "JPEG file". |
|
515 |
*/ |
|
516 |
QString QFileSystemModel::type(const QModelIndex &index) const |
|
517 |
{ |
|
518 |
Q_D(const QFileSystemModel); |
|
519 |
if (!index.isValid()) |
|
520 |
return QString(); |
|
521 |
return d->node(index)->type(); |
|
522 |
} |
|
523 |
||
524 |
/*! |
|
525 |
Returns the date and time when \a index was last modified. |
|
526 |
*/ |
|
527 |
QDateTime QFileSystemModel::lastModified(const QModelIndex &index) const |
|
528 |
{ |
|
529 |
Q_D(const QFileSystemModel); |
|
530 |
if (!index.isValid()) |
|
531 |
return QDateTime(); |
|
532 |
return d->node(index)->lastModified(); |
|
533 |
} |
|
534 |
||
535 |
/*! |
|
536 |
\reimp |
|
537 |
*/ |
|
538 |
QModelIndex QFileSystemModel::parent(const QModelIndex &index) const |
|
539 |
{ |
|
540 |
Q_D(const QFileSystemModel); |
|
541 |
if (!d->indexValid(index)) |
|
542 |
return QModelIndex(); |
|
543 |
||
544 |
QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index); |
|
545 |
Q_ASSERT(indexNode != 0); |
|
546 |
QFileSystemModelPrivate::QFileSystemNode *parentNode = (indexNode ? indexNode->parent : 0); |
|
547 |
if (parentNode == 0 || parentNode == &d->root) |
|
548 |
return QModelIndex(); |
|
549 |
||
550 |
// get the parent's row |
|
551 |
QFileSystemModelPrivate::QFileSystemNode *grandParentNode = parentNode->parent; |
|
552 |
Q_ASSERT(grandParentNode->children.contains(parentNode->fileName)); |
|
553 |
int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName)); |
|
554 |
if (visualRow == -1) |
|
555 |
return QModelIndex(); |
|
556 |
return createIndex(visualRow, 0, parentNode); |
|
557 |
} |
|
558 |
||
559 |
/* |
|
560 |
\internal |
|
561 |
||
562 |
return the index for node |
|
563 |
*/ |
|
564 |
QModelIndex QFileSystemModelPrivate::index(const QFileSystemModelPrivate::QFileSystemNode *node) const |
|
565 |
{ |
|
566 |
Q_Q(const QFileSystemModel); |
|
567 |
QFileSystemModelPrivate::QFileSystemNode *parentNode = (node ? node->parent : 0); |
|
568 |
if (node == &root || !parentNode) |
|
569 |
return QModelIndex(); |
|
570 |
||
571 |
// get the parent's row |
|
572 |
Q_ASSERT(node); |
|
573 |
if (!node->isVisible) |
|
574 |
return QModelIndex(); |
|
575 |
||
576 |
int visualRow = translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName)); |
|
577 |
return q->createIndex(visualRow, 0, const_cast<QFileSystemNode*>(node)); |
|
578 |
} |
|
579 |
||
580 |
/*! |
|
581 |
\reimp |
|
582 |
*/ |
|
583 |
bool QFileSystemModel::hasChildren(const QModelIndex &parent) const |
|
584 |
{ |
|
585 |
Q_D(const QFileSystemModel); |
|
586 |
if (parent.column() > 0) |
|
587 |
return false; |
|
588 |
||
589 |
if (!parent.isValid()) // drives |
|
590 |
return true; |
|
591 |
||
592 |
const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent); |
|
593 |
Q_ASSERT(indexNode); |
|
594 |
return (indexNode->isDir()); |
|
595 |
} |
|
596 |
||
597 |
/*! |
|
598 |
\reimp |
|
599 |
*/ |
|
600 |
bool QFileSystemModel::canFetchMore(const QModelIndex &parent) const |
|
601 |
{ |
|
602 |
Q_D(const QFileSystemModel); |
|
603 |
const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent); |
|
604 |
return (!indexNode->populatedChildren); |
|
605 |
} |
|
606 |
||
607 |
/*! |
|
608 |
\reimp |
|
609 |
*/ |
|
610 |
void QFileSystemModel::fetchMore(const QModelIndex &parent) |
|
611 |
{ |
|
612 |
Q_D(QFileSystemModel); |
|
613 |
if (!d->setRootPath) |
|
614 |
return; |
|
615 |
QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent); |
|
616 |
if (indexNode->populatedChildren) |
|
617 |
return; |
|
618 |
indexNode->populatedChildren = true; |
|
619 |
d->fileInfoGatherer.list(filePath(parent)); |
|
620 |
} |
|
621 |
||
622 |
/*! |
|
623 |
\reimp |
|
624 |
*/ |
|
625 |
int QFileSystemModel::rowCount(const QModelIndex &parent) const |
|
626 |
{ |
|
627 |
Q_D(const QFileSystemModel); |
|
628 |
if (parent.column() > 0) |
|
629 |
return 0; |
|
630 |
||
631 |
if (!parent.isValid()) |
|
632 |
return d->root.visibleChildren.count(); |
|
633 |
||
634 |
const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent); |
|
635 |
return parentNode->visibleChildren.count(); |
|
636 |
} |
|
637 |
||
638 |
/*! |
|
639 |
\reimp |
|
640 |
*/ |
|
641 |
int QFileSystemModel::columnCount(const QModelIndex &parent) const |
|
642 |
{ |
|
643 |
return (parent.column() > 0) ? 0 : 4; |
|
644 |
} |
|
645 |
||
646 |
/*! |
|
647 |
Returns the data stored under the given \a role for the item "My Computer". |
|
648 |
||
649 |
\sa Qt::ItemDataRole |
|
650 |
*/ |
|
651 |
QVariant QFileSystemModel::myComputer(int role) const |
|
652 |
{ |
|
653 |
Q_D(const QFileSystemModel); |
|
654 |
switch (role) { |
|
655 |
case Qt::DisplayRole: |
|
656 |
return d->myComputer(); |
|
657 |
case Qt::DecorationRole: |
|
658 |
return d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Computer); |
|
659 |
} |
|
660 |
return QVariant(); |
|
661 |
} |
|
662 |
||
663 |
/*! |
|
664 |
\reimp |
|
665 |
*/ |
|
666 |
QVariant QFileSystemModel::data(const QModelIndex &index, int role) const |
|
667 |
{ |
|
668 |
Q_D(const QFileSystemModel); |
|
669 |
if (!index.isValid() || index.model() != this) |
|
670 |
return QVariant(); |
|
671 |
||
672 |
switch (role) { |
|
673 |
case Qt::EditRole: |
|
674 |
case Qt::DisplayRole: |
|
675 |
switch (index.column()) { |
|
676 |
case 0: return d->name(index); |
|
677 |
case 1: return d->size(index); |
|
678 |
case 2: return d->type(index); |
|
679 |
case 3: return d->time(index); |
|
680 |
default: |
|
681 |
qWarning("data: invalid display value column %d", index.column()); |
|
682 |
break; |
|
683 |
} |
|
684 |
break; |
|
685 |
case FilePathRole: |
|
686 |
return filePath(index); |
|
687 |
case FileNameRole: |
|
688 |
return d->name(index); |
|
689 |
case Qt::DecorationRole: |
|
690 |
if (index.column() == 0) { |
|
691 |
QIcon icon = d->icon(index); |
|
692 |
if (icon.isNull()) { |
|
693 |
if (d->node(index)->isDir()) |
|
694 |
icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Folder); |
|
695 |
else |
|
696 |
icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::File); |
|
697 |
} |
|
698 |
return icon; |
|
699 |
} |
|
700 |
break; |
|
701 |
case Qt::TextAlignmentRole: |
|
702 |
if (index.column() == 1) |
|
703 |
return Qt::AlignRight; |
|
704 |
break; |
|
705 |
case FilePermissions: |
|
706 |
int p = permissions(index); |
|
707 |
return p; |
|
708 |
} |
|
709 |
||
710 |
return QVariant(); |
|
711 |
} |
|
712 |
||
713 |
/*! |
|
714 |
\internal |
|
715 |
*/ |
|
716 |
QString QFileSystemModelPrivate::size(const QModelIndex &index) const |
|
717 |
{ |
|
718 |
if (!index.isValid()) |
|
719 |
return QString(); |
|
720 |
const QFileSystemNode *n = node(index); |
|
721 |
if (n->isDir()) { |
|
722 |
#ifdef Q_OS_MAC |
|
723 |
return QLatin1String("--"); |
|
724 |
#else |
|
725 |
return QLatin1String(""); |
|
726 |
#endif |
|
727 |
// Windows - "" |
|
728 |
// OS X - "--" |
|
729 |
// Konqueror - "4 KB" |
|
730 |
// Nautilus - "9 items" (the number of children) |
|
731 |
} |
|
732 |
return size(n->size()); |
|
733 |
} |
|
734 |
||
735 |
QString QFileSystemModelPrivate::size(qint64 bytes) |
|
736 |
{ |
|
737 |
// According to the Si standard KB is 1000 bytes, KiB is 1024 |
|
738 |
// but on windows sizes are calculated by dividing by 1024 so we do what they do. |
|
739 |
const qint64 kb = 1024; |
|
740 |
const qint64 mb = 1024 * kb; |
|
741 |
const qint64 gb = 1024 * mb; |
|
742 |
const qint64 tb = 1024 * gb; |
|
743 |
if (bytes >= tb) |
|
744 |
return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3)); |
|
745 |
if (bytes >= gb) |
|
746 |
return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2)); |
|
747 |
if (bytes >= mb) |
|
748 |
return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1)); |
|
749 |
if (bytes >= kb) |
|
750 |
return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb)); |
|
751 |
return QFileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes)); |
|
752 |
} |
|
753 |
||
754 |
/*! |
|
755 |
\internal |
|
756 |
*/ |
|
757 |
QString QFileSystemModelPrivate::time(const QModelIndex &index) const |
|
758 |
{ |
|
759 |
if (!index.isValid()) |
|
760 |
return QString(); |
|
761 |
#ifndef QT_NO_DATESTRING |
|
762 |
return node(index)->lastModified().toString(Qt::SystemLocaleDate); |
|
763 |
#else |
|
764 |
Q_UNUSED(index); |
|
765 |
return QString(); |
|
766 |
#endif |
|
767 |
} |
|
768 |
||
769 |
/* |
|
770 |
\internal |
|
771 |
*/ |
|
772 |
QString QFileSystemModelPrivate::type(const QModelIndex &index) const |
|
773 |
{ |
|
774 |
if (!index.isValid()) |
|
775 |
return QString(); |
|
776 |
return node(index)->type(); |
|
777 |
} |
|
778 |
||
779 |
/*! |
|
780 |
\internal |
|
781 |
*/ |
|
782 |
QString QFileSystemModelPrivate::name(const QModelIndex &index) const |
|
783 |
{ |
|
784 |
if (!index.isValid()) |
|
785 |
return QString(); |
|
786 |
QFileSystemNode *dirNode = node(index); |
|
787 |
if (dirNode->isSymLink() && fileInfoGatherer.resolveSymlinks()) { |
|
788 |
QString fullPath = QDir::fromNativeSeparators(filePath(index)); |
|
789 |
if (resolvedSymLinks.contains(fullPath)) |
|
790 |
return resolvedSymLinks[fullPath]; |
|
791 |
} |
|
792 |
// ### TODO it would be nice to grab the volume name if dirNode->parent == root |
|
793 |
return dirNode->fileName; |
|
794 |
} |
|
795 |
||
796 |
/*! |
|
797 |
\internal |
|
798 |
*/ |
|
799 |
QIcon QFileSystemModelPrivate::icon(const QModelIndex &index) const |
|
800 |
{ |
|
801 |
if (!index.isValid()) |
|
802 |
return QIcon(); |
|
803 |
return node(index)->icon(); |
|
804 |
} |
|
805 |
||
806 |
/*! |
|
807 |
\reimp |
|
808 |
*/ |
|
809 |
bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role) |
|
810 |
{ |
|
811 |
Q_D(QFileSystemModel); |
|
812 |
if (!idx.isValid() |
|
813 |
|| idx.column() != 0 |
|
814 |
|| role != Qt::EditRole |
|
815 |
|| (flags(idx) & Qt::ItemIsEditable) == 0) { |
|
816 |
return false; |
|
817 |
} |
|
818 |
||
819 |
QString newName = value.toString(); |
|
820 |
QString oldName = idx.data().toString(); |
|
821 |
if (newName == idx.data().toString()) |
|
822 |
return true; |
|
823 |
||
824 |
if (newName.isEmpty() |
|
825 |
|| newName.contains(QDir::separator()) |
|
826 |
|| !QDir(filePath(parent(idx))).rename(oldName, newName)) { |
|
827 |
#ifndef QT_NO_MESSAGEBOX |
|
828 |
QMessageBox::information(0, QFileSystemModel::tr("Invalid filename"), |
|
829 |
QFileSystemModel::tr("<b>The name \"%1\" can not be used.</b><p>Try using another name, with fewer characters or no punctuations marks.") |
|
830 |
.arg(newName), |
|
831 |
QMessageBox::Ok); |
|
832 |
#endif // QT_NO_MESSAGEBOX |
|
833 |
return false; |
|
834 |
} else { |
|
835 |
/* |
|
836 |
*After re-naming something we don't want the selection to change* |
|
837 |
- can't remove rows and later insert |
|
838 |
- can't quickly remove and insert |
|
839 |
- index pointer can't change because treeview doesn't use persistant index's |
|
840 |
||
841 |
- if this get any more complicated think of changing it to just |
|
842 |
use layoutChanged |
|
843 |
*/ |
|
844 |
||
845 |
QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx); |
|
846 |
QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent; |
|
847 |
int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName); |
|
848 |
||
849 |
d->addNode(parentNode, newName,indexNode->info->fileInfo()); |
|
850 |
parentNode->visibleChildren.removeAt(visibleLocation); |
|
851 |
QFileSystemModelPrivate::QFileSystemNode * oldValue = parentNode->children.value(oldName); |
|
852 |
parentNode->children[newName] = oldValue; |
|
853 |
QFileInfo info(d->rootDir, newName); |
|
854 |
oldValue->fileName = newName; |
|
855 |
oldValue->parent = parentNode; |
|
856 |
oldValue->populate(d->fileInfoGatherer.getInfo(info)); |
|
857 |
oldValue->isVisible = true; |
|
858 |
||
859 |
parentNode->children.remove(oldName); |
|
860 |
parentNode->visibleChildren.insert(visibleLocation, newName); |
|
861 |
||
862 |
d->delayedSort(); |
|
863 |
emit fileRenamed(filePath(idx.parent()), oldName, newName); |
|
864 |
} |
|
865 |
return true; |
|
866 |
} |
|
867 |
||
868 |
/*! |
|
869 |
\reimp |
|
870 |
*/ |
|
871 |
QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const |
|
872 |
{ |
|
873 |
switch (role) { |
|
874 |
case Qt::DecorationRole: |
|
875 |
if (section == 0) { |
|
876 |
// ### TODO oh man this is ugly and doesn't even work all the way! |
|
877 |
// it is still 2 pixels off |
|
878 |
QImage pixmap(16, 1, QImage::Format_Mono); |
|
879 |
pixmap.fill(0); |
|
880 |
pixmap.setAlphaChannel(pixmap.createAlphaMask()); |
|
881 |
return pixmap; |
|
882 |
} |
|
883 |
break; |
|
884 |
case Qt::TextAlignmentRole: |
|
885 |
return Qt::AlignLeft; |
|
886 |
} |
|
887 |
||
888 |
if (orientation != Qt::Horizontal || role != Qt::DisplayRole) |
|
889 |
return QAbstractItemModel::headerData(section, orientation, role); |
|
890 |
||
891 |
QString returnValue; |
|
892 |
switch (section) { |
|
893 |
case 0: returnValue = tr("Name"); |
|
894 |
break; |
|
895 |
case 1: returnValue = tr("Size"); |
|
896 |
break; |
|
897 |
case 2: returnValue = |
|
898 |
#ifdef Q_OS_MAC |
|
899 |
tr("Kind", "Match OS X Finder"); |
|
900 |
#else |
|
901 |
tr("Type", "All other platforms"); |
|
902 |
#endif |
|
903 |
break; |
|
904 |
// Windows - Type |
|
905 |
// OS X - Kind |
|
906 |
// Konqueror - File Type |
|
907 |
// Nautilus - Type |
|
908 |
case 3: returnValue = tr("Date Modified"); |
|
909 |
break; |
|
910 |
default: return QVariant(); |
|
911 |
} |
|
912 |
return returnValue; |
|
913 |
} |
|
914 |
||
915 |
/*! |
|
916 |
\reimp |
|
917 |
*/ |
|
918 |
Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const |
|
919 |
{ |
|
920 |
Q_D(const QFileSystemModel); |
|
921 |
Qt::ItemFlags flags = QAbstractItemModel::flags(index); |
|
922 |
if (!index.isValid()) |
|
923 |
return flags; |
|
924 |
||
925 |
QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index); |
|
926 |
if (d->nameFilterDisables && !d->passNameFilters(indexNode)) { |
|
927 |
flags &= ~Qt::ItemIsEnabled; |
|
928 |
// ### TODO you shouldn't be able to set this as the current item, task 119433 |
|
929 |
return flags; |
|
930 |
} |
|
931 |
||
932 |
flags |= Qt::ItemIsDragEnabled; |
|
933 |
if (d->readOnly) |
|
934 |
return flags; |
|
935 |
if ((index.column() == 0) && indexNode->permissions() & QFile::WriteUser) { |
|
936 |
flags |= Qt::ItemIsEditable; |
|
937 |
if (indexNode->isDir()) |
|
938 |
flags |= Qt::ItemIsDropEnabled; |
|
939 |
} |
|
940 |
return flags; |
|
941 |
} |
|
942 |
||
943 |
/*! |
|
944 |
\internal |
|
945 |
*/ |
|
946 |
void QFileSystemModelPrivate::_q_performDelayedSort() |
|
947 |
{ |
|
948 |
Q_Q(QFileSystemModel); |
|
949 |
q->sort(sortColumn, sortOrder); |
|
950 |
} |
|
951 |
||
952 |
static inline QChar getNextChar(const QString &s, int location) |
|
953 |
{ |
|
954 |
return (location < s.length()) ? s.at(location) : QChar(); |
|
955 |
} |
|
956 |
||
957 |
/*! |
|
958 |
Natural number sort, skips spaces. |
|
959 |
||
960 |
Examples: |
|
961 |
1, 2, 10, 55, 100 |
|
962 |
01.jpg, 2.jpg, 10.jpg |
|
963 |
||
964 |
Note on the algorithm: |
|
965 |
Only as many characters as necessary are looked at and at most they all |
|
966 |
are looked at once. |
|
967 |
||
968 |
Slower then QString::compare() (of course) |
|
969 |
*/ |
|
970 |
int QFileSystemModelPrivate::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs) |
|
971 |
{ |
|
972 |
for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) { |
|
973 |
// skip spaces, tabs and 0's |
|
974 |
QChar c1 = getNextChar(s1, l1); |
|
975 |
while (c1.isSpace()) |
|
976 |
c1 = getNextChar(s1, ++l1); |
|
977 |
QChar c2 = getNextChar(s2, l2); |
|
978 |
while (c2.isSpace()) |
|
979 |
c2 = getNextChar(s2, ++l2); |
|
980 |
||
981 |
if (c1.isDigit() && c2.isDigit()) { |
|
982 |
while (c1.digitValue() == 0) |
|
983 |
c1 = getNextChar(s1, ++l1); |
|
984 |
while (c2.digitValue() == 0) |
|
985 |
c2 = getNextChar(s2, ++l2); |
|
986 |
||
987 |
int lookAheadLocation1 = l1; |
|
988 |
int lookAheadLocation2 = l2; |
|
989 |
int currentReturnValue = 0; |
|
990 |
// find the last digit, setting currentReturnValue as we go if it isn't equal |
|
991 |
for ( |
|
992 |
QChar lookAhead1 = c1, lookAhead2 = c2; |
|
993 |
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); |
|
994 |
lookAhead1 = getNextChar(s1, ++lookAheadLocation1), |
|
995 |
lookAhead2 = getNextChar(s2, ++lookAheadLocation2) |
|
996 |
) { |
|
997 |
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit(); |
|
998 |
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit(); |
|
999 |
if (!is1ADigit && !is2ADigit) |
|
1000 |
break; |
|
1001 |
if (!is1ADigit) |
|
1002 |
return -1; |
|
1003 |
if (!is2ADigit) |
|
1004 |
return 1; |
|
1005 |
if (currentReturnValue == 0) { |
|
1006 |
if (lookAhead1 < lookAhead2) { |
|
1007 |
currentReturnValue = -1; |
|
1008 |
} else if (lookAhead1 > lookAhead2) { |
|
1009 |
currentReturnValue = 1; |
|
1010 |
} |
|
1011 |
} |
|
1012 |
} |
|
1013 |
if (currentReturnValue != 0) |
|
1014 |
return currentReturnValue; |
|
1015 |
} |
|
1016 |
||
1017 |
if (cs == Qt::CaseInsensitive) { |
|
1018 |
if (!c1.isLower()) c1 = c1.toLower(); |
|
1019 |
if (!c2.isLower()) c2 = c2.toLower(); |
|
1020 |
} |
|
1021 |
int r = QString::localeAwareCompare(c1, c2); |
|
1022 |
if (r < 0) |
|
1023 |
return -1; |
|
1024 |
if (r > 0) |
|
1025 |
return 1; |
|
1026 |
} |
|
1027 |
// The two strings are the same (02 == 2) so fall back to the normal sort |
|
1028 |
return QString::compare(s1, s2, cs); |
|
1029 |
} |
|
1030 |
||
1031 |
/* |
|
1032 |
\internal |
|
1033 |
Helper functor used by sort() |
|
1034 |
*/ |
|
1035 |
class QFileSystemModelSorter |
|
1036 |
{ |
|
1037 |
public: |
|
1038 |
inline QFileSystemModelSorter(int column) : sortColumn(column) {} |
|
1039 |
||
1040 |
bool compareNodes(const QFileSystemModelPrivate::QFileSystemNode *l, |
|
1041 |
const QFileSystemModelPrivate::QFileSystemNode *r) const |
|
1042 |
{ |
|
1043 |
switch (sortColumn) { |
|
1044 |
case 0: { |
|
1045 |
#ifndef Q_OS_MAC |
|
1046 |
// place directories before files |
|
1047 |
bool left = l->isDir(); |
|
1048 |
bool right = r->isDir(); |
|
1049 |
if (left ^ right) |
|
1050 |
return left; |
|
1051 |
#endif |
|
1052 |
return QFileSystemModelPrivate::naturalCompare(l->fileName, |
|
1053 |
r->fileName, Qt::CaseInsensitive) < 0; |
|
1054 |
} |
|
1055 |
case 1: |
|
1056 |
// Directories go first |
|
1057 |
if (l->isDir() && !r->isDir()) |
|
1058 |
return true; |
|
1059 |
return l->size() < r->size(); |
|
1060 |
case 2: |
|
1061 |
return l->type() < r->type(); |
|
1062 |
case 3: |
|
1063 |
return l->lastModified() < r->lastModified(); |
|
1064 |
} |
|
1065 |
Q_ASSERT(false); |
|
1066 |
return false; |
|
1067 |
} |
|
1068 |
||
1069 |
bool operator()(const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &l, |
|
1070 |
const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &r) const |
|
1071 |
{ |
|
1072 |
return compareNodes(l.first, r.first); |
|
1073 |
} |
|
1074 |
||
1075 |
||
1076 |
private: |
|
1077 |
int sortColumn; |
|
1078 |
}; |
|
1079 |
||
1080 |
/* |
|
1081 |
\internal |
|
1082 |
||
1083 |
Sort all of the children of parent |
|
1084 |
*/ |
|
1085 |
void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent) |
|
1086 |
{ |
|
1087 |
Q_Q(QFileSystemModel); |
|
1088 |
QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent); |
|
1089 |
if (indexNode->children.count() == 0) |
|
1090 |
return; |
|
1091 |
||
1092 |
QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > values; |
|
1093 |
QHash<QString, QFileSystemNode *>::const_iterator iterator; |
|
1094 |
int i = 0; |
|
1095 |
for(iterator = indexNode->children.begin() ; iterator != indexNode->children.end() ; ++iterator) { |
|
1096 |
if (filtersAcceptsNode(iterator.value())) { |
|
1097 |
values.append(QPair<QFileSystemModelPrivate::QFileSystemNode*, int>((iterator.value()), i)); |
|
1098 |
} else { |
|
1099 |
iterator.value()->isVisible = false; |
|
1100 |
} |
|
1101 |
i++; |
|
1102 |
} |
|
1103 |
QFileSystemModelSorter ms(column); |
|
1104 |
qStableSort(values.begin(), values.end(), ms); |
|
1105 |
// First update the new visible list |
|
1106 |
indexNode->visibleChildren.clear(); |
|
1107 |
//No more dirty item we reset our internal dirty index |
|
1108 |
indexNode->dirtyChildrenIndex = -1; |
|
1109 |
for (int i = 0; i < values.count(); ++i) { |
|
1110 |
indexNode->visibleChildren.append(values.at(i).first->fileName); |
|
1111 |
values.at(i).first->isVisible = true; |
|
1112 |
} |
|
1113 |
||
1114 |
if (!disableRecursiveSort) { |
|
1115 |
for (int i = 0; i < q->rowCount(parent); ++i) { |
|
1116 |
const QModelIndex childIndex = q->index(i, 0, parent); |
|
1117 |
QFileSystemModelPrivate::QFileSystemNode *indexNode = node(childIndex); |
|
1118 |
//Only do a recursive sort on visible nodes |
|
1119 |
if (indexNode->isVisible) |
|
1120 |
sortChildren(column, childIndex); |
|
1121 |
} |
|
1122 |
} |
|
1123 |
} |
|
1124 |
||
1125 |
/*! |
|
1126 |
\reimp |
|
1127 |
*/ |
|
1128 |
void QFileSystemModel::sort(int column, Qt::SortOrder order) |
|
1129 |
{ |
|
1130 |
Q_D(QFileSystemModel); |
|
1131 |
if (d->sortOrder == order && d->sortColumn == column && !d->forceSort) |
|
1132 |
return; |
|
1133 |
||
1134 |
emit layoutAboutToBeChanged(); |
|
1135 |
QModelIndexList oldList = persistentIndexList(); |
|
1136 |
QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > oldNodes; |
|
1137 |
for (int i = 0; i < oldList.count(); ++i) { |
|
1138 |
QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldList.at(i)), oldList.at(i).column()); |
|
1139 |
oldNodes.append(pair); |
|
1140 |
} |
|
1141 |
||
1142 |
if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) { |
|
1143 |
//we sort only from where we are, don't need to sort all the model |
|
1144 |
d->sortChildren(column, index(rootPath())); |
|
1145 |
d->sortColumn = column; |
|
1146 |
d->forceSort = false; |
|
1147 |
} |
|
1148 |
d->sortOrder = order; |
|
1149 |
||
1150 |
QModelIndexList newList; |
|
1151 |
for (int i = 0; i < oldNodes.count(); ++i) { |
|
1152 |
QModelIndex idx = d->index(oldNodes.at(i).first); |
|
1153 |
idx = idx.sibling(idx.row(), oldNodes.at(i).second); |
|
1154 |
newList.append(idx); |
|
1155 |
} |
|
1156 |
changePersistentIndexList(oldList, newList); |
|
1157 |
emit layoutChanged(); |
|
1158 |
} |
|
1159 |
||
1160 |
/*! |
|
1161 |
Returns a list of MIME types that can be used to describe a list of items |
|
1162 |
in the model. |
|
1163 |
*/ |
|
1164 |
QStringList QFileSystemModel::mimeTypes() const |
|
1165 |
{ |
|
1166 |
return QStringList(QLatin1String("text/uri-list")); |
|
1167 |
} |
|
1168 |
||
1169 |
/*! |
|
1170 |
Returns an object that contains a serialized description of the specified |
|
1171 |
\a indexes. The format used to describe the items corresponding to the |
|
1172 |
indexes is obtained from the mimeTypes() function. |
|
1173 |
||
1174 |
If the list of indexes is empty, 0 is returned rather than a serialized |
|
1175 |
empty list. |
|
1176 |
*/ |
|
1177 |
QMimeData *QFileSystemModel::mimeData(const QModelIndexList &indexes) const |
|
1178 |
{ |
|
1179 |
QList<QUrl> urls; |
|
1180 |
QList<QModelIndex>::const_iterator it = indexes.begin(); |
|
1181 |
for (; it != indexes.end(); ++it) |
|
1182 |
if ((*it).column() == 0) |
|
1183 |
urls << QUrl::fromLocalFile(filePath(*it)); |
|
1184 |
QMimeData *data = new QMimeData(); |
|
1185 |
data->setUrls(urls); |
|
1186 |
return data; |
|
1187 |
} |
|
1188 |
||
1189 |
/*! |
|
1190 |
Handles the \a data supplied by a drag and drop operation that ended with |
|
1191 |
the given \a action over the row in the model specified by the \a row and |
|
1192 |
\a column and by the \a parent index. |
|
1193 |
||
1194 |
\sa supportedDropActions() |
|
1195 |
*/ |
|
1196 |
bool QFileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, |
|
1197 |
int row, int column, const QModelIndex &parent) |
|
1198 |
{ |
|
1199 |
Q_UNUSED(row); |
|
1200 |
Q_UNUSED(column); |
|
1201 |
if (!parent.isValid() || isReadOnly()) |
|
1202 |
return false; |
|
1203 |
||
1204 |
bool success = true; |
|
1205 |
QString to = filePath(parent) + QDir::separator(); |
|
1206 |
||
1207 |
QList<QUrl> urls = data->urls(); |
|
1208 |
QList<QUrl>::const_iterator it = urls.constBegin(); |
|
1209 |
||
1210 |
switch (action) { |
|
1211 |
case Qt::CopyAction: |
|
1212 |
for (; it != urls.constEnd(); ++it) { |
|
1213 |
QString path = (*it).toLocalFile(); |
|
1214 |
success = QFile::copy(path, to + QFileInfo(path).fileName()) && success; |
|
1215 |
} |
|
1216 |
break; |
|
1217 |
case Qt::LinkAction: |
|
1218 |
for (; it != urls.constEnd(); ++it) { |
|
1219 |
QString path = (*it).toLocalFile(); |
|
1220 |
success = QFile::link(path, to + QFileInfo(path).fileName()) && success; |
|
1221 |
} |
|
1222 |
break; |
|
1223 |
case Qt::MoveAction: |
|
1224 |
for (; it != urls.constEnd(); ++it) { |
|
1225 |
QString path = (*it).toLocalFile(); |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
3
diff
changeset
|
1226 |
success = QFile::rename(path, to + QFileInfo(path).fileName()) && success; |
0 | 1227 |
} |
1228 |
break; |
|
1229 |
default: |
|
1230 |
return false; |
|
1231 |
} |
|
1232 |
||
1233 |
return success; |
|
1234 |
} |
|
1235 |
||
1236 |
/*! |
|
1237 |
\reimp |
|
1238 |
*/ |
|
1239 |
Qt::DropActions QFileSystemModel::supportedDropActions() const |
|
1240 |
{ |
|
1241 |
return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; |
|
1242 |
} |
|
1243 |
||
1244 |
/*! |
|
1245 |
Returns the path of the item stored in the model under the |
|
1246 |
\a index given. |
|
1247 |
*/ |
|
1248 |
QString QFileSystemModel::filePath(const QModelIndex &index) const |
|
1249 |
{ |
|
1250 |
Q_D(const QFileSystemModel); |
|
1251 |
QString fullPath = d->filePath(index); |
|
1252 |
QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index); |
|
1253 |
if (dirNode->isSymLink() && d->fileInfoGatherer.resolveSymlinks() |
|
1254 |
&& d->resolvedSymLinks.contains(fullPath) |
|
1255 |
&& dirNode->isDir()) { |
|
1256 |
QFileInfo resolvedInfo(fullPath); |
|
1257 |
resolvedInfo = resolvedInfo.canonicalFilePath(); |
|
1258 |
if (resolvedInfo.exists()) |
|
1259 |
return resolvedInfo.filePath(); |
|
1260 |
} |
|
1261 |
return fullPath; |
|
1262 |
} |
|
1263 |
||
1264 |
QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const |
|
1265 |
{ |
|
1266 |
Q_Q(const QFileSystemModel); |
|
1267 |
Q_UNUSED(q); |
|
1268 |
if (!index.isValid()) |
|
1269 |
return QString(); |
|
1270 |
Q_ASSERT(index.model() == q); |
|
1271 |
||
1272 |
QStringList path; |
|
1273 |
QModelIndex idx = index; |
|
1274 |
while (idx.isValid()) { |
|
1275 |
QFileSystemModelPrivate::QFileSystemNode *dirNode = node(idx); |
|
1276 |
if (dirNode) |
|
1277 |
path.prepend(dirNode->fileName); |
|
1278 |
idx = idx.parent(); |
|
1279 |
} |
|
1280 |
QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator())); |
|
1281 |
#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE) |
|
1282 |
if ((fullPath.length() > 2) && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/')) |
|
1283 |
fullPath = fullPath.mid(1); |
|
1284 |
#endif |
|
1285 |
return fullPath; |
|
1286 |
} |
|
1287 |
||
1288 |
/*! |
|
1289 |
Create a directory with the \a name in the \a parent model index. |
|
1290 |
*/ |
|
1291 |
QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent, const QString &name) |
|
1292 |
{ |
|
1293 |
Q_D(QFileSystemModel); |
|
1294 |
if (!parent.isValid()) |
|
1295 |
return parent; |
|
1296 |
||
1297 |
QDir dir(filePath(parent)); |
|
1298 |
if (!dir.mkdir(name)) |
|
1299 |
return QModelIndex(); |
|
1300 |
QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent); |
|
1301 |
d->addNode(parentNode, name, QFileInfo()); |
|
1302 |
Q_ASSERT(parentNode->children.contains(name)); |
|
1303 |
QFileSystemModelPrivate::QFileSystemNode *node = parentNode->children[name]; |
|
1304 |
node->populate(d->fileInfoGatherer.getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name))); |
|
1305 |
d->addVisibleFiles(parentNode, QStringList(name)); |
|
1306 |
return d->index(node); |
|
1307 |
} |
|
1308 |
||
1309 |
/*! |
|
1310 |
Returns the complete OR-ed together combination of QFile::Permission for the \a index. |
|
1311 |
*/ |
|
1312 |
QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index) const |
|
1313 |
{ |
|
1314 |
Q_D(const QFileSystemModel); |
|
1315 |
QFile::Permissions p = d->node(index)->permissions(); |
|
1316 |
if (d->readOnly) { |
|
1317 |
p ^= (QFile::WriteOwner | QFile::WriteUser |
|
1318 |
| QFile::WriteGroup | QFile::WriteOther); |
|
1319 |
} |
|
1320 |
return p; |
|
1321 |
} |
|
1322 |
||
1323 |
/*! |
|
1324 |
Sets the directory that is being watched by the model to \a newPath by |
|
1325 |
installing a \l{QFileSystemWatcher}{file system watcher} on it. Any |
|
1326 |
changes to files and directories within this directory will be |
|
1327 |
reflected in the model. |
|
1328 |
||
1329 |
If the path is changed, the rootPathChanged() signal will be emitted. |
|
1330 |
||
1331 |
\note This function does not change the structure of the model or |
|
1332 |
modify the data available to views. In other words, the "root" of |
|
1333 |
the model is \e not changed to include only files and directories |
|
1334 |
within the directory specified by \a newPath in the file system. |
|
1335 |
*/ |
|
1336 |
QModelIndex QFileSystemModel::setRootPath(const QString &newPath) |
|
1337 |
{ |
|
1338 |
Q_D(QFileSystemModel); |
|
1339 |
#ifdef Q_OS_WIN |
|
1340 |
QString longNewPath = QDir::fromNativeSeparators(qt_GetLongPathName(newPath)); |
|
1341 |
#else |
|
1342 |
QString longNewPath = newPath; |
|
1343 |
#endif |
|
1344 |
QDir newPathDir(longNewPath); |
|
1345 |
//we remove .. and . from the given path if exist |
|
1346 |
if (!newPath.isEmpty()) { |
|
1347 |
longNewPath = QDir::cleanPath(longNewPath); |
|
1348 |
newPathDir.setPath(longNewPath); |
|
1349 |
} |
|
1350 |
||
1351 |
d->setRootPath = true; |
|
1352 |
||
1353 |
//user don't ask for the root path ("") but the conversion failed |
|
1354 |
if (!newPath.isEmpty() && longNewPath.isEmpty()) |
|
1355 |
return d->index(rootPath()); |
|
1356 |
||
1357 |
if (d->rootDir.path() == longNewPath) |
|
1358 |
return d->index(rootPath()); |
|
1359 |
||
1360 |
bool showDrives = (longNewPath.isEmpty() || longNewPath == d->myComputer()); |
|
1361 |
if (!showDrives && !newPathDir.exists()) |
|
1362 |
return d->index(rootPath()); |
|
1363 |
||
13
c0432d11811c
eb175c3290cd7ea85da4a590db9461504a4904bc
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1364 |
//We remove the watcher on the previous path |
c0432d11811c
eb175c3290cd7ea85da4a590db9461504a4904bc
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1365 |
if (!rootPath().isEmpty() && rootPath() != QLatin1String(".")) { |
c0432d11811c
eb175c3290cd7ea85da4a590db9461504a4904bc
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1366 |
//This remove the watcher for the old rootPath |
c0432d11811c
eb175c3290cd7ea85da4a590db9461504a4904bc
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1367 |
d->fileInfoGatherer.removePath(rootPath()); |
c0432d11811c
eb175c3290cd7ea85da4a590db9461504a4904bc
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1368 |
//This line "marks" the node as dirty, so the next fetchMore |
c0432d11811c
eb175c3290cd7ea85da4a590db9461504a4904bc
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1369 |
//call on the path will ask the gatherer to install a watcher again |
c0432d11811c
eb175c3290cd7ea85da4a590db9461504a4904bc
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1370 |
//But it doesn't re-fetch everything |
c0432d11811c
eb175c3290cd7ea85da4a590db9461504a4904bc
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1371 |
d->node(rootPath())->populatedChildren = false; |
c0432d11811c
eb175c3290cd7ea85da4a590db9461504a4904bc
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1372 |
} |
c0432d11811c
eb175c3290cd7ea85da4a590db9461504a4904bc
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1373 |
|
0 | 1374 |
// We have a new valid root path |
1375 |
d->rootDir = newPathDir; |
|
1376 |
QModelIndex newRootIndex; |
|
1377 |
if (showDrives) { |
|
1378 |
// otherwise dir will become '.' |
|
1379 |
d->rootDir.setPath(QLatin1String("")); |
|
1380 |
} else { |
|
1381 |
newRootIndex = d->index(newPathDir.path()); |
|
1382 |
} |
|
1383 |
fetchMore(newRootIndex); |
|
1384 |
emit rootPathChanged(longNewPath); |
|
1385 |
d->forceSort = true; |
|
1386 |
d->delayedSort(); |
|
1387 |
return newRootIndex; |
|
1388 |
} |
|
1389 |
||
1390 |
/*! |
|
1391 |
The currently set root path |
|
1392 |
||
1393 |
\sa rootDirectory() |
|
1394 |
*/ |
|
1395 |
QString QFileSystemModel::rootPath() const |
|
1396 |
{ |
|
1397 |
Q_D(const QFileSystemModel); |
|
1398 |
return d->rootDir.path(); |
|
1399 |
} |
|
1400 |
||
1401 |
/*! |
|
1402 |
The currently set directory |
|
1403 |
||
1404 |
\sa rootPath() |
|
1405 |
*/ |
|
1406 |
QDir QFileSystemModel::rootDirectory() const |
|
1407 |
{ |
|
1408 |
Q_D(const QFileSystemModel); |
|
1409 |
QDir dir(d->rootDir); |
|
1410 |
dir.setNameFilters(nameFilters()); |
|
1411 |
dir.setFilter(filter()); |
|
1412 |
return dir; |
|
1413 |
} |
|
1414 |
||
1415 |
/*! |
|
1416 |
Sets the \a provider of file icons for the directory model. |
|
1417 |
*/ |
|
1418 |
void QFileSystemModel::setIconProvider(QFileIconProvider *provider) |
|
1419 |
{ |
|
1420 |
Q_D(QFileSystemModel); |
|
1421 |
d->fileInfoGatherer.setIconProvider(provider); |
|
1422 |
QApplication::processEvents(); |
|
1423 |
d->root.updateIcon(provider, QString()); |
|
1424 |
} |
|
1425 |
||
1426 |
/*! |
|
1427 |
Returns the file icon provider for this directory model. |
|
1428 |
*/ |
|
1429 |
QFileIconProvider *QFileSystemModel::iconProvider() const |
|
1430 |
{ |
|
1431 |
Q_D(const QFileSystemModel); |
|
1432 |
return d->fileInfoGatherer.iconProvider(); |
|
1433 |
} |
|
1434 |
||
1435 |
/*! |
|
1436 |
Sets the directory model's filter to that specified by \a filters. |
|
1437 |
||
1438 |
Note that the filter you set should always include the QDir::AllDirs enum value, |
|
1439 |
otherwise QFileSystemModel won't be able to read the directory structure. |
|
1440 |
||
1441 |
\sa QDir::Filters |
|
1442 |
*/ |
|
1443 |
void QFileSystemModel::setFilter(QDir::Filters filters) |
|
1444 |
{ |
|
1445 |
Q_D(QFileSystemModel); |
|
1446 |
if (d->filters == filters) |
|
1447 |
return; |
|
1448 |
d->filters = filters; |
|
1449 |
// CaseSensitivity might have changed |
|
1450 |
setNameFilters(nameFilters()); |
|
1451 |
d->forceSort = true; |
|
1452 |
d->delayedSort(); |
|
1453 |
} |
|
1454 |
||
1455 |
/*! |
|
1456 |
Returns the filter specified for the directory model. |
|
1457 |
||
1458 |
If a filter has not been set, the default filter is QDir::AllEntries | |
|
1459 |
QDir::NoDotAndDotDot | QDir::AllDirs. |
|
1460 |
||
1461 |
\sa QDir::Filters |
|
1462 |
*/ |
|
1463 |
QDir::Filters QFileSystemModel::filter() const |
|
1464 |
{ |
|
1465 |
Q_D(const QFileSystemModel); |
|
1466 |
return d->filters; |
|
1467 |
} |
|
1468 |
||
1469 |
/*! |
|
1470 |
\property QFileSystemModel::resolveSymlinks |
|
1471 |
\brief Whether the directory model should resolve symbolic links |
|
1472 |
||
1473 |
This is only relevant on operating systems that support symbolic links. |
|
1474 |
||
1475 |
By default, this property is false. |
|
1476 |
*/ |
|
1477 |
void QFileSystemModel::setResolveSymlinks(bool enable) |
|
1478 |
{ |
|
1479 |
Q_D(QFileSystemModel); |
|
1480 |
d->fileInfoGatherer.setResolveSymlinks(enable); |
|
1481 |
} |
|
1482 |
||
1483 |
bool QFileSystemModel::resolveSymlinks() const |
|
1484 |
{ |
|
1485 |
Q_D(const QFileSystemModel); |
|
1486 |
return d->fileInfoGatherer.resolveSymlinks(); |
|
1487 |
} |
|
1488 |
||
1489 |
/*! |
|
1490 |
\property QFileSystemModel::readOnly |
|
1491 |
\brief Whether the directory model allows writing to the file system |
|
1492 |
||
1493 |
If this property is set to false, the directory model will allow renaming, copying |
|
1494 |
and deleting of files and directories. |
|
1495 |
||
1496 |
This property is true by default |
|
1497 |
*/ |
|
1498 |
void QFileSystemModel::setReadOnly(bool enable) |
|
1499 |
{ |
|
1500 |
Q_D(QFileSystemModel); |
|
1501 |
d->readOnly = enable; |
|
1502 |
} |
|
1503 |
||
1504 |
bool QFileSystemModel::isReadOnly() const |
|
1505 |
{ |
|
1506 |
Q_D(const QFileSystemModel); |
|
1507 |
return d->readOnly; |
|
1508 |
} |
|
1509 |
||
1510 |
/*! |
|
1511 |
\property QFileSystemModel::nameFilterDisables |
|
1512 |
\brief Whether files that don't pass the name filter are hidden or disabled |
|
1513 |
||
1514 |
This property is true by default |
|
1515 |
*/ |
|
1516 |
void QFileSystemModel::setNameFilterDisables(bool enable) |
|
1517 |
{ |
|
1518 |
Q_D(QFileSystemModel); |
|
1519 |
if (d->nameFilterDisables == enable) |
|
1520 |
return; |
|
1521 |
d->nameFilterDisables = enable; |
|
1522 |
d->forceSort = true; |
|
1523 |
d->delayedSort(); |
|
1524 |
} |
|
1525 |
||
1526 |
bool QFileSystemModel::nameFilterDisables() const |
|
1527 |
{ |
|
1528 |
Q_D(const QFileSystemModel); |
|
1529 |
return d->nameFilterDisables; |
|
1530 |
} |
|
1531 |
||
1532 |
/*! |
|
1533 |
Sets the name \a filters to apply against the existing files. |
|
1534 |
*/ |
|
1535 |
void QFileSystemModel::setNameFilters(const QStringList &filters) |
|
1536 |
{ |
|
1537 |
// Prep the regexp's ahead of time |
|
1538 |
#ifndef QT_NO_REGEXP |
|
1539 |
Q_D(QFileSystemModel); |
|
1540 |
||
1541 |
if (!d->bypassFilters.isEmpty()) { |
|
1542 |
// update the bypass filter to only bypass the stuff that must be kept around |
|
1543 |
d->bypassFilters.clear(); |
|
1544 |
// We guarantee that rootPath will stick around |
|
1545 |
QPersistentModelIndex root(index(rootPath())); |
|
1546 |
QModelIndexList persistantList = persistentIndexList(); |
|
1547 |
for (int i = 0; i < persistantList.count(); ++i) { |
|
1548 |
QFileSystemModelPrivate::QFileSystemNode *node; |
|
1549 |
node = d->node(persistantList.at(i)); |
|
1550 |
while (node) { |
|
1551 |
if (d->bypassFilters.contains(node)) |
|
1552 |
break; |
|
1553 |
if (node->isDir()) |
|
1554 |
d->bypassFilters[node] = true; |
|
1555 |
node = node->parent; |
|
1556 |
} |
|
1557 |
} |
|
1558 |
} |
|
1559 |
||
1560 |
d->nameFilters.clear(); |
|
1561 |
const Qt::CaseSensitivity caseSensitive = |
|
1562 |
(filter() & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive; |
|
1563 |
for (int i = 0; i < filters.size(); ++i) { |
|
1564 |
d->nameFilters << QRegExp(filters.at(i), caseSensitive, QRegExp::Wildcard); |
|
1565 |
} |
|
1566 |
d->forceSort = true; |
|
1567 |
d->delayedSort(); |
|
1568 |
#endif |
|
1569 |
} |
|
1570 |
||
1571 |
/*! |
|
1572 |
Returns a list of filters applied to the names in the model. |
|
1573 |
*/ |
|
1574 |
QStringList QFileSystemModel::nameFilters() const |
|
1575 |
{ |
|
1576 |
Q_D(const QFileSystemModel); |
|
1577 |
QStringList filters; |
|
1578 |
#ifndef QT_NO_REGEXP |
|
1579 |
for (int i = 0; i < d->nameFilters.size(); ++i) { |
|
1580 |
filters << d->nameFilters.at(i).pattern(); |
|
1581 |
} |
|
1582 |
#endif |
|
1583 |
return filters; |
|
1584 |
} |
|
1585 |
||
1586 |
/*! |
|
1587 |
\reimp |
|
1588 |
*/ |
|
1589 |
bool QFileSystemModel::event(QEvent *event) |
|
1590 |
{ |
|
1591 |
Q_D(QFileSystemModel); |
|
1592 |
if (event->type() == QEvent::LanguageChange) { |
|
1593 |
d->root.retranslateStrings(d->fileInfoGatherer.iconProvider(), QString()); |
|
1594 |
return true; |
|
1595 |
} |
|
1596 |
return QAbstractItemModel::event(event); |
|
1597 |
} |
|
1598 |
||
1599 |
/*! |
|
1600 |
\internal |
|
1601 |
||
1602 |
Performed quick listing and see if any files have been added or removed, |
|
1603 |
then fetch more information on visible files. |
|
1604 |
*/ |
|
1605 |
void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files) |
|
1606 |
{ |
|
1607 |
QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory, false); |
|
1608 |
if (parentNode->children.count() == 0) |
|
1609 |
return; |
|
1610 |
QStringList toRemove; |
|
1611 |
#if defined(Q_OS_SYMBIAN) |
|
1612 |
// Filename case must be exact in qBinaryFind below, so create a list of all lowercase names. |
|
1613 |
QStringList newFiles; |
|
1614 |
for(int i = 0; i < files.size(); i++) { |
|
1615 |
newFiles << files.at(i).toLower(); |
|
1616 |
} |
|
1617 |
#else |
|
1618 |
QStringList newFiles = files; |
|
1619 |
#endif |
|
1620 |
qSort(newFiles.begin(), newFiles.end()); |
|
1621 |
QHash<QString, QFileSystemNode*>::const_iterator i = parentNode->children.constBegin(); |
|
1622 |
while (i != parentNode->children.constEnd()) { |
|
1623 |
QStringList::iterator iterator; |
|
1624 |
iterator = qBinaryFind(newFiles.begin(), newFiles.end(), |
|
1625 |
#if defined(Q_OS_SYMBIAN) |
|
1626 |
i.value()->fileName.toLower()); |
|
1627 |
#else |
|
1628 |
i.value()->fileName); |
|
1629 |
#endif |
|
1630 |
if (iterator == newFiles.end()) { |
|
1631 |
toRemove.append(i.value()->fileName); |
|
1632 |
} |
|
1633 |
++i; |
|
1634 |
} |
|
1635 |
for (int i = 0 ; i < toRemove.count() ; ++i ) |
|
1636 |
removeNode(parentNode, toRemove[i]); |
|
1637 |
} |
|
1638 |
||
1639 |
/*! |
|
1640 |
\internal |
|
1641 |
||
1642 |
Adds a new file to the children of parentNode |
|
1643 |
||
1644 |
*WARNING* this will change the count of children |
|
1645 |
*/ |
|
1646 |
QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo& info) |
|
1647 |
{ |
|
1648 |
// In the common case, itemLocation == count() so check there first |
|
1649 |
QFileSystemModelPrivate::QFileSystemNode *node = new QFileSystemModelPrivate::QFileSystemNode(fileName, parentNode); |
|
1650 |
#ifndef QT_NO_FILESYSTEMWATCHER |
|
1651 |
node->populate(info); |
|
1652 |
#endif |
|
1653 |
parentNode->children.insert(fileName, node); |
|
1654 |
return node; |
|
1655 |
} |
|
1656 |
||
1657 |
/*! |
|
1658 |
\internal |
|
1659 |
||
1660 |
File at parentNode->children(itemLocation) has been removed, remove from the lists |
|
1661 |
and emit signals if necessary |
|
1662 |
||
1663 |
*WARNING* this will change the count of children and could change visibleChildren |
|
1664 |
*/ |
|
1665 |
void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode, const QString& name) |
|
1666 |
{ |
|
1667 |
Q_Q(QFileSystemModel); |
|
1668 |
QModelIndex parent = index(parentNode); |
|
1669 |
bool indexHidden = isHiddenByFilter(parentNode, parent); |
|
1670 |
||
1671 |
int vLocation = parentNode->visibleLocation(name); |
|
1672 |
if (vLocation >= 0 && !indexHidden) |
|
1673 |
q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation), |
|
1674 |
translateVisibleLocation(parentNode, vLocation)); |
|
1675 |
QFileSystemNode * node = parentNode->children.take(name); |
|
1676 |
delete node; |
|
1677 |
// cleanup sort files after removing rather then re-sorting which is O(n) |
|
1678 |
if (vLocation >= 0) |
|
1679 |
parentNode->visibleChildren.removeAt(vLocation); |
|
1680 |
if (vLocation >= 0 && !indexHidden) |
|
1681 |
q->endRemoveRows(); |
|
1682 |
} |
|
1683 |
||
1684 |
/* |
|
1685 |
\internal |
|
1686 |
Helper functor used by addVisibleFiles() |
|
1687 |
*/ |
|
1688 |
class QFileSystemModelVisibleFinder |
|
1689 |
{ |
|
1690 |
public: |
|
1691 |
inline QFileSystemModelVisibleFinder(QFileSystemModelPrivate::QFileSystemNode *node, QFileSystemModelSorter *sorter) : parentNode(node), sorter(sorter) {} |
|
1692 |
||
1693 |
bool operator()(const QString &, QString r) const |
|
1694 |
{ |
|
1695 |
return sorter->compareNodes(parentNode->children.value(name), parentNode->children.value(r)); |
|
1696 |
} |
|
1697 |
||
1698 |
QString name; |
|
1699 |
private: |
|
1700 |
QFileSystemModelPrivate::QFileSystemNode *parentNode; |
|
1701 |
QFileSystemModelSorter *sorter; |
|
1702 |
}; |
|
1703 |
||
1704 |
/*! |
|
1705 |
\internal |
|
1706 |
||
1707 |
File at parentNode->children(itemLocation) was not visible before, but now should be |
|
1708 |
and emit signals if necessary. |
|
1709 |
||
1710 |
*WARNING* this will change the visible count |
|
1711 |
*/ |
|
1712 |
void QFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles) |
|
1713 |
{ |
|
1714 |
Q_Q(QFileSystemModel); |
|
1715 |
QModelIndex parent = index(parentNode); |
|
1716 |
bool indexHidden = isHiddenByFilter(parentNode, parent); |
|
1717 |
if (!indexHidden) { |
|
1718 |
q->beginInsertRows(parent, parentNode->visibleChildren.count() , parentNode->visibleChildren.count() + newFiles.count() - 1); |
|
1719 |
} |
|
1720 |
||
1721 |
if (parentNode->dirtyChildrenIndex == -1) |
|
1722 |
parentNode->dirtyChildrenIndex = parentNode->visibleChildren.count(); |
|
1723 |
||
1724 |
for (int i = 0; i < newFiles.count(); ++i) { |
|
1725 |
parentNode->visibleChildren.append(newFiles.at(i)); |
|
1726 |
parentNode->children[newFiles.at(i)]->isVisible = true; |
|
1727 |
} |
|
1728 |
if (!indexHidden) |
|
1729 |
q->endInsertRows(); |
|
1730 |
} |
|
1731 |
||
1732 |
/*! |
|
1733 |
\internal |
|
1734 |
||
1735 |
File was visible before, but now should NOT be |
|
1736 |
||
1737 |
*WARNING* this will change the visible count |
|
1738 |
*/ |
|
1739 |
void QFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode, int vLocation) |
|
1740 |
{ |
|
1741 |
Q_Q(QFileSystemModel); |
|
1742 |
if (vLocation == -1) |
|
1743 |
return; |
|
1744 |
QModelIndex parent = index(parentNode); |
|
1745 |
bool indexHidden = isHiddenByFilter(parentNode, parent); |
|
1746 |
if (!indexHidden) |
|
1747 |
q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation), |
|
1748 |
translateVisibleLocation(parentNode, vLocation)); |
|
1749 |
parentNode->children[parentNode->visibleChildren.at(vLocation)]->isVisible = false; |
|
1750 |
parentNode->visibleChildren.removeAt(vLocation); |
|
1751 |
if (!indexHidden) |
|
1752 |
q->endRemoveRows(); |
|
1753 |
} |
|
1754 |
||
1755 |
/*! |
|
1756 |
\internal |
|
1757 |
||
1758 |
The thread has received new information about files, |
|
1759 |
update and emit dataChanged if it has actually changed. |
|
1760 |
*/ |
|
1761 |
void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo> > &updates) |
|
1762 |
{ |
|
1763 |
Q_Q(QFileSystemModel); |
|
1764 |
QVector<QString> rowsToUpdate; |
|
1765 |
QStringList newFiles; |
|
1766 |
QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path, false); |
|
1767 |
QModelIndex parentIndex = index(parentNode); |
|
1768 |
for (int i = 0; i < updates.count(); ++i) { |
|
1769 |
QString fileName = updates.at(i).first; |
|
1770 |
Q_ASSERT(!fileName.isEmpty()); |
|
1771 |
QExtendedInformation info = fileInfoGatherer.getInfo(updates.at(i).second); |
|
1772 |
bool previouslyHere = parentNode->children.contains(fileName); |
|
1773 |
if (!previouslyHere) { |
|
1774 |
addNode(parentNode, fileName, info.fileInfo()); |
|
1775 |
} |
|
1776 |
QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName); |
|
1777 |
bool isCaseSensitive = parentNode->caseSensitive(); |
|
1778 |
if (isCaseSensitive) { |
|
1779 |
if (node->fileName != fileName) |
|
1780 |
continue; |
|
1781 |
} else { |
|
1782 |
if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0) |
|
1783 |
continue; |
|
1784 |
} |
|
1785 |
if (isCaseSensitive) { |
|
1786 |
Q_ASSERT(node->fileName == fileName); |
|
1787 |
} else { |
|
1788 |
node->fileName = fileName; |
|
1789 |
} |
|
1790 |
||
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
3
diff
changeset
|
1791 |
if (info.size() == -1 && !info.isSymLink()) { |
0 | 1792 |
removeNode(parentNode, fileName); |
1793 |
continue; |
|
1794 |
} |
|
1795 |
if (*node != info ) { |
|
1796 |
node->populate(info); |
|
1797 |
bypassFilters.remove(node); |
|
1798 |
// brand new information. |
|
1799 |
if (filtersAcceptsNode(node)) { |
|
1800 |
if (!node->isVisible) { |
|
1801 |
newFiles.append(fileName); |
|
1802 |
} else { |
|
1803 |
rowsToUpdate.append(fileName); |
|
1804 |
} |
|
1805 |
} else { |
|
1806 |
if (node->isVisible) { |
|
1807 |
int visibleLocation = parentNode->visibleLocation(fileName); |
|
1808 |
removeVisibleFile(parentNode, visibleLocation); |
|
1809 |
} else { |
|
1810 |
// The file is not visible, don't do anything |
|
1811 |
} |
|
1812 |
} |
|
1813 |
} |
|
1814 |
} |
|
1815 |
||
1816 |
// bundle up all of the changed signals into as few as possible. |
|
1817 |
qSort(rowsToUpdate.begin(), rowsToUpdate.end()); |
|
1818 |
QString min; |
|
1819 |
QString max; |
|
1820 |
for (int i = 0; i < rowsToUpdate.count(); ++i) { |
|
1821 |
QString value = rowsToUpdate.at(i); |
|
1822 |
//##TODO is there a way to bundle signals with QString as the content of the list? |
|
1823 |
/*if (min.isEmpty()) { |
|
1824 |
min = value; |
|
1825 |
if (i != rowsToUpdate.count() - 1) |
|
1826 |
continue; |
|
1827 |
} |
|
1828 |
if (i != rowsToUpdate.count() - 1) { |
|
1829 |
if ((value == min + 1 && max.isEmpty()) || value == max + 1) { |
|
1830 |
max = value; |
|
1831 |
continue; |
|
1832 |
} |
|
1833 |
}*/ |
|
1834 |
max = value; |
|
1835 |
min = value; |
|
1836 |
int visibleMin = parentNode->visibleLocation(min); |
|
1837 |
int visibleMax = parentNode->visibleLocation(max); |
|
1838 |
if (visibleMin >= 0 |
|
1839 |
&& visibleMin < parentNode->visibleChildren.count() |
|
1840 |
&& parentNode->visibleChildren.at(visibleMin) == min |
|
1841 |
&& visibleMax >= 0) { |
|
1842 |
QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMin), 0, parentIndex); |
|
1843 |
QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMax), 3, parentIndex); |
|
1844 |
emit q->dataChanged(bottom, top); |
|
1845 |
} |
|
1846 |
||
1847 |
/*min = QString(); |
|
1848 |
max = QString();*/ |
|
1849 |
} |
|
1850 |
||
1851 |
if (newFiles.count() > 0) { |
|
1852 |
addVisibleFiles(parentNode, newFiles); |
|
1853 |
} |
|
1854 |
||
1855 |
if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) { |
|
1856 |
forceSort = true; |
|
1857 |
delayedSort(); |
|
1858 |
} |
|
1859 |
} |
|
1860 |
||
1861 |
/*! |
|
1862 |
\internal |
|
1863 |
*/ |
|
1864 |
void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName) |
|
1865 |
{ |
|
1866 |
resolvedSymLinks[fileName] = resolvedName; |
|
1867 |
} |
|
1868 |
||
1869 |
/*! |
|
1870 |
\internal |
|
1871 |
*/ |
|
1872 |
void QFileSystemModelPrivate::init() |
|
1873 |
{ |
|
1874 |
Q_Q(QFileSystemModel); |
|
1875 |
qRegisterMetaType<QList<QPair<QString,QFileInfo> > >("QList<QPair<QString,QFileInfo> >"); |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1876 |
q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(QString,QStringList)), |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1877 |
q, SLOT(_q_directoryChanged(QString,QStringList))); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1878 |
q->connect(&fileInfoGatherer, SIGNAL(updates(QString,QList<QPair<QString,QFileInfo> >)), |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1879 |
q, SLOT(_q_fileSystemChanged(QString,QList<QPair<QString,QFileInfo> >))); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1880 |
q->connect(&fileInfoGatherer, SIGNAL(nameResolved(QString,QString)), |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1881 |
q, SLOT(_q_resolvedName(QString,QString))); |
0 | 1882 |
q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection); |
1883 |
} |
|
1884 |
||
1885 |
/*! |
|
1886 |
\internal |
|
1887 |
||
1888 |
Returns false if node doesn't pass the filters otherwise true |
|
1889 |
||
1890 |
QDir::Modified is not supported |
|
1891 |
QDir::Drives is not supported |
|
1892 |
*/ |
|
1893 |
bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) const |
|
1894 |
{ |
|
1895 |
// always accept drives |
|
1896 |
if (node->parent == &root || bypassFilters.contains(node)) |
|
1897 |
return true; |
|
1898 |
||
1899 |
// If we don't know anything yet don't accept it |
|
1900 |
if (!node->hasInformation()) |
|
1901 |
return false; |
|
1902 |
||
1903 |
const bool filterPermissions = ((filters & QDir::PermissionMask) |
|
1904 |
&& (filters & QDir::PermissionMask) != QDir::PermissionMask); |
|
1905 |
const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs)); |
|
1906 |
const bool hideFiles = !(filters & QDir::Files); |
|
1907 |
const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable)); |
|
1908 |
const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable)); |
|
1909 |
const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable)); |
|
1910 |
const bool hideHidden = !(filters & QDir::Hidden); |
|
1911 |
const bool hideSystem = !(filters & QDir::System); |
|
1912 |
const bool hideSymlinks = (filters & QDir::NoSymLinks); |
|
1913 |
const bool hideDotAndDotDot = (filters & QDir::NoDotAndDotDot); |
|
1914 |
||
1915 |
// Note that we match the behavior of entryList and not QFileInfo on this and this |
|
1916 |
// incompatibility won't be fixed until Qt 5 at least |
|
1917 |
bool isDotOrDot = ( (node->fileName == QLatin1String(".") |
|
1918 |
|| node->fileName == QLatin1String(".."))); |
|
1919 |
if ( (hideHidden && (!isDotOrDot && node->isHidden())) |
|
1920 |
|| (hideSystem && node->isSystem()) |
|
1921 |
|| (hideDirs && node->isDir()) |
|
1922 |
|| (hideFiles && node->isFile()) |
|
1923 |
|| (hideSymlinks && node->isSymLink()) |
|
1924 |
|| (hideReadable && node->isReadable()) |
|
1925 |
|| (hideWritable && node->isWritable()) |
|
1926 |
|| (hideExecutable && node->isExecutable()) |
|
1927 |
|| (hideDotAndDotDot && isDotOrDot)) |
|
1928 |
return false; |
|
1929 |
||
1930 |
return nameFilterDisables || passNameFilters(node); |
|
1931 |
} |
|
1932 |
||
1933 |
/* |
|
1934 |
\internal |
|
1935 |
||
1936 |
Returns true if node passes the name filters and should be visible. |
|
1937 |
*/ |
|
1938 |
bool QFileSystemModelPrivate::passNameFilters(const QFileSystemNode *node) const |
|
1939 |
{ |
|
1940 |
#ifndef QT_NO_REGEXP |
|
1941 |
if (nameFilters.isEmpty()) |
|
1942 |
return true; |
|
1943 |
||
1944 |
// Check the name regularexpression filters |
|
1945 |
if (!(node->isDir() && (filters & QDir::AllDirs))) { |
|
1946 |
for (int i = 0; i < nameFilters.size(); ++i) { |
|
1947 |
if (nameFilters.at(i).exactMatch(node->fileName)) |
|
1948 |
return true; |
|
1949 |
} |
|
1950 |
return false; |
|
1951 |
} |
|
1952 |
#endif |
|
1953 |
return true; |
|
1954 |
} |
|
1955 |
||
1956 |
QT_END_NAMESPACE |
|
1957 |
||
1958 |
#include "moc_qfilesystemmodel.cpp" |
|
1959 |
||
1960 |
#endif // QT_NO_FILESYSTEMMODEL |