0
|
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 "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();
|
|
1226 |
success = QFile::copy(path, to + QFileInfo(path).fileName())
|
|
1227 |
&& QFile::remove(path) && success;
|
|
1228 |
}
|
|
1229 |
break;
|
|
1230 |
default:
|
|
1231 |
return false;
|
|
1232 |
}
|
|
1233 |
|
|
1234 |
return success;
|
|
1235 |
}
|
|
1236 |
|
|
1237 |
/*!
|
|
1238 |
\reimp
|
|
1239 |
*/
|
|
1240 |
Qt::DropActions QFileSystemModel::supportedDropActions() const
|
|
1241 |
{
|
|
1242 |
return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
|
|
1243 |
}
|
|
1244 |
|
|
1245 |
/*!
|
|
1246 |
Returns the path of the item stored in the model under the
|
|
1247 |
\a index given.
|
|
1248 |
*/
|
|
1249 |
QString QFileSystemModel::filePath(const QModelIndex &index) const
|
|
1250 |
{
|
|
1251 |
Q_D(const QFileSystemModel);
|
|
1252 |
QString fullPath = d->filePath(index);
|
|
1253 |
QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);
|
|
1254 |
if (dirNode->isSymLink() && d->fileInfoGatherer.resolveSymlinks()
|
|
1255 |
&& d->resolvedSymLinks.contains(fullPath)
|
|
1256 |
&& dirNode->isDir()) {
|
|
1257 |
QFileInfo resolvedInfo(fullPath);
|
|
1258 |
resolvedInfo = resolvedInfo.canonicalFilePath();
|
|
1259 |
if (resolvedInfo.exists())
|
|
1260 |
return resolvedInfo.filePath();
|
|
1261 |
}
|
|
1262 |
return fullPath;
|
|
1263 |
}
|
|
1264 |
|
|
1265 |
QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const
|
|
1266 |
{
|
|
1267 |
Q_Q(const QFileSystemModel);
|
|
1268 |
Q_UNUSED(q);
|
|
1269 |
if (!index.isValid())
|
|
1270 |
return QString();
|
|
1271 |
Q_ASSERT(index.model() == q);
|
|
1272 |
|
|
1273 |
QStringList path;
|
|
1274 |
QModelIndex idx = index;
|
|
1275 |
while (idx.isValid()) {
|
|
1276 |
QFileSystemModelPrivate::QFileSystemNode *dirNode = node(idx);
|
|
1277 |
if (dirNode)
|
|
1278 |
path.prepend(dirNode->fileName);
|
|
1279 |
idx = idx.parent();
|
|
1280 |
}
|
|
1281 |
QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator()));
|
|
1282 |
#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE)
|
|
1283 |
if ((fullPath.length() > 2) && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/'))
|
|
1284 |
fullPath = fullPath.mid(1);
|
|
1285 |
#endif
|
|
1286 |
return fullPath;
|
|
1287 |
}
|
|
1288 |
|
|
1289 |
/*!
|
|
1290 |
Create a directory with the \a name in the \a parent model index.
|
|
1291 |
*/
|
|
1292 |
QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent, const QString &name)
|
|
1293 |
{
|
|
1294 |
Q_D(QFileSystemModel);
|
|
1295 |
if (!parent.isValid())
|
|
1296 |
return parent;
|
|
1297 |
|
|
1298 |
QDir dir(filePath(parent));
|
|
1299 |
if (!dir.mkdir(name))
|
|
1300 |
return QModelIndex();
|
|
1301 |
QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);
|
|
1302 |
d->addNode(parentNode, name, QFileInfo());
|
|
1303 |
Q_ASSERT(parentNode->children.contains(name));
|
|
1304 |
QFileSystemModelPrivate::QFileSystemNode *node = parentNode->children[name];
|
|
1305 |
node->populate(d->fileInfoGatherer.getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name)));
|
|
1306 |
d->addVisibleFiles(parentNode, QStringList(name));
|
|
1307 |
return d->index(node);
|
|
1308 |
}
|
|
1309 |
|
|
1310 |
/*!
|
|
1311 |
Returns the complete OR-ed together combination of QFile::Permission for the \a index.
|
|
1312 |
*/
|
|
1313 |
QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index) const
|
|
1314 |
{
|
|
1315 |
Q_D(const QFileSystemModel);
|
|
1316 |
QFile::Permissions p = d->node(index)->permissions();
|
|
1317 |
if (d->readOnly) {
|
|
1318 |
p ^= (QFile::WriteOwner | QFile::WriteUser
|
|
1319 |
| QFile::WriteGroup | QFile::WriteOther);
|
|
1320 |
}
|
|
1321 |
return p;
|
|
1322 |
}
|
|
1323 |
|
|
1324 |
/*!
|
|
1325 |
Sets the directory that is being watched by the model to \a newPath by
|
|
1326 |
installing a \l{QFileSystemWatcher}{file system watcher} on it. Any
|
|
1327 |
changes to files and directories within this directory will be
|
|
1328 |
reflected in the model.
|
|
1329 |
|
|
1330 |
If the path is changed, the rootPathChanged() signal will be emitted.
|
|
1331 |
|
|
1332 |
\note This function does not change the structure of the model or
|
|
1333 |
modify the data available to views. In other words, the "root" of
|
|
1334 |
the model is \e not changed to include only files and directories
|
|
1335 |
within the directory specified by \a newPath in the file system.
|
|
1336 |
*/
|
|
1337 |
QModelIndex QFileSystemModel::setRootPath(const QString &newPath)
|
|
1338 |
{
|
|
1339 |
Q_D(QFileSystemModel);
|
|
1340 |
#ifdef Q_OS_WIN
|
|
1341 |
QString longNewPath = QDir::fromNativeSeparators(qt_GetLongPathName(newPath));
|
|
1342 |
#else
|
|
1343 |
QString longNewPath = newPath;
|
|
1344 |
#endif
|
|
1345 |
QDir newPathDir(longNewPath);
|
|
1346 |
//we remove .. and . from the given path if exist
|
|
1347 |
if (!newPath.isEmpty()) {
|
|
1348 |
longNewPath = QDir::cleanPath(longNewPath);
|
|
1349 |
newPathDir.setPath(longNewPath);
|
|
1350 |
}
|
|
1351 |
|
|
1352 |
d->setRootPath = true;
|
|
1353 |
|
|
1354 |
//user don't ask for the root path ("") but the conversion failed
|
|
1355 |
if (!newPath.isEmpty() && longNewPath.isEmpty())
|
|
1356 |
return d->index(rootPath());
|
|
1357 |
|
|
1358 |
if (d->rootDir.path() == longNewPath)
|
|
1359 |
return d->index(rootPath());
|
|
1360 |
|
|
1361 |
bool showDrives = (longNewPath.isEmpty() || longNewPath == d->myComputer());
|
|
1362 |
if (!showDrives && !newPathDir.exists())
|
|
1363 |
return d->index(rootPath());
|
|
1364 |
|
|
1365 |
// We have a new valid root path
|
|
1366 |
d->rootDir = newPathDir;
|
|
1367 |
QModelIndex newRootIndex;
|
|
1368 |
if (showDrives) {
|
|
1369 |
// otherwise dir will become '.'
|
|
1370 |
d->rootDir.setPath(QLatin1String(""));
|
|
1371 |
} else {
|
|
1372 |
newRootIndex = d->index(newPathDir.path());
|
|
1373 |
}
|
|
1374 |
fetchMore(newRootIndex);
|
|
1375 |
emit rootPathChanged(longNewPath);
|
|
1376 |
d->forceSort = true;
|
|
1377 |
d->delayedSort();
|
|
1378 |
return newRootIndex;
|
|
1379 |
}
|
|
1380 |
|
|
1381 |
/*!
|
|
1382 |
The currently set root path
|
|
1383 |
|
|
1384 |
\sa rootDirectory()
|
|
1385 |
*/
|
|
1386 |
QString QFileSystemModel::rootPath() const
|
|
1387 |
{
|
|
1388 |
Q_D(const QFileSystemModel);
|
|
1389 |
return d->rootDir.path();
|
|
1390 |
}
|
|
1391 |
|
|
1392 |
/*!
|
|
1393 |
The currently set directory
|
|
1394 |
|
|
1395 |
\sa rootPath()
|
|
1396 |
*/
|
|
1397 |
QDir QFileSystemModel::rootDirectory() const
|
|
1398 |
{
|
|
1399 |
Q_D(const QFileSystemModel);
|
|
1400 |
QDir dir(d->rootDir);
|
|
1401 |
dir.setNameFilters(nameFilters());
|
|
1402 |
dir.setFilter(filter());
|
|
1403 |
return dir;
|
|
1404 |
}
|
|
1405 |
|
|
1406 |
/*!
|
|
1407 |
Sets the \a provider of file icons for the directory model.
|
|
1408 |
*/
|
|
1409 |
void QFileSystemModel::setIconProvider(QFileIconProvider *provider)
|
|
1410 |
{
|
|
1411 |
Q_D(QFileSystemModel);
|
|
1412 |
d->fileInfoGatherer.setIconProvider(provider);
|
|
1413 |
QApplication::processEvents();
|
|
1414 |
d->root.updateIcon(provider, QString());
|
|
1415 |
}
|
|
1416 |
|
|
1417 |
/*!
|
|
1418 |
Returns the file icon provider for this directory model.
|
|
1419 |
*/
|
|
1420 |
QFileIconProvider *QFileSystemModel::iconProvider() const
|
|
1421 |
{
|
|
1422 |
Q_D(const QFileSystemModel);
|
|
1423 |
return d->fileInfoGatherer.iconProvider();
|
|
1424 |
}
|
|
1425 |
|
|
1426 |
/*!
|
|
1427 |
Sets the directory model's filter to that specified by \a filters.
|
|
1428 |
|
|
1429 |
Note that the filter you set should always include the QDir::AllDirs enum value,
|
|
1430 |
otherwise QFileSystemModel won't be able to read the directory structure.
|
|
1431 |
|
|
1432 |
\sa QDir::Filters
|
|
1433 |
*/
|
|
1434 |
void QFileSystemModel::setFilter(QDir::Filters filters)
|
|
1435 |
{
|
|
1436 |
Q_D(QFileSystemModel);
|
|
1437 |
if (d->filters == filters)
|
|
1438 |
return;
|
|
1439 |
d->filters = filters;
|
|
1440 |
// CaseSensitivity might have changed
|
|
1441 |
setNameFilters(nameFilters());
|
|
1442 |
d->forceSort = true;
|
|
1443 |
d->delayedSort();
|
|
1444 |
}
|
|
1445 |
|
|
1446 |
/*!
|
|
1447 |
Returns the filter specified for the directory model.
|
|
1448 |
|
|
1449 |
If a filter has not been set, the default filter is QDir::AllEntries |
|
|
1450 |
QDir::NoDotAndDotDot | QDir::AllDirs.
|
|
1451 |
|
|
1452 |
\sa QDir::Filters
|
|
1453 |
*/
|
|
1454 |
QDir::Filters QFileSystemModel::filter() const
|
|
1455 |
{
|
|
1456 |
Q_D(const QFileSystemModel);
|
|
1457 |
return d->filters;
|
|
1458 |
}
|
|
1459 |
|
|
1460 |
/*!
|
|
1461 |
\property QFileSystemModel::resolveSymlinks
|
|
1462 |
\brief Whether the directory model should resolve symbolic links
|
|
1463 |
|
|
1464 |
This is only relevant on operating systems that support symbolic links.
|
|
1465 |
|
|
1466 |
By default, this property is false.
|
|
1467 |
*/
|
|
1468 |
void QFileSystemModel::setResolveSymlinks(bool enable)
|
|
1469 |
{
|
|
1470 |
Q_D(QFileSystemModel);
|
|
1471 |
d->fileInfoGatherer.setResolveSymlinks(enable);
|
|
1472 |
}
|
|
1473 |
|
|
1474 |
bool QFileSystemModel::resolveSymlinks() const
|
|
1475 |
{
|
|
1476 |
Q_D(const QFileSystemModel);
|
|
1477 |
return d->fileInfoGatherer.resolveSymlinks();
|
|
1478 |
}
|
|
1479 |
|
|
1480 |
/*!
|
|
1481 |
\property QFileSystemModel::readOnly
|
|
1482 |
\brief Whether the directory model allows writing to the file system
|
|
1483 |
|
|
1484 |
If this property is set to false, the directory model will allow renaming, copying
|
|
1485 |
and deleting of files and directories.
|
|
1486 |
|
|
1487 |
This property is true by default
|
|
1488 |
*/
|
|
1489 |
void QFileSystemModel::setReadOnly(bool enable)
|
|
1490 |
{
|
|
1491 |
Q_D(QFileSystemModel);
|
|
1492 |
d->readOnly = enable;
|
|
1493 |
}
|
|
1494 |
|
|
1495 |
bool QFileSystemModel::isReadOnly() const
|
|
1496 |
{
|
|
1497 |
Q_D(const QFileSystemModel);
|
|
1498 |
return d->readOnly;
|
|
1499 |
}
|
|
1500 |
|
|
1501 |
/*!
|
|
1502 |
\property QFileSystemModel::nameFilterDisables
|
|
1503 |
\brief Whether files that don't pass the name filter are hidden or disabled
|
|
1504 |
|
|
1505 |
This property is true by default
|
|
1506 |
*/
|
|
1507 |
void QFileSystemModel::setNameFilterDisables(bool enable)
|
|
1508 |
{
|
|
1509 |
Q_D(QFileSystemModel);
|
|
1510 |
if (d->nameFilterDisables == enable)
|
|
1511 |
return;
|
|
1512 |
d->nameFilterDisables = enable;
|
|
1513 |
d->forceSort = true;
|
|
1514 |
d->delayedSort();
|
|
1515 |
}
|
|
1516 |
|
|
1517 |
bool QFileSystemModel::nameFilterDisables() const
|
|
1518 |
{
|
|
1519 |
Q_D(const QFileSystemModel);
|
|
1520 |
return d->nameFilterDisables;
|
|
1521 |
}
|
|
1522 |
|
|
1523 |
/*!
|
|
1524 |
Sets the name \a filters to apply against the existing files.
|
|
1525 |
*/
|
|
1526 |
void QFileSystemModel::setNameFilters(const QStringList &filters)
|
|
1527 |
{
|
|
1528 |
// Prep the regexp's ahead of time
|
|
1529 |
#ifndef QT_NO_REGEXP
|
|
1530 |
Q_D(QFileSystemModel);
|
|
1531 |
|
|
1532 |
if (!d->bypassFilters.isEmpty()) {
|
|
1533 |
// update the bypass filter to only bypass the stuff that must be kept around
|
|
1534 |
d->bypassFilters.clear();
|
|
1535 |
// We guarantee that rootPath will stick around
|
|
1536 |
QPersistentModelIndex root(index(rootPath()));
|
|
1537 |
QModelIndexList persistantList = persistentIndexList();
|
|
1538 |
for (int i = 0; i < persistantList.count(); ++i) {
|
|
1539 |
QFileSystemModelPrivate::QFileSystemNode *node;
|
|
1540 |
node = d->node(persistantList.at(i));
|
|
1541 |
while (node) {
|
|
1542 |
if (d->bypassFilters.contains(node))
|
|
1543 |
break;
|
|
1544 |
if (node->isDir())
|
|
1545 |
d->bypassFilters[node] = true;
|
|
1546 |
node = node->parent;
|
|
1547 |
}
|
|
1548 |
}
|
|
1549 |
}
|
|
1550 |
|
|
1551 |
d->nameFilters.clear();
|
|
1552 |
const Qt::CaseSensitivity caseSensitive =
|
|
1553 |
(filter() & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
|
|
1554 |
for (int i = 0; i < filters.size(); ++i) {
|
|
1555 |
d->nameFilters << QRegExp(filters.at(i), caseSensitive, QRegExp::Wildcard);
|
|
1556 |
}
|
|
1557 |
d->forceSort = true;
|
|
1558 |
d->delayedSort();
|
|
1559 |
#endif
|
|
1560 |
}
|
|
1561 |
|
|
1562 |
/*!
|
|
1563 |
Returns a list of filters applied to the names in the model.
|
|
1564 |
*/
|
|
1565 |
QStringList QFileSystemModel::nameFilters() const
|
|
1566 |
{
|
|
1567 |
Q_D(const QFileSystemModel);
|
|
1568 |
QStringList filters;
|
|
1569 |
#ifndef QT_NO_REGEXP
|
|
1570 |
for (int i = 0; i < d->nameFilters.size(); ++i) {
|
|
1571 |
filters << d->nameFilters.at(i).pattern();
|
|
1572 |
}
|
|
1573 |
#endif
|
|
1574 |
return filters;
|
|
1575 |
}
|
|
1576 |
|
|
1577 |
/*!
|
|
1578 |
\reimp
|
|
1579 |
*/
|
|
1580 |
bool QFileSystemModel::event(QEvent *event)
|
|
1581 |
{
|
|
1582 |
Q_D(QFileSystemModel);
|
|
1583 |
if (event->type() == QEvent::LanguageChange) {
|
|
1584 |
d->root.retranslateStrings(d->fileInfoGatherer.iconProvider(), QString());
|
|
1585 |
return true;
|
|
1586 |
}
|
|
1587 |
return QAbstractItemModel::event(event);
|
|
1588 |
}
|
|
1589 |
|
|
1590 |
/*!
|
|
1591 |
\internal
|
|
1592 |
|
|
1593 |
Performed quick listing and see if any files have been added or removed,
|
|
1594 |
then fetch more information on visible files.
|
|
1595 |
*/
|
|
1596 |
void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files)
|
|
1597 |
{
|
|
1598 |
QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory, false);
|
|
1599 |
if (parentNode->children.count() == 0)
|
|
1600 |
return;
|
|
1601 |
QStringList toRemove;
|
|
1602 |
#if defined(Q_OS_SYMBIAN)
|
|
1603 |
// Filename case must be exact in qBinaryFind below, so create a list of all lowercase names.
|
|
1604 |
QStringList newFiles;
|
|
1605 |
for(int i = 0; i < files.size(); i++) {
|
|
1606 |
newFiles << files.at(i).toLower();
|
|
1607 |
}
|
|
1608 |
#else
|
|
1609 |
QStringList newFiles = files;
|
|
1610 |
#endif
|
|
1611 |
qSort(newFiles.begin(), newFiles.end());
|
|
1612 |
QHash<QString, QFileSystemNode*>::const_iterator i = parentNode->children.constBegin();
|
|
1613 |
while (i != parentNode->children.constEnd()) {
|
|
1614 |
QStringList::iterator iterator;
|
|
1615 |
iterator = qBinaryFind(newFiles.begin(), newFiles.end(),
|
|
1616 |
#if defined(Q_OS_SYMBIAN)
|
|
1617 |
i.value()->fileName.toLower());
|
|
1618 |
#else
|
|
1619 |
i.value()->fileName);
|
|
1620 |
#endif
|
|
1621 |
if (iterator == newFiles.end()) {
|
|
1622 |
toRemove.append(i.value()->fileName);
|
|
1623 |
}
|
|
1624 |
++i;
|
|
1625 |
}
|
|
1626 |
for (int i = 0 ; i < toRemove.count() ; ++i )
|
|
1627 |
removeNode(parentNode, toRemove[i]);
|
|
1628 |
}
|
|
1629 |
|
|
1630 |
/*!
|
|
1631 |
\internal
|
|
1632 |
|
|
1633 |
Adds a new file to the children of parentNode
|
|
1634 |
|
|
1635 |
*WARNING* this will change the count of children
|
|
1636 |
*/
|
|
1637 |
QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo& info)
|
|
1638 |
{
|
|
1639 |
// In the common case, itemLocation == count() so check there first
|
|
1640 |
QFileSystemModelPrivate::QFileSystemNode *node = new QFileSystemModelPrivate::QFileSystemNode(fileName, parentNode);
|
|
1641 |
#ifndef QT_NO_FILESYSTEMWATCHER
|
|
1642 |
node->populate(info);
|
|
1643 |
#endif
|
|
1644 |
parentNode->children.insert(fileName, node);
|
|
1645 |
return node;
|
|
1646 |
}
|
|
1647 |
|
|
1648 |
/*!
|
|
1649 |
\internal
|
|
1650 |
|
|
1651 |
File at parentNode->children(itemLocation) has been removed, remove from the lists
|
|
1652 |
and emit signals if necessary
|
|
1653 |
|
|
1654 |
*WARNING* this will change the count of children and could change visibleChildren
|
|
1655 |
*/
|
|
1656 |
void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode, const QString& name)
|
|
1657 |
{
|
|
1658 |
Q_Q(QFileSystemModel);
|
|
1659 |
QModelIndex parent = index(parentNode);
|
|
1660 |
bool indexHidden = isHiddenByFilter(parentNode, parent);
|
|
1661 |
|
|
1662 |
int vLocation = parentNode->visibleLocation(name);
|
|
1663 |
if (vLocation >= 0 && !indexHidden)
|
|
1664 |
q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
|
|
1665 |
translateVisibleLocation(parentNode, vLocation));
|
|
1666 |
QFileSystemNode * node = parentNode->children.take(name);
|
|
1667 |
delete node;
|
|
1668 |
// cleanup sort files after removing rather then re-sorting which is O(n)
|
|
1669 |
if (vLocation >= 0)
|
|
1670 |
parentNode->visibleChildren.removeAt(vLocation);
|
|
1671 |
if (vLocation >= 0 && !indexHidden)
|
|
1672 |
q->endRemoveRows();
|
|
1673 |
}
|
|
1674 |
|
|
1675 |
/*
|
|
1676 |
\internal
|
|
1677 |
Helper functor used by addVisibleFiles()
|
|
1678 |
*/
|
|
1679 |
class QFileSystemModelVisibleFinder
|
|
1680 |
{
|
|
1681 |
public:
|
|
1682 |
inline QFileSystemModelVisibleFinder(QFileSystemModelPrivate::QFileSystemNode *node, QFileSystemModelSorter *sorter) : parentNode(node), sorter(sorter) {}
|
|
1683 |
|
|
1684 |
bool operator()(const QString &, QString r) const
|
|
1685 |
{
|
|
1686 |
return sorter->compareNodes(parentNode->children.value(name), parentNode->children.value(r));
|
|
1687 |
}
|
|
1688 |
|
|
1689 |
QString name;
|
|
1690 |
private:
|
|
1691 |
QFileSystemModelPrivate::QFileSystemNode *parentNode;
|
|
1692 |
QFileSystemModelSorter *sorter;
|
|
1693 |
};
|
|
1694 |
|
|
1695 |
/*!
|
|
1696 |
\internal
|
|
1697 |
|
|
1698 |
File at parentNode->children(itemLocation) was not visible before, but now should be
|
|
1699 |
and emit signals if necessary.
|
|
1700 |
|
|
1701 |
*WARNING* this will change the visible count
|
|
1702 |
*/
|
|
1703 |
void QFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles)
|
|
1704 |
{
|
|
1705 |
Q_Q(QFileSystemModel);
|
|
1706 |
QModelIndex parent = index(parentNode);
|
|
1707 |
bool indexHidden = isHiddenByFilter(parentNode, parent);
|
|
1708 |
if (!indexHidden) {
|
|
1709 |
q->beginInsertRows(parent, parentNode->visibleChildren.count() , parentNode->visibleChildren.count() + newFiles.count() - 1);
|
|
1710 |
}
|
|
1711 |
|
|
1712 |
if (parentNode->dirtyChildrenIndex == -1)
|
|
1713 |
parentNode->dirtyChildrenIndex = parentNode->visibleChildren.count();
|
|
1714 |
|
|
1715 |
for (int i = 0; i < newFiles.count(); ++i) {
|
|
1716 |
parentNode->visibleChildren.append(newFiles.at(i));
|
|
1717 |
parentNode->children[newFiles.at(i)]->isVisible = true;
|
|
1718 |
}
|
|
1719 |
if (!indexHidden)
|
|
1720 |
q->endInsertRows();
|
|
1721 |
}
|
|
1722 |
|
|
1723 |
/*!
|
|
1724 |
\internal
|
|
1725 |
|
|
1726 |
File was visible before, but now should NOT be
|
|
1727 |
|
|
1728 |
*WARNING* this will change the visible count
|
|
1729 |
*/
|
|
1730 |
void QFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode, int vLocation)
|
|
1731 |
{
|
|
1732 |
Q_Q(QFileSystemModel);
|
|
1733 |
if (vLocation == -1)
|
|
1734 |
return;
|
|
1735 |
QModelIndex parent = index(parentNode);
|
|
1736 |
bool indexHidden = isHiddenByFilter(parentNode, parent);
|
|
1737 |
if (!indexHidden)
|
|
1738 |
q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
|
|
1739 |
translateVisibleLocation(parentNode, vLocation));
|
|
1740 |
parentNode->children[parentNode->visibleChildren.at(vLocation)]->isVisible = false;
|
|
1741 |
parentNode->visibleChildren.removeAt(vLocation);
|
|
1742 |
if (!indexHidden)
|
|
1743 |
q->endRemoveRows();
|
|
1744 |
}
|
|
1745 |
|
|
1746 |
/*!
|
|
1747 |
\internal
|
|
1748 |
|
|
1749 |
The thread has received new information about files,
|
|
1750 |
update and emit dataChanged if it has actually changed.
|
|
1751 |
*/
|
|
1752 |
void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo> > &updates)
|
|
1753 |
{
|
|
1754 |
Q_Q(QFileSystemModel);
|
|
1755 |
QVector<QString> rowsToUpdate;
|
|
1756 |
QStringList newFiles;
|
|
1757 |
QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path, false);
|
|
1758 |
QModelIndex parentIndex = index(parentNode);
|
|
1759 |
for (int i = 0; i < updates.count(); ++i) {
|
|
1760 |
QString fileName = updates.at(i).first;
|
|
1761 |
Q_ASSERT(!fileName.isEmpty());
|
|
1762 |
QExtendedInformation info = fileInfoGatherer.getInfo(updates.at(i).second);
|
|
1763 |
bool previouslyHere = parentNode->children.contains(fileName);
|
|
1764 |
if (!previouslyHere) {
|
|
1765 |
addNode(parentNode, fileName, info.fileInfo());
|
|
1766 |
}
|
|
1767 |
QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName);
|
|
1768 |
bool isCaseSensitive = parentNode->caseSensitive();
|
|
1769 |
if (isCaseSensitive) {
|
|
1770 |
if (node->fileName != fileName)
|
|
1771 |
continue;
|
|
1772 |
} else {
|
|
1773 |
if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0)
|
|
1774 |
continue;
|
|
1775 |
}
|
|
1776 |
if (isCaseSensitive) {
|
|
1777 |
Q_ASSERT(node->fileName == fileName);
|
|
1778 |
} else {
|
|
1779 |
node->fileName = fileName;
|
|
1780 |
}
|
|
1781 |
|
|
1782 |
if (info.size() == -1) {
|
|
1783 |
removeNode(parentNode, fileName);
|
|
1784 |
continue;
|
|
1785 |
}
|
|
1786 |
if (*node != info ) {
|
|
1787 |
node->populate(info);
|
|
1788 |
bypassFilters.remove(node);
|
|
1789 |
// brand new information.
|
|
1790 |
if (filtersAcceptsNode(node)) {
|
|
1791 |
if (!node->isVisible) {
|
|
1792 |
newFiles.append(fileName);
|
|
1793 |
} else {
|
|
1794 |
rowsToUpdate.append(fileName);
|
|
1795 |
}
|
|
1796 |
} else {
|
|
1797 |
if (node->isVisible) {
|
|
1798 |
int visibleLocation = parentNode->visibleLocation(fileName);
|
|
1799 |
removeVisibleFile(parentNode, visibleLocation);
|
|
1800 |
} else {
|
|
1801 |
// The file is not visible, don't do anything
|
|
1802 |
}
|
|
1803 |
}
|
|
1804 |
}
|
|
1805 |
}
|
|
1806 |
|
|
1807 |
// bundle up all of the changed signals into as few as possible.
|
|
1808 |
qSort(rowsToUpdate.begin(), rowsToUpdate.end());
|
|
1809 |
QString min;
|
|
1810 |
QString max;
|
|
1811 |
for (int i = 0; i < rowsToUpdate.count(); ++i) {
|
|
1812 |
QString value = rowsToUpdate.at(i);
|
|
1813 |
//##TODO is there a way to bundle signals with QString as the content of the list?
|
|
1814 |
/*if (min.isEmpty()) {
|
|
1815 |
min = value;
|
|
1816 |
if (i != rowsToUpdate.count() - 1)
|
|
1817 |
continue;
|
|
1818 |
}
|
|
1819 |
if (i != rowsToUpdate.count() - 1) {
|
|
1820 |
if ((value == min + 1 && max.isEmpty()) || value == max + 1) {
|
|
1821 |
max = value;
|
|
1822 |
continue;
|
|
1823 |
}
|
|
1824 |
}*/
|
|
1825 |
max = value;
|
|
1826 |
min = value;
|
|
1827 |
int visibleMin = parentNode->visibleLocation(min);
|
|
1828 |
int visibleMax = parentNode->visibleLocation(max);
|
|
1829 |
if (visibleMin >= 0
|
|
1830 |
&& visibleMin < parentNode->visibleChildren.count()
|
|
1831 |
&& parentNode->visibleChildren.at(visibleMin) == min
|
|
1832 |
&& visibleMax >= 0) {
|
|
1833 |
QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMin), 0, parentIndex);
|
|
1834 |
QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMax), 3, parentIndex);
|
|
1835 |
emit q->dataChanged(bottom, top);
|
|
1836 |
}
|
|
1837 |
|
|
1838 |
/*min = QString();
|
|
1839 |
max = QString();*/
|
|
1840 |
}
|
|
1841 |
|
|
1842 |
if (newFiles.count() > 0) {
|
|
1843 |
addVisibleFiles(parentNode, newFiles);
|
|
1844 |
}
|
|
1845 |
|
|
1846 |
if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) {
|
|
1847 |
forceSort = true;
|
|
1848 |
delayedSort();
|
|
1849 |
}
|
|
1850 |
}
|
|
1851 |
|
|
1852 |
/*!
|
|
1853 |
\internal
|
|
1854 |
*/
|
|
1855 |
void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName)
|
|
1856 |
{
|
|
1857 |
resolvedSymLinks[fileName] = resolvedName;
|
|
1858 |
}
|
|
1859 |
|
|
1860 |
/*!
|
|
1861 |
\internal
|
|
1862 |
*/
|
|
1863 |
void QFileSystemModelPrivate::init()
|
|
1864 |
{
|
|
1865 |
Q_Q(QFileSystemModel);
|
|
1866 |
qRegisterMetaType<QList<QPair<QString,QFileInfo> > >("QList<QPair<QString,QFileInfo> >");
|
|
1867 |
q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(const QString &, const QStringList &)),
|
|
1868 |
q, SLOT(_q_directoryChanged(const QString &, const QStringList &)));
|
|
1869 |
q->connect(&fileInfoGatherer, SIGNAL(updates(const QString &, const QList<QPair<QString, QFileInfo> > &)),
|
|
1870 |
q, SLOT(_q_fileSystemChanged(const QString &, const QList<QPair<QString, QFileInfo> > &)));
|
|
1871 |
q->connect(&fileInfoGatherer, SIGNAL(nameResolved(const QString &, const QString &)),
|
|
1872 |
q, SLOT(_q_resolvedName(const QString &, const QString &)));
|
|
1873 |
q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection);
|
|
1874 |
}
|
|
1875 |
|
|
1876 |
/*!
|
|
1877 |
\internal
|
|
1878 |
|
|
1879 |
Returns false if node doesn't pass the filters otherwise true
|
|
1880 |
|
|
1881 |
QDir::Modified is not supported
|
|
1882 |
QDir::Drives is not supported
|
|
1883 |
*/
|
|
1884 |
bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) const
|
|
1885 |
{
|
|
1886 |
// always accept drives
|
|
1887 |
if (node->parent == &root || bypassFilters.contains(node))
|
|
1888 |
return true;
|
|
1889 |
|
|
1890 |
// If we don't know anything yet don't accept it
|
|
1891 |
if (!node->hasInformation())
|
|
1892 |
return false;
|
|
1893 |
|
|
1894 |
const bool filterPermissions = ((filters & QDir::PermissionMask)
|
|
1895 |
&& (filters & QDir::PermissionMask) != QDir::PermissionMask);
|
|
1896 |
const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
|
|
1897 |
const bool hideFiles = !(filters & QDir::Files);
|
|
1898 |
const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
|
|
1899 |
const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
|
|
1900 |
const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
|
|
1901 |
const bool hideHidden = !(filters & QDir::Hidden);
|
|
1902 |
const bool hideSystem = !(filters & QDir::System);
|
|
1903 |
const bool hideSymlinks = (filters & QDir::NoSymLinks);
|
|
1904 |
const bool hideDotAndDotDot = (filters & QDir::NoDotAndDotDot);
|
|
1905 |
|
|
1906 |
// Note that we match the behavior of entryList and not QFileInfo on this and this
|
|
1907 |
// incompatibility won't be fixed until Qt 5 at least
|
|
1908 |
bool isDotOrDot = ( (node->fileName == QLatin1String(".")
|
|
1909 |
|| node->fileName == QLatin1String("..")));
|
|
1910 |
if ( (hideHidden && (!isDotOrDot && node->isHidden()))
|
|
1911 |
|| (hideSystem && node->isSystem())
|
|
1912 |
|| (hideDirs && node->isDir())
|
|
1913 |
|| (hideFiles && node->isFile())
|
|
1914 |
|| (hideSymlinks && node->isSymLink())
|
|
1915 |
|| (hideReadable && node->isReadable())
|
|
1916 |
|| (hideWritable && node->isWritable())
|
|
1917 |
|| (hideExecutable && node->isExecutable())
|
|
1918 |
|| (hideDotAndDotDot && isDotOrDot))
|
|
1919 |
return false;
|
|
1920 |
|
|
1921 |
return nameFilterDisables || passNameFilters(node);
|
|
1922 |
}
|
|
1923 |
|
|
1924 |
/*
|
|
1925 |
\internal
|
|
1926 |
|
|
1927 |
Returns true if node passes the name filters and should be visible.
|
|
1928 |
*/
|
|
1929 |
bool QFileSystemModelPrivate::passNameFilters(const QFileSystemNode *node) const
|
|
1930 |
{
|
|
1931 |
#ifndef QT_NO_REGEXP
|
|
1932 |
if (nameFilters.isEmpty())
|
|
1933 |
return true;
|
|
1934 |
|
|
1935 |
// Check the name regularexpression filters
|
|
1936 |
if (!(node->isDir() && (filters & QDir::AllDirs))) {
|
|
1937 |
for (int i = 0; i < nameFilters.size(); ++i) {
|
|
1938 |
if (nameFilters.at(i).exactMatch(node->fileName))
|
|
1939 |
return true;
|
|
1940 |
}
|
|
1941 |
return false;
|
|
1942 |
}
|
|
1943 |
#endif
|
|
1944 |
return true;
|
|
1945 |
}
|
|
1946 |
|
|
1947 |
QT_END_NAMESPACE
|
|
1948 |
|
|
1949 |
#include "moc_qfilesystemmodel.cpp"
|
|
1950 |
|
|
1951 |
#endif // QT_NO_FILESYSTEMMODEL
|