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