source: trunk/src/gui/dialogs/qfilesystemmodel.cpp@ 64

Last change on this file since 64 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 60.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
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 <windows.h>
53#endif
54
55QT_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
165bool 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*/
198QFileSystemModel::QFileSystemModel(QObject *parent)
199 : QAbstractItemModel(*new QFileSystemModelPrivate, parent)
200{
201 Q_D(QFileSystemModel);
202 d->init();
203}
204
205/*!
206 \internal
207*/
208QFileSystemModel::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*/
218QFileSystemModel::~QFileSystemModel()
219{
220}
221
222/*!
223 \reimp
224*/
225QModelIndex 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*/
249QModelIndex 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 */
264QFileSystemModelPrivate::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
274static 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 QT_WA({
300 WIN32_FIND_DATAW findData;
301 h = ::FindFirstFileW((wchar_t *)longSection.utf16(), &findData);
302 if (h != INVALID_HANDLE_VALUE)
303 longPath.append(QString::fromUtf16((ushort*)findData.cFileName));
304 } , {
305 WIN32_FIND_DATAA findData;
306 h = ::FindFirstFileA(section.toLocal8Bit(), &findData);
307 if (h != INVALID_HANDLE_VALUE)
308 longPath.append(QString::fromLocal8Bit(findData.cFileName));
309 });
310 if (h == INVALID_HANDLE_VALUE) {
311 longPath.append(section);
312 break;
313 } else {
314 ::FindClose(h);
315 }
316 }
317 if (it != constEnd)
318 longPath.append(*it);
319 else
320 break;
321 }
322 ++it;
323 if (isSep && it == constEnd) // break out if the last character is a separator
324 break;
325 ++i;
326 } while (true);
327 return longPath;
328}
329#endif
330
331/*!
332 \internal
333
334 Given a path return the matching QFileSystemNode or &root if invalid
335*/
336QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QString &path, bool fetch) const
337{
338 Q_Q(const QFileSystemModel);
339 Q_UNUSED(q);
340 if (path.isEmpty() || path == myComputer() || path.startsWith(QLatin1String(":")))
341 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
342
343 // Construct the nodes up to the new root path if they need to be built
344 QString absolutePath;
345#ifdef Q_OS_WIN
346 QString longPath = qt_GetLongPathName(path);
347#else
348 QString longPath = path;
349#endif
350 if (longPath == rootDir.path())
351 absolutePath = rootDir.absolutePath();
352 else
353 absolutePath = QDir(longPath).absolutePath();
354
355 // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const?
356 QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts);
357 if ((pathElements.isEmpty())
358#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE)
359 && QDir::fromNativeSeparators(longPath) != QLatin1String("/")
360#endif
361 )
362 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
363 QModelIndex index = QModelIndex(); // start with "My Computer"
364#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
365 if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path
366 QString host = QLatin1String("\\\\") + pathElements.first();
367 if (absolutePath == QDir::fromNativeSeparators(host))
368 absolutePath.append(QLatin1Char('/'));
369 if (longPath.endsWith(QLatin1Char('/')) && !absolutePath.endsWith(QLatin1Char('/')))
370 absolutePath.append(QLatin1Char('/'));
371 int r = 0;
372 QFileSystemModelPrivate::QFileSystemNode *rootNode = const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
373 if (!root.children.contains(host.toLower())) {
374 if (pathElements.count() == 1 && !absolutePath.endsWith(QLatin1Char('/')))
375 return rootNode;
376 QFileInfo info(host);
377 if (!info.exists())
378 return rootNode;
379 QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
380 p->addNode(rootNode, host,info);
381 p->addVisibleFiles(rootNode, QStringList(host));
382 }
383 r = rootNode->visibleLocation(host);
384 r = translateVisibleLocation(rootNode, r);
385 index = q->index(r, 0, QModelIndex());
386 pathElements.pop_front();
387 } else {
388 if (!pathElements.at(0).contains(QLatin1String(":")))
389 pathElements.prepend(QDir(longPath).rootPath());
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*/
466void 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*/
490bool 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 */
505qint64 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 */
516QString 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 */
527QDateTime 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*/
538QModelIndex 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*/
564QModelIndex 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*/
583bool 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 */
600bool 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 */
610void 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*/
625int 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*/
641int 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 */
651QVariant 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*/
666QVariant 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*/
716QString 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
735QString 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*/
757QString 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*/
772QString 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*/
782QString 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*/
799QIcon 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*/
809bool 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*/
871QVariant 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 case Qt::TextAlignmentRole:
884 return Qt::AlignLeft;
885 }
886
887 if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
888 return QAbstractItemModel::headerData(section, orientation, role);
889
890 QString returnValue;
891 switch (section) {
892 case 0: returnValue = tr("Name");
893 break;
894 case 1: returnValue = tr("Size");
895 break;
896 case 2: returnValue =
897#ifdef Q_OS_MAC
898 tr("Kind", "Match OS X Finder");
899#else
900 tr("Type", "All other platforms");
901#endif
902 break;
903 // Windows - Type
904 // OS X - Kind
905 // Konqueror - File Type
906 // Nautilus - Type
907 case 3: returnValue = tr("Date Modified");
908 break;
909 default: return QVariant();
910 }
911 return returnValue;
912}
913
914/*!
915 \reimp
916*/
917Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const
918{
919 Q_D(const QFileSystemModel);
920 Qt::ItemFlags flags = QAbstractItemModel::flags(index);
921 if (!index.isValid())
922 return flags;
923
924 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index);
925 if (d->nameFilterDisables && !d->passNameFilters(indexNode)) {
926 flags &= ~Qt::ItemIsEnabled;
927 // ### TODO you shouldn't be able to set this as the current item, task 119433
928 return flags;
929 }
930
931 flags |= Qt::ItemIsDragEnabled;
932 if (d->readOnly)
933 return flags;
934 if ((index.column() == 0) && indexNode->permissions() & QFile::WriteUser) {
935 flags |= Qt::ItemIsEditable;
936 if (indexNode->isDir())
937 flags |= Qt::ItemIsDropEnabled;
938 }
939 return flags;
940}
941
942/*!
943 \internal
944*/
945void QFileSystemModelPrivate::_q_performDelayedSort()
946{
947 Q_Q(QFileSystemModel);
948 q->sort(sortColumn, sortOrder);
949}
950
951static inline QChar getNextChar(const QString &s, int location)
952{
953 return (location < s.length()) ? s.at(location) : QChar();
954}
955
956/*!
957 Natural number sort, skips spaces.
958
959 Examples:
960 1, 2, 10, 55, 100
961 01.jpg, 2.jpg, 10.jpg
962
963 Note on the algorithm:
964 Only as many characters as necessary are looked at and at most they all
965 are looked at once.
966
967 Slower then QString::compare() (of course)
968 */
969int QFileSystemModelPrivate::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
970{
971 for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) {
972 // skip spaces, tabs and 0's
973 QChar c1 = getNextChar(s1, l1);
974 while (c1.isSpace())
975 c1 = getNextChar(s1, ++l1);
976 QChar c2 = getNextChar(s2, l2);
977 while (c2.isSpace())
978 c2 = getNextChar(s2, ++l2);
979
980 if (c1.isDigit() && c2.isDigit()) {
981 while (c1.digitValue() == 0)
982 c1 = getNextChar(s1, ++l1);
983 while (c2.digitValue() == 0)
984 c2 = getNextChar(s2, ++l2);
985
986 int lookAheadLocation1 = l1;
987 int lookAheadLocation2 = l2;
988 int currentReturnValue = 0;
989 // find the last digit, setting currentReturnValue as we go if it isn't equal
990 for (
991 QChar lookAhead1 = c1, lookAhead2 = c2;
992 (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
993 lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
994 lookAhead2 = getNextChar(s2, ++lookAheadLocation2)
995 ) {
996 bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
997 bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
998 if (!is1ADigit && !is2ADigit)
999 break;
1000 if (!is1ADigit)
1001 return -1;
1002 if (!is2ADigit)
1003 return 1;
1004 if (currentReturnValue == 0) {
1005 if (lookAhead1 < lookAhead2) {
1006 currentReturnValue = -1;
1007 } else if (lookAhead1 > lookAhead2) {
1008 currentReturnValue = 1;
1009 }
1010 }
1011 }
1012 if (currentReturnValue != 0)
1013 return currentReturnValue;
1014 }
1015
1016 if (cs == Qt::CaseInsensitive) {
1017 if (!c1.isLower()) c1 = c1.toLower();
1018 if (!c2.isLower()) c2 = c2.toLower();
1019 }
1020 int r = QString::localeAwareCompare(c1, c2);
1021 if (r < 0)
1022 return -1;
1023 if (r > 0)
1024 return 1;
1025 }
1026 // The two strings are the same (02 == 2) so fall back to the normal sort
1027 return QString::compare(s1, s2, cs);
1028}
1029
1030/*
1031 \internal
1032 Helper functor used by sort()
1033*/
1034class QFileSystemModelSorter
1035{
1036public:
1037 inline QFileSystemModelSorter(int column) : sortColumn(column) {}
1038
1039 bool compareNodes(const QFileSystemModelPrivate::QFileSystemNode *l,
1040 const QFileSystemModelPrivate::QFileSystemNode *r) const
1041 {
1042 switch (sortColumn) {
1043 case 0: {
1044#ifndef Q_OS_MAC
1045 // place directories before files
1046 bool left = l->isDir();
1047 bool right = r->isDir();
1048 if (left ^ right)
1049 return left;
1050#endif
1051 return QFileSystemModelPrivate::naturalCompare(l->fileName,
1052 r->fileName, Qt::CaseInsensitive) < 0;
1053 }
1054 case 1:
1055 // Directories go first
1056 if (l->isDir() && !r->isDir())
1057 return true;
1058 return l->size() < r->size();
1059 case 2:
1060 return l->type() < r->type();
1061 case 3:
1062 return l->lastModified() < r->lastModified();
1063 }
1064 Q_ASSERT(false);
1065 return false;
1066 }
1067
1068 bool operator()(const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &l,
1069 const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &r) const
1070 {
1071 return compareNodes(l.first, r.first);
1072 }
1073
1074
1075private:
1076 int sortColumn;
1077};
1078
1079/*
1080 \internal
1081
1082 Sort all of the children of parent
1083*/
1084void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent)
1085{
1086 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent);
1087 if (indexNode->children.count() == 0)
1088 return;
1089
1090 QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > values;
1091 QHash<QString, QFileSystemNode *>::const_iterator iterator;
1092 int i = 0;
1093 for(iterator = indexNode->children.begin() ; iterator != indexNode->children.end() ; ++iterator) {
1094 if (filtersAcceptsNode(iterator.value())) {
1095 values.append(QPair<QFileSystemModelPrivate::QFileSystemNode*, int>((iterator.value()), i));
1096 } else {
1097 iterator.value()->isVisible = false;
1098 }
1099 i++;
1100 }
1101 QFileSystemModelSorter ms(column);
1102 qStableSort(values.begin(), values.end(), ms);
1103 // First update the new visible list
1104 indexNode->visibleChildren.clear();
1105 for (int i = 0; i < values.count(); ++i) {
1106 indexNode->visibleChildren.append(values.at(i).first->fileName);
1107 values.at(i).first->isVisible = true;
1108 }
1109}
1110
1111/*!
1112 \reimp
1113*/
1114void QFileSystemModel::sort(int column, Qt::SortOrder order)
1115{
1116 Q_D(QFileSystemModel);
1117 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
1118 return;
1119
1120 emit layoutAboutToBeChanged();
1121 QModelIndexList oldList = persistentIndexList();
1122 QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > oldNodes;
1123 for (int i = 0; i < oldList.count(); ++i) {
1124 QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldList.at(i)), oldList.at(i).column());
1125 oldNodes.append(pair);
1126 }
1127
1128 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
1129 //we sort only from where we are, don't need to sort all the model
1130 d->sortChildren(column, index(rootPath()));
1131 d->sortColumn = column;
1132 d->forceSort = false;
1133 }
1134 d->sortOrder = order;
1135
1136 QModelIndexList newList;
1137 for (int i = 0; i < oldNodes.count(); ++i) {
1138 QModelIndex idx = d->index(oldNodes.at(i).first);
1139 idx = idx.sibling(idx.row(), oldNodes.at(i).second);
1140 newList.append(idx);
1141 }
1142 changePersistentIndexList(oldList, newList);
1143 emit layoutChanged();
1144}
1145
1146/*!
1147 Returns a list of MIME types that can be used to describe a list of items
1148 in the model.
1149*/
1150QStringList QFileSystemModel::mimeTypes() const
1151{
1152 return QStringList(QLatin1String("text/uri-list"));
1153}
1154
1155/*!
1156 Returns an object that contains a serialized description of the specified
1157 \a indexes. The format used to describe the items corresponding to the
1158 indexes is obtained from the mimeTypes() function.
1159
1160 If the list of indexes is empty, 0 is returned rather than a serialized
1161 empty list.
1162*/
1163QMimeData *QFileSystemModel::mimeData(const QModelIndexList &indexes) const
1164{
1165 QList<QUrl> urls;
1166 QList<QModelIndex>::const_iterator it = indexes.begin();
1167 for (; it != indexes.end(); ++it)
1168 if ((*it).column() == 0)
1169 urls << QUrl::fromLocalFile(filePath(*it));
1170 QMimeData *data = new QMimeData();
1171 data->setUrls(urls);
1172 return data;
1173}
1174
1175/*!
1176 Handles the \a data supplied by a drag and drop operation that ended with
1177 the given \a action over the row in the model specified by the \a row and
1178 \a column and by the \a parent index.
1179
1180 \sa supportedDropActions()
1181*/
1182bool QFileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
1183 int row, int column, const QModelIndex &parent)
1184{
1185 Q_UNUSED(row);
1186 Q_UNUSED(column);
1187 if (!parent.isValid() || isReadOnly())
1188 return false;
1189
1190 bool success = true;
1191 QString to = filePath(parent) + QDir::separator();
1192
1193 QList<QUrl> urls = data->urls();
1194 QList<QUrl>::const_iterator it = urls.constBegin();
1195
1196 switch (action) {
1197 case Qt::CopyAction:
1198 for (; it != urls.constEnd(); ++it) {
1199 QString path = (*it).toLocalFile();
1200 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
1201 }
1202 break;
1203 case Qt::LinkAction:
1204 for (; it != urls.constEnd(); ++it) {
1205 QString path = (*it).toLocalFile();
1206 success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
1207 }
1208 break;
1209 case Qt::MoveAction:
1210 for (; it != urls.constEnd(); ++it) {
1211 QString path = (*it).toLocalFile();
1212 success = QFile::copy(path, to + QFileInfo(path).fileName())
1213 && QFile::remove(path) && success;
1214 }
1215 break;
1216 default:
1217 return false;
1218 }
1219
1220 return success;
1221}
1222
1223/*!
1224 \reimp
1225*/
1226Qt::DropActions QFileSystemModel::supportedDropActions() const
1227{
1228 return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
1229}
1230
1231/*!
1232 Returns the path of the item stored in the model under the
1233 \a index given.
1234*/
1235QString QFileSystemModel::filePath(const QModelIndex &index) const
1236{
1237 Q_D(const QFileSystemModel);
1238 QString fullPath = d->filePath(index);
1239 QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index);
1240 if (dirNode->isSymLink() && d->fileInfoGatherer.resolveSymlinks()
1241 && d->resolvedSymLinks.contains(fullPath)
1242 && dirNode->isDir()) {
1243 QFileInfo resolvedInfo(fullPath);
1244 resolvedInfo = resolvedInfo.canonicalFilePath();
1245 if (resolvedInfo.exists())
1246 return resolvedInfo.filePath();
1247 }
1248 return fullPath;
1249}
1250
1251QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const
1252{
1253 Q_Q(const QFileSystemModel);
1254 Q_UNUSED(q);
1255 if (!index.isValid())
1256 return QString();
1257 Q_ASSERT(index.model() == q);
1258
1259 QStringList path;
1260 QModelIndex idx = index;
1261 while (idx.isValid()) {
1262 QFileSystemModelPrivate::QFileSystemNode *dirNode = node(idx);
1263 if (dirNode)
1264 path.prepend(dirNode->fileName);
1265 idx = idx.parent();
1266 }
1267 QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator()));
1268#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE)
1269 if ((fullPath.length() > 2) && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/'))
1270 fullPath = fullPath.mid(1);
1271#endif
1272 return fullPath;
1273}
1274
1275/*!
1276 Create a directory with the \a name in the \a parent model index.
1277*/
1278QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent, const QString &name)
1279{
1280 Q_D(QFileSystemModel);
1281 if (!parent.isValid())
1282 return parent;
1283
1284 QDir dir(filePath(parent));
1285 if (!dir.mkdir(name))
1286 return QModelIndex();
1287 QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);
1288 d->addNode(parentNode, name, QFileInfo());
1289 Q_ASSERT(parentNode->children.contains(name));
1290 QFileSystemModelPrivate::QFileSystemNode *node = parentNode->children[name];
1291 node->populate(d->fileInfoGatherer.getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name)));
1292 d->addVisibleFiles(parentNode, QStringList(name));
1293 return d->index(node);
1294}
1295
1296/*!
1297 Returns the complete OR-ed together combination of QFile::Permission for the \a index.
1298 */
1299QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index) const
1300{
1301 Q_D(const QFileSystemModel);
1302 QFile::Permissions p = d->node(index)->permissions();
1303 if (d->readOnly) {
1304 p ^= (QFile::WriteOwner | QFile::WriteUser
1305 | QFile::WriteGroup | QFile::WriteOther);
1306 }
1307 return p;
1308}
1309
1310/*!
1311 Sets the directory that is being watched by the model to \a newPath by
1312 installing a \l{QFileSystemWatcher}{file system watcher} on it. Any
1313 changes to files and directories within this directory will be
1314 reflected in the model.
1315
1316 If the path is changed, the rootPathChanged() signal will be emitted.
1317
1318 \note This function does not change the structure of the model or
1319 modify the data available to views. In other words, the "root" of
1320 the model is \e not changed to include only files and directories
1321 within the directory specified by \a newPath in the file system.
1322 */
1323QModelIndex QFileSystemModel::setRootPath(const QString &newPath)
1324{
1325 Q_D(QFileSystemModel);
1326#ifdef Q_OS_WIN
1327 QString longNewPath = QDir::fromNativeSeparators(qt_GetLongPathName(newPath));
1328#else
1329 QString longNewPath = newPath;
1330#endif
1331 QDir newPathDir(longNewPath);
1332 //we remove .. and . from the given path if exist
1333 if (!newPath.isEmpty()) {
1334 longNewPath = QDir::cleanPath(longNewPath);
1335 newPathDir.setPath(longNewPath);
1336 }
1337
1338 d->setRootPath = true;
1339
1340 //user don't ask for the root path ("") but the conversion failed
1341 if (!newPath.isEmpty() && longNewPath.isEmpty())
1342 return d->index(rootPath());
1343
1344 if (d->rootDir.path() == longNewPath)
1345 return d->index(rootPath());
1346
1347 bool showDrives = (longNewPath.isEmpty() || longNewPath == d->myComputer());
1348 if (!showDrives && !newPathDir.exists())
1349 return d->index(rootPath());
1350
1351 // We have a new valid root path
1352 d->rootDir = newPathDir;
1353 QModelIndex newRootIndex;
1354 if (showDrives) {
1355 // otherwise dir will become '.'
1356 d->rootDir.setPath(QLatin1String(""));
1357 } else {
1358 newRootIndex = d->index(newPathDir.path());
1359 }
1360 fetchMore(newRootIndex);
1361 emit rootPathChanged(longNewPath);
1362 d->forceSort = true;
1363 d->delayedSort();
1364 return newRootIndex;
1365}
1366
1367/*!
1368 The currently set root path
1369
1370 \sa rootDirectory()
1371*/
1372QString QFileSystemModel::rootPath() const
1373{
1374 Q_D(const QFileSystemModel);
1375 return d->rootDir.path();
1376}
1377
1378/*!
1379 The currently set directory
1380
1381 \sa rootPath()
1382*/
1383QDir QFileSystemModel::rootDirectory() const
1384{
1385 Q_D(const QFileSystemModel);
1386 QDir dir(d->rootDir);
1387 dir.setNameFilters(nameFilters());
1388 dir.setFilter(filter());
1389 return dir;
1390}
1391
1392/*!
1393 Sets the \a provider of file icons for the directory model.
1394*/
1395void QFileSystemModel::setIconProvider(QFileIconProvider *provider)
1396{
1397 Q_D(QFileSystemModel);
1398 d->fileInfoGatherer.setIconProvider(provider);
1399 qApp->processEvents();
1400 d->root.updateIcon(provider, QString());
1401}
1402
1403/*!
1404 Returns the file icon provider for this directory model.
1405*/
1406QFileIconProvider *QFileSystemModel::iconProvider() const
1407{
1408 Q_D(const QFileSystemModel);
1409 return d->fileInfoGatherer.iconProvider();
1410}
1411
1412/*!
1413 Sets the directory model's filter to that specified by \a filters.
1414
1415 Note that the filter you set should always include the QDir::AllDirs enum value,
1416 otherwise QFileSystemModel won't be able to read the directory structure.
1417
1418 \sa QDir::Filters
1419*/
1420void QFileSystemModel::setFilter(QDir::Filters filters)
1421{
1422 Q_D(QFileSystemModel);
1423 if (d->filters == filters)
1424 return;
1425 d->filters = filters;
1426 // CaseSensitivity might have changed
1427 setNameFilters(nameFilters());
1428 d->forceSort = true;
1429 d->delayedSort();
1430}
1431
1432/*!
1433 Returns the filter specified for the directory model.
1434
1435 If a filter has not been set, the default filter is QDir::AllEntries |
1436 QDir::NoDotAndDotDot | QDir::AllDirs.
1437
1438 \sa QDir::Filters
1439*/
1440QDir::Filters QFileSystemModel::filter() const
1441{
1442 Q_D(const QFileSystemModel);
1443 return d->filters;
1444}
1445
1446/*!
1447 \property QFileSystemModel::resolveSymlinks
1448 \brief Whether the directory model should resolve symbolic links
1449
1450 This is only relevant on operating systems that support symbolic links.
1451
1452 By default, this property is false.
1453*/
1454void QFileSystemModel::setResolveSymlinks(bool enable)
1455{
1456 Q_D(QFileSystemModel);
1457 d->fileInfoGatherer.setResolveSymlinks(enable);
1458}
1459
1460bool QFileSystemModel::resolveSymlinks() const
1461{
1462 Q_D(const QFileSystemModel);
1463 return d->fileInfoGatherer.resolveSymlinks();
1464}
1465
1466/*!
1467 \property QFileSystemModel::readOnly
1468 \brief Whether the directory model allows writing to the file system
1469
1470 If this property is set to false, the directory model will allow renaming, copying
1471 and deleting of files and directories.
1472
1473 This property is true by default
1474*/
1475void QFileSystemModel::setReadOnly(bool enable)
1476{
1477 Q_D(QFileSystemModel);
1478 d->readOnly = enable;
1479}
1480
1481bool QFileSystemModel::isReadOnly() const
1482{
1483 Q_D(const QFileSystemModel);
1484 return d->readOnly;
1485}
1486
1487/*!
1488 \property QFileSystemModel::nameFilterDisables
1489 \brief Whether files that don't pass the name filter are hidden or disabled
1490
1491 This property is true by default
1492*/
1493void QFileSystemModel::setNameFilterDisables(bool enable)
1494{
1495 Q_D(QFileSystemModel);
1496 if (d->nameFilterDisables == enable)
1497 return;
1498 d->nameFilterDisables = enable;
1499 d->forceSort = true;
1500 d->delayedSort();
1501}
1502
1503bool QFileSystemModel::nameFilterDisables() const
1504{
1505 Q_D(const QFileSystemModel);
1506 return d->nameFilterDisables;
1507}
1508
1509/*!
1510 Sets the name \a filters to apply against the existing files.
1511*/
1512void QFileSystemModel::setNameFilters(const QStringList &filters)
1513{
1514 // Prep the regexp's ahead of time
1515#ifndef QT_NO_REGEXP
1516 Q_D(QFileSystemModel);
1517
1518 if (!d->bypassFilters.isEmpty()) {
1519 // update the bypass filter to only bypass the stuff that must be kept around
1520 d->bypassFilters.clear();
1521 // We guarantee that rootPath will stick around
1522 QPersistentModelIndex root(index(rootPath()));
1523 QModelIndexList persistantList = persistentIndexList();
1524 for (int i = 0; i < persistantList.count(); ++i) {
1525 QFileSystemModelPrivate::QFileSystemNode *node;
1526 node = d->node(persistantList.at(i));
1527 while (node) {
1528 if (d->bypassFilters.contains(node))
1529 break;
1530 if (node->isDir())
1531 d->bypassFilters[node] = true;
1532 node = node->parent;
1533 }
1534 }
1535 }
1536
1537 d->nameFilters.clear();
1538 const Qt::CaseSensitivity caseSensitive =
1539 (filter() & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
1540 for (int i = 0; i < filters.size(); ++i) {
1541 d->nameFilters << QRegExp(filters.at(i), caseSensitive, QRegExp::Wildcard);
1542 }
1543 d->forceSort = true;
1544 d->delayedSort();
1545#endif
1546}
1547
1548/*!
1549 Returns a list of filters applied to the names in the model.
1550*/
1551QStringList QFileSystemModel::nameFilters() const
1552{
1553 Q_D(const QFileSystemModel);
1554 QStringList filters;
1555#ifndef QT_NO_REGEXP
1556 for (int i = 0; i < d->nameFilters.size(); ++i) {
1557 filters << d->nameFilters.at(i).pattern();
1558 }
1559#endif
1560 return filters;
1561}
1562
1563/*!
1564 \reimp
1565*/
1566bool QFileSystemModel::event(QEvent *event)
1567{
1568 Q_D(QFileSystemModel);
1569 if (event->type() == QEvent::LanguageChange) {
1570 d->root.retranslateStrings(d->fileInfoGatherer.iconProvider(), QString());
1571 return true;
1572 }
1573 return QAbstractItemModel::event(event);
1574}
1575
1576/*!
1577 \internal
1578
1579 Performed quick listing and see if any files have been added or removed,
1580 then fetch more information on visible files.
1581 */
1582void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files)
1583{
1584 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory, false);
1585 if (parentNode->children.count() == 0)
1586 return;
1587 QStringList toRemove;
1588 QStringList newFiles = files;
1589 qSort(newFiles.begin(), newFiles.end());
1590 QHash<QString, QFileSystemNode*>::const_iterator i = parentNode->children.constBegin();
1591 while (i != parentNode->children.constEnd()) {
1592 QStringList::iterator iterator;
1593 iterator = qBinaryFind(newFiles.begin(), newFiles.end(), i.value()->fileName);
1594 if (iterator == newFiles.end()) {
1595 toRemove.append(i.value()->fileName);
1596 }
1597 ++i;
1598 }
1599 for (int i = 0 ; i < toRemove.count() ; ++i )
1600 removeNode(parentNode, toRemove[i]);
1601}
1602
1603/*!
1604 \internal
1605
1606 Adds a new file to the children of parentNode
1607
1608 *WARNING* this will change the count of children
1609*/
1610QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo& info)
1611{
1612 // In the common case, itemLocation == count() so check there first
1613 QFileSystemModelPrivate::QFileSystemNode *node = new QFileSystemModelPrivate::QFileSystemNode(fileName, parentNode);
1614#ifndef QT_NO_FILESYSTEMWATCHER
1615 node->populate(info);
1616#endif
1617 parentNode->children.insert(fileName, node);
1618 return node;
1619}
1620
1621/*!
1622 \internal
1623
1624 File at parentNode->children(itemLocation) has been removed, remove from the lists
1625 and emit signals if necessary
1626
1627 *WARNING* this will change the count of children and could change visibleChildren
1628 */
1629void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode, const QString& name)
1630{
1631 Q_Q(QFileSystemModel);
1632 QModelIndex parent = index(parentNode);
1633 bool indexHidden = isHiddenByFilter(parentNode, parent);
1634
1635 int vLocation = parentNode->visibleLocation(name);
1636 if (vLocation >= 0 && !indexHidden)
1637 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1638 translateVisibleLocation(parentNode, vLocation));
1639 QFileSystemNode * node = parentNode->children.take(name);
1640 delete node;
1641 // cleanup sort files after removing rather then re-sorting which is O(n)
1642 if (vLocation >= 0)
1643 parentNode->visibleChildren.removeAt(vLocation);
1644 if (vLocation >= 0 && !indexHidden)
1645 q->endRemoveRows();
1646}
1647
1648/*
1649 \internal
1650 Helper functor used by addVisibleFiles()
1651*/
1652class QFileSystemModelVisibleFinder
1653{
1654public:
1655 inline QFileSystemModelVisibleFinder(QFileSystemModelPrivate::QFileSystemNode *node, QFileSystemModelSorter *sorter) : parentNode(node), sorter(sorter) {}
1656
1657 bool operator()(const QString &, QString r) const
1658 {
1659 return sorter->compareNodes(parentNode->children.value(name), parentNode->children.value(r));
1660 }
1661
1662 QString name;
1663private:
1664 QFileSystemModelPrivate::QFileSystemNode *parentNode;
1665 QFileSystemModelSorter *sorter;
1666};
1667
1668/*!
1669 \internal
1670
1671 File at parentNode->children(itemLocation) was not visible before, but now should be
1672 and emit signals if necessary.
1673
1674 *WARNING* this will change the visible count
1675 */
1676void QFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles)
1677{
1678 Q_Q(QFileSystemModel);
1679 QModelIndex parent = index(parentNode);
1680 bool indexHidden = isHiddenByFilter(parentNode, parent);
1681 if (!indexHidden) {
1682 q->beginInsertRows(parent, parentNode->visibleChildren.count() , parentNode->visibleChildren.count() + newFiles.count() - 1);
1683 }
1684 for (int i = 0; i < newFiles.count(); ++i) {
1685 parentNode->visibleChildren.append(newFiles.at(i));
1686 parentNode->children[newFiles.at(i)]->isVisible = true;
1687 }
1688 if (!indexHidden)
1689 q->endInsertRows();
1690}
1691
1692/*!
1693 \internal
1694
1695 File was visible before, but now should NOT be
1696
1697 *WARNING* this will change the visible count
1698 */
1699void QFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode, int vLocation)
1700{
1701 Q_Q(QFileSystemModel);
1702 if (vLocation == -1)
1703 return;
1704 QModelIndex parent = index(parentNode);
1705 bool indexHidden = isHiddenByFilter(parentNode, parent);
1706 if (!indexHidden)
1707 q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation),
1708 translateVisibleLocation(parentNode, vLocation));
1709 parentNode->children[parentNode->visibleChildren.at(vLocation)]->isVisible = false;
1710 parentNode->visibleChildren.removeAt(vLocation);
1711 if (!indexHidden)
1712 q->endRemoveRows();
1713}
1714
1715/*!
1716 \internal
1717
1718 The thread has received new information about files,
1719 update and emit dataChanged if it has actually changed.
1720 */
1721void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo> > &updates)
1722{
1723 Q_Q(QFileSystemModel);
1724 QVector<QString> rowsToUpdate;
1725 QStringList newFiles;
1726 QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path, false);
1727 QModelIndex parentIndex = index(parentNode);
1728 for (int i = 0; i < updates.count(); ++i) {
1729 QString fileName = updates.at(i).first;
1730 Q_ASSERT(!fileName.isEmpty());
1731 QExtendedInformation info = fileInfoGatherer.getInfo(updates.at(i).second);
1732 bool previouslyHere = parentNode->children.contains(fileName);
1733 if (!previouslyHere) {
1734 addNode(parentNode, fileName, info.fileInfo());
1735 }
1736 QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName);
1737 bool isCaseSensitive = parentNode->caseSensitive();
1738 if (isCaseSensitive) {
1739 if (node->fileName != fileName)
1740 continue;
1741 } else {
1742 if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0)
1743 continue;
1744 }
1745 if (isCaseSensitive) {
1746 Q_ASSERT(node->fileName == fileName);
1747 } else {
1748 node->fileName = fileName;
1749 }
1750
1751 if (info.size() == -1) {
1752 removeNode(parentNode, fileName);
1753 continue;
1754 }
1755 if (*node != info ) {
1756 node->populate(info);
1757 bypassFilters.remove(node);
1758 // brand new information.
1759 if (filtersAcceptsNode(node)) {
1760 if (!node->isVisible) {
1761 newFiles.append(fileName);
1762 } else {
1763 rowsToUpdate.append(fileName);
1764 }
1765 } else {
1766 if (node->isVisible) {
1767 int visibleLocation = parentNode->visibleLocation(fileName);
1768 removeVisibleFile(parentNode, visibleLocation);
1769 } else {
1770 // The file is not visible, don't do anything
1771 }
1772 }
1773 }
1774 }
1775
1776 // bundle up all of the changed signals into as few as possible.
1777 qSort(rowsToUpdate.begin(), rowsToUpdate.end());
1778 QString min;
1779 QString max;
1780 for (int i = 0; i < rowsToUpdate.count(); ++i) {
1781 QString value = rowsToUpdate.at(i);
1782 //##TODO is there a way to bundle signals with QString as the content of the list?
1783 /*if (min.isEmpty()) {
1784 min = value;
1785 if (i != rowsToUpdate.count() - 1)
1786 continue;
1787 }
1788 if (i != rowsToUpdate.count() - 1) {
1789 if ((value == min + 1 && max.isEmpty()) || value == max + 1) {
1790 max = value;
1791 continue;
1792 }
1793 }*/
1794 max = value;
1795 min = value;
1796 int visibleMin = parentNode->visibleLocation(min);
1797 int visibleMax = parentNode->visibleLocation(max);
1798 if (visibleMin >= 0
1799 && visibleMin < parentNode->visibleChildren.count()
1800 && parentNode->visibleChildren.at(visibleMin) == min
1801 && visibleMax >= 0) {
1802 QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMin), 0, parentIndex);
1803 QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMax), 3, parentIndex);
1804 emit q->dataChanged(bottom, top);
1805 }
1806
1807 /*min = QString();
1808 max = QString();*/
1809 }
1810
1811 if (newFiles.count() > 0) {
1812 addVisibleFiles(parentNode, newFiles);
1813 }
1814
1815 if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) {
1816 forceSort = true;
1817 delayedSort();
1818 }
1819}
1820
1821/*!
1822 \internal
1823*/
1824void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName)
1825{
1826 resolvedSymLinks[fileName] = resolvedName;
1827}
1828
1829/*!
1830 \internal
1831*/
1832void QFileSystemModelPrivate::init()
1833{
1834 Q_Q(QFileSystemModel);
1835 qRegisterMetaType<QList<QPair<QString,QFileInfo> > >("QList<QPair<QString,QFileInfo> >");
1836 q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(const QString &, const QStringList &)),
1837 q, SLOT(_q_directoryChanged(const QString &, const QStringList &)));
1838 q->connect(&fileInfoGatherer, SIGNAL(updates(const QString &, const QList<QPair<QString, QFileInfo> > &)),
1839 q, SLOT(_q_fileSystemChanged(const QString &, const QList<QPair<QString, QFileInfo> > &)));
1840 q->connect(&fileInfoGatherer, SIGNAL(nameResolved(const QString &, const QString &)),
1841 q, SLOT(_q_resolvedName(const QString &, const QString &)));
1842 q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection);
1843}
1844
1845/*!
1846 \internal
1847
1848 Returns false if node doesn't pass the filters otherwise true
1849
1850 QDir::Modified is not supported
1851 QDir::Drives is not supported
1852*/
1853bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) const
1854{
1855 // always accept drives
1856 if (node->parent == &root || bypassFilters.contains(node))
1857 return true;
1858
1859 // If we don't know anything yet don't accept it
1860 if (!node->hasInformation())
1861 return false;
1862
1863 const bool filterPermissions = ((filters & QDir::PermissionMask)
1864 && (filters & QDir::PermissionMask) != QDir::PermissionMask);
1865 const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
1866 const bool hideFiles = !(filters & QDir::Files);
1867 const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable));
1868 const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable));
1869 const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable));
1870 const bool hideHidden = !(filters & QDir::Hidden);
1871 const bool hideSystem = !(filters & QDir::System);
1872 const bool hideSymlinks = (filters & QDir::NoSymLinks);
1873 const bool hideDotAndDotDot = (filters & QDir::NoDotAndDotDot);
1874
1875 // Note that we match the behavior of entryList and not QFileInfo on this and this
1876 // incompatibility won't be fixed until Qt 5 at least
1877 bool isDotOrDot = ( (node->fileName == QLatin1String(".")
1878 || node->fileName == QLatin1String("..")));
1879 if ( (hideHidden && (!isDotOrDot && node->isHidden()))
1880 || (hideSystem && node->isSystem())
1881 || (hideDirs && node->isDir())
1882 || (hideFiles && node->isFile())
1883 || (hideSymlinks && node->isSymLink())
1884 || (hideReadable && node->isReadable())
1885 || (hideWritable && node->isWritable())
1886 || (hideExecutable && node->isExecutable())
1887 || (hideDotAndDotDot && isDotOrDot))
1888 return false;
1889
1890 return nameFilterDisables || passNameFilters(node);
1891}
1892
1893/*
1894 \internal
1895
1896 Returns true if node passes the name filters and should be visible.
1897 */
1898bool QFileSystemModelPrivate::passNameFilters(const QFileSystemNode *node) const
1899{
1900#ifndef QT_NO_REGEXP
1901 if (nameFilters.isEmpty())
1902 return true;
1903
1904 // Check the name regularexpression filters
1905 if (!(node->isDir() && (filters & QDir::AllDirs))) {
1906 for (int i = 0; i < nameFilters.size(); ++i) {
1907 if (nameFilters.at(i).exactMatch(node->fileName))
1908 return true;
1909 }
1910 return false;
1911 }
1912#endif
1913 return true;
1914}
1915
1916#include "moc_qfilesystemmodel.cpp"
1917
1918#endif // QT_NO_FILESYSTEMMODEL
1919
1920QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.