|
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 |