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

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 61.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** 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 have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
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 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*/
328QFileSystemModelPrivate::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 QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts);
348 if ((pathElements.isEmpty())
349#if (!defined(Q_OS_WIN) || defined(Q_OS_WINCE)) && !defined(Q_OS_SYMBIAN) && !defined(Q_OS_OS2)
350 && QDir::fromNativeSeparators(longPath) != QLatin1String("/")
351#endif
352 )
353 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
354 QModelIndex index = QModelIndex(); // start with "My Computer"
355#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_OS2)
356 if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path
357 QString host = QLatin1String("\\\\") + pathElements.first();
358 if (absolutePath == QDir::fromNativeSeparators(host))
359 absolutePath.append(QLatin1Char('/'));
360 if (longPath.endsWith(QLatin1Char('/')) && !absolutePath.endsWith(QLatin1Char('/')))
361 absolutePath.append(QLatin1Char('/'));
362 int r = 0;
363 QFileSystemModelPrivate::QFileSystemNode *rootNode = const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
364 if (!root.children.contains(host.toLower())) {
365 if (pathElements.count() == 1 && !absolutePath.endsWith(QLatin1Char('/')))
366 return rootNode;
367 QFileInfo info(host);
368 if (!info.exists())
369 return rootNode;
370 QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
371 p->addNode(rootNode, host,info);
372 p->addVisibleFiles(rootNode, QStringList(host));
373 }
374 r = rootNode->visibleLocation(host);
375 r = translateVisibleLocation(rootNode, r);
376 index = q->index(r, 0, QModelIndex());
377 pathElements.pop_front();
378 } else
379#endif
380
381#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN)
382 {
383 if (!pathElements.at(0).contains(QLatin1String(":"))) {
384 // The reason we express it like this instead of with anonymous, temporary
385 // variables, is to workaround a compiler crash with Q_CC_NOKIAX86.
386 QString rootPath = QDir(longPath).rootPath();
387 pathElements.prepend(rootPath);
388 }
389 if (pathElements.at(0).endsWith(QLatin1Char('/')))
390 pathElements[0].chop(1);
391 }
392#else
393 // add the "/" item, since it is a valid path element on Unix
394 if (absolutePath[0] == QLatin1Char('/'))
395 pathElements.prepend(QLatin1String("/"));
396#endif
397
398 QFileSystemModelPrivate::QFileSystemNode *parent = node(index);
399
400 for (int i = 0; i < pathElements.count(); ++i) {
401 QString element = pathElements.at(i);
402#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
403 // On Windows and OS/2, "filename......." and "filename" are equivalent Task #133928
404 while (element.endsWith(QLatin1Char('.')))
405 element.chop(1);
406#endif
407 bool alreadyExisted = parent->children.contains(element);
408
409 // we couldn't find the path element, we create a new node since we
410 // _know_ that the path is valid
411 if (alreadyExisted) {
412 if ((parent->children.count() == 0)
413 || (parent->caseSensitive()
414 && parent->children.value(element)->fileName != element)
415 || (!parent->caseSensitive()
416 && parent->children.value(element)->fileName.toLower() != element.toLower()))
417 alreadyExisted = false;
418 }
419
420 QFileSystemModelPrivate::QFileSystemNode *node;
421 if (!alreadyExisted) {
422 // Someone might call ::index("file://cookie/monster/doesn't/like/veggies"),
423 // a path that doesn't exists, I.E. don't blindly create directories.
424 QFileInfo info(absolutePath);
425 if (!info.exists())
426 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
427 QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
428 node = p->addNode(parent, element,info);
429#ifndef QT_NO_FILESYSTEMWATCHER
430 node->populate(fileInfoGatherer.getInfo(info));
431#endif
432 } else {
433 node = parent->children.value(element);
434 }
435
436 Q_ASSERT(node);
437 if (!node->isVisible) {
438 // It has been filtered out
439 if (alreadyExisted && node->hasInformation() && !fetch)
440 return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root);
441
442 QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this);
443 p->addVisibleFiles(parent, QStringList(element));
444 if (!p->bypassFilters.contains(node))
445 p->bypassFilters[node] = 1;
446 QString dir = q->filePath(this->index(parent));
447 if (!node->hasInformation() && fetch) {
448 Fetching f;
449 f.dir = dir;
450 f.file = element;
451 f.node = node;
452 p->toFetch.append(f);
453 p->fetchingTimer.start(0, const_cast<QFileSystemModel*>(q));
454 }
455 }
456 parent = node;
457 }
458
459 return parent;
460}
461
462/*!
463 \reimp
464*/
465void QFileSystemModel::timerEvent(QTimerEvent *event)
466{
467 Q_D(QFileSystemModel);
468 if (event->timerId() == d->fetchingTimer.timerId()) {
469 d->fetchingTimer.stop();
470#ifndef QT_NO_FILESYSTEMWATCHER
471 for (int i = 0; i < d->toFetch.count(); ++i) {
472 const QFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node;
473 if (!node->hasInformation()) {
474 d->fileInfoGatherer.fetchExtendedInformation(d->toFetch.at(i).dir,
475 QStringList(d->toFetch.at(i).file));
476 } else {
477 // qDebug() << "yah!, you saved a little gerbil soul";
478 }
479 }
480#endif
481 d->toFetch.clear();
482 }
483}
484
485/*!
486 Returns true if the model item \a index represents a directory;
487 otherwise returns false.
488*/
489bool QFileSystemModel::isDir(const QModelIndex &index) const
490{
491 // This function is for public usage only because it could create a file info
492 Q_D(const QFileSystemModel);
493 if (!index.isValid())
494 return true;
495 QFileSystemModelPrivate::QFileSystemNode *n = d->node(index);
496 if (n->hasInformation())
497 return n->isDir();
498 return fileInfo(index).isDir();
499}
500
501/*!
502 Returns the size in bytes of \a index. If the file does not exist, 0 is returned.
503 */
504qint64 QFileSystemModel::size(const QModelIndex &index) const
505{
506 Q_D(const QFileSystemModel);
507 if (!index.isValid())
508 return 0;
509 return d->node(index)->size();
510}
511
512/*!
513 Returns the type of file \a index such as "Directory" or "JPEG file".
514 */
515QString QFileSystemModel::type(const QModelIndex &index) const
516{
517 Q_D(const QFileSystemModel);
518 if (!index.isValid())
519 return QString();
520 return d->node(index)->type();
521}
522
523/*!
524 Returns the date and time when \a index was last modified.
525 */
526QDateTime QFileSystemModel::lastModified(const QModelIndex &index) const
527{
528 Q_D(const QFileSystemModel);
529 if (!index.isValid())
530 return QDateTime();
531 return d->node(index)->lastModified();
532}
533
534/*!
535 \reimp
536*/
537QModelIndex QFileSystemModel::parent(const QModelIndex &index) const
538{
539 Q_D(const QFileSystemModel);
540 if (!d->indexValid(index))
541 return QModelIndex();
542
543 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index);
544 Q_ASSERT(indexNode != 0);
545 QFileSystemModelPrivate::QFileSystemNode *parentNode = (indexNode ? indexNode->parent : 0);
546 if (parentNode == 0 || parentNode == &d->root)
547 return QModelIndex();
548
549 // get the parent's row
550 QFileSystemModelPrivate::QFileSystemNode *grandParentNode = parentNode->parent;
551 Q_ASSERT(grandParentNode->children.contains(parentNode->fileName));
552 int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName));
553 if (visualRow == -1)
554 return QModelIndex();
555 return createIndex(visualRow, 0, parentNode);
556}
557
558/*
559 \internal
560
561 return the index for node
562*/
563QModelIndex QFileSystemModelPrivate::index(const QFileSystemModelPrivate::QFileSystemNode *node) const
564{
565 Q_Q(const QFileSystemModel);
566 QFileSystemModelPrivate::QFileSystemNode *parentNode = (node ? node->parent : 0);
567 if (node == &root || !parentNode)
568 return QModelIndex();
569
570 // get the parent's row
571 Q_ASSERT(node);
572 if (!node->isVisible)
573 return QModelIndex();
574
575 int visualRow = translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName));
576 return q->createIndex(visualRow, 0, const_cast<QFileSystemNode*>(node));
577}
578
579/*!
580 \reimp
581*/
582bool QFileSystemModel::hasChildren(const QModelIndex &parent) const
583{
584 Q_D(const QFileSystemModel);
585 if (parent.column() > 0)
586 return false;
587
588 if (!parent.isValid()) // drives
589 return true;
590
591 const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
592 Q_ASSERT(indexNode);
593 return (indexNode->isDir());
594}
595
596/*!
597 \reimp
598 */
599bool QFileSystemModel::canFetchMore(const QModelIndex &parent) const
600{
601 Q_D(const QFileSystemModel);
602 const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
603 return (!indexNode->populatedChildren);
604}
605
606/*!
607 \reimp
608 */
609void QFileSystemModel::fetchMore(const QModelIndex &parent)
610{
611 Q_D(QFileSystemModel);
612 if (!d->setRootPath)
613 return;
614 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent);
615 if (indexNode->populatedChildren)
616 return;
617 indexNode->populatedChildren = true;
618 d->fileInfoGatherer.list(filePath(parent));
619}
620
621/*!
622 \reimp
623*/
624int QFileSystemModel::rowCount(const QModelIndex &parent) const
625{
626 Q_D(const QFileSystemModel);
627 if (parent.column() > 0)
628 return 0;
629
630 if (!parent.isValid())
631 return d->root.visibleChildren.count();
632
633 const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent);
634 return parentNode->visibleChildren.count();
635}
636
637/*!
638 \reimp
639*/
640int QFileSystemModel::columnCount(const QModelIndex &parent) const
641{
642 return (parent.column() > 0) ? 0 : 4;
643}
644
645/*!
646 Returns the data stored under the given \a role for the item "My Computer".
647
648 \sa Qt::ItemDataRole
649 */
650QVariant QFileSystemModel::myComputer(int role) const
651{
652 Q_D(const QFileSystemModel);
653 switch (role) {
654 case Qt::DisplayRole:
655 return d->myComputer();
656 case Qt::DecorationRole:
657 return d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Computer);
658 }
659 return QVariant();
660}
661
662/*!
663 \reimp
664*/
665QVariant QFileSystemModel::data(const QModelIndex &index, int role) const
666{
667 Q_D(const QFileSystemModel);
668 if (!index.isValid() || index.model() != this)
669 return QVariant();
670
671 switch (role) {
672 case Qt::EditRole:
673 case Qt::DisplayRole:
674 switch (index.column()) {
675 case 0: return d->name(index);
676 case 1: return d->size(index);
677 case 2: return d->type(index);
678 case 3: return d->time(index);
679 default:
680 qWarning("data: invalid display value column %d", index.column());
681 break;
682 }
683 break;
684 case FilePathRole:
685 return filePath(index);
686 case FileNameRole:
687 return d->name(index);
688 case Qt::DecorationRole:
689 if (index.column() == 0) {
690 QIcon icon = d->icon(index);
691 if (icon.isNull()) {
692 if (d->node(index)->isDir())
693 icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Folder);
694 else
695 icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::File);
696 }
697 return icon;
698 }
699 break;
700 case Qt::TextAlignmentRole:
701 if (index.column() == 1)
702 return Qt::AlignRight;
703 break;
704 case FilePermissions:
705 int p = permissions(index);
706 return p;
707 }
708
709 return QVariant();
710}
711
712/*!
713 \internal
714*/
715QString QFileSystemModelPrivate::size(const QModelIndex &index) const
716{
717 if (!index.isValid())
718 return QString();
719 const QFileSystemNode *n = node(index);
720 if (n->isDir()) {
721#ifdef Q_OS_MAC
722 return QLatin1String("--");
723#else
724 return QLatin1String("");
725#endif
726 // Windows - ""
727 // OS X - "--"
728 // Konqueror - "4 KB"
729 // Nautilus - "9 items" (the number of children)
730 }
731 return size(n->size());
732}
733
734QString QFileSystemModelPrivate::size(qint64 bytes)
735{
736 // According to the Si standard KB is 1000 bytes, KiB is 1024
737 // but on windows sizes are calculated by dividing by 1024 so we do what they do.
738 const qint64 kb = 1024;
739 const qint64 mb = 1024 * kb;
740 const qint64 gb = 1024 * mb;
741 const qint64 tb = 1024 * gb;
742 if (bytes >= tb)
743 return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3));
744 if (bytes >= gb)
745 return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2));
746 if (bytes >= mb)
747 return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1));
748 if (bytes >= kb)
749 return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb));
750 return QFileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes));
751}
752
753/*!
754 \internal
755*/
756QString QFileSystemModelPrivate::time(const QModelIndex &index) const
757{
758 if (!index.isValid())
759 return QString();
760#ifndef QT_NO_DATESTRING
761 return node(index)->lastModified().toString(Qt::SystemLocaleDate);
762#else
763 Q_UNUSED(index);
764 return QString();
765#endif
766}
767
768/*
769 \internal
770*/
771QString QFileSystemModelPrivate::type(const QModelIndex &index) const
772{
773 if (!index.isValid())
774 return QString();
775 return node(index)->type();
776}
777
778/*!
779 \internal
780*/
781QString QFileSystemModelPrivate::name(const QModelIndex &index) const
782{
783 if (!index.isValid())
784 return QString();
785 QFileSystemNode *dirNode = node(index);
786 if (dirNode->isSymLink() && fileInfoGatherer.resolveSymlinks()) {
787 QString fullPath = QDir::fromNativeSeparators(filePath(index));
788 if (resolvedSymLinks.contains(fullPath))
789 return resolvedSymLinks[fullPath];
790 }
791 // ### TODO it would be nice to grab the volume name if dirNode->parent == root
792 return dirNode->fileName;
793}
794
795/*!
796 \internal
797*/
798QIcon QFileSystemModelPrivate::icon(const QModelIndex &index) const
799{
800 if (!index.isValid())
801 return QIcon();
802 return node(index)->icon();
803}
804
805/*!
806 \reimp
807*/
808bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role)
809{
810 Q_D(QFileSystemModel);
811 if (!idx.isValid()
812 || idx.column() != 0
813 || role != Qt::EditRole
814 || (flags(idx) & Qt::ItemIsEditable) == 0) {
815 return false;
816 }
817
818 QString newName = value.toString();
819 QString oldName = idx.data().toString();
820 if (newName == idx.data().toString())
821 return true;
822
823 if (newName.isEmpty()
824 || newName.contains(QDir::separator())
825 || !QDir(filePath(parent(idx))).rename(oldName, newName)) {
826#ifndef QT_NO_MESSAGEBOX
827 QMessageBox::information(0, QFileSystemModel::tr("Invalid filename"),
828 QFileSystemModel::tr("<b>The name \"%1\" can not be used.</b><p>Try using another name, with fewer characters or no punctuations marks.")
829 .arg(newName),
830 QMessageBox::Ok);
831#endif // QT_NO_MESSAGEBOX
832 return false;
833 } else {
834 /*
835 *After re-naming something we don't want the selection to change*
836 - can't remove rows and later insert
837 - can't quickly remove and insert
838 - index pointer can't change because treeview doesn't use persistant index's
839
840 - if this get any more complicated think of changing it to just
841 use layoutChanged
842 */
843
844 QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx);
845 QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent;
846 int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName);
847
848 d->addNode(parentNode, newName,indexNode->info->fileInfo());
849 parentNode->visibleChildren.removeAt(visibleLocation);
850 QFileSystemModelPrivate::QFileSystemNode * oldValue = parentNode->children.value(oldName);
851 parentNode->children[newName] = oldValue;
852 QFileInfo info(d->rootDir, newName);
853 oldValue->fileName = newName;
854 oldValue->parent = parentNode;
855 oldValue->populate(d->fileInfoGatherer.getInfo(info));
856 oldValue->isVisible = true;
857
858 parentNode->children.remove(oldName);
859 parentNode->visibleChildren.insert(visibleLocation, newName);
860
861 d->delayedSort();
862 emit fileRenamed(filePath(idx.parent()), oldName, newName);
863 }
864 return true;
865}
866
867/*!
868 \reimp
869*/
870QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
871{
872 switch (role) {
873 case Qt::DecorationRole:
874 if (section == 0) {
875 // ### TODO oh man this is ugly and doesn't even work all the way!
876 // it is still 2 pixels off
877 QImage pixmap(16, 1, QImage::Format_Mono);
878 pixmap.fill(0);
879 pixmap.setAlphaChannel(pixmap.createAlphaMask());
880 return pixmap;
881 }
882 break;
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 Q_Q(QFileSystemModel);
1087 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent);
1088 if (indexNode->children.count() == 0)
1089 return;
1090
1091 QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > values;
1092 QHash<FileNameKey, QFileSystemNode *>::const_iterator iterator;
1093 int i = 0;
1094 for(iterator = indexNode->children.begin() ; iterator != indexNode->children.end() ; ++iterator) {
1095 if (filtersAcceptsNode(iterator.value())) {
1096 values.append(QPair<QFileSystemModelPrivate::QFileSystemNode*, int>((iterator.value()), i));
1097 } else {
1098 iterator.value()->isVisible = false;
1099 }
1100 i++;
1101 }
1102 QFileSystemModelSorter ms(column);
1103 qStableSort(values.begin(), values.end(), ms);
1104 // First update the new visible list
1105 indexNode->visibleChildren.clear();
1106 //No more dirty item we reset our internal dirty index
1107 indexNode->dirtyChildrenIndex = -1;
1108 for (int i = 0; i < values.count(); ++i) {
1109 indexNode->visibleChildren.append(values.at(i).first->fileName);
1110 values.at(i).first->isVisible = true;
1111 }
1112
1113 if (!disableRecursiveSort) {
1114 for (int i = 0; i < q->rowCount(parent); ++i) {
1115 const QModelIndex childIndex = q->index(i, 0, parent);
1116 QFileSystemModelPrivate::QFileSystemNode *indexNode = node(childIndex);
1117 //Only do a recursive sort on visible nodes
1118 if (indexNode->isVisible)
1119 sortChildren(column, childIndex);
1120 }
1121 }
1122}
1123
1124/*!
1125 \reimp
1126*/
1127void QFileSystemModel::sort(int column, Qt::SortOrder order)
1128{
1129 Q_D(QFileSystemModel);
1130 if (d->sortOrder == order && d->sortColumn == column && !d->forceSort)
1131 return;
1132
1133 emit layoutAboutToBeChanged();
1134 QModelIndexList oldList = persistentIndexList();
1135 QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > oldNodes;
1136 for (int i = 0; i < oldList.count(); ++i) {
1137 QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldList.at(i)), oldList.at(i).column());
1138 oldNodes.append(pair);
1139 }
1140
1141 if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) {
1142 //we sort only from where we are, don't need to sort all the model
1143 d->sortChildren(column, index(rootPath()));
1144 d->sortColumn = column;
1145 d->forceSort = false;
1146 }
1147 d->sortOrder = order;
1148
1149 QModelIndexList newList;
1150 for (int i = 0; i < oldNodes.count(); ++i) {
1151 QModelIndex idx = d->index(oldNodes.at(i).first);
1152 idx = idx.sibling(idx.row(), oldNodes.at(i).second);
1153 newList.append(idx);
1154 }
1155 changePersistentIndexList(oldList, newList);
1156 emit layoutChanged();
1157}
1158
1159/*!
1160 Returns a list of MIME types that can be used to describe a list of items
1161 in the model.
1162*/
1163QStringList QFileSystemModel::mimeTypes() const
1164{
1165 return QStringList(QLatin1String("text/uri-list"));
1166}
1167
1168/*!
1169 Returns an object that contains a serialized description of the specified
1170 \a indexes. The format used to describe the items corresponding to the
1171 indexes is obtained from the mimeTypes() function.
1172
1173 If the list of indexes is empty, 0 is returned rather than a serialized
1174 empty list.
1175*/
1176QMimeData *QFileSystemModel::mimeData(const QModelIndexList &indexes) const
1177{
1178 QList<QUrl> urls;
1179 QList<QModelIndex>::const_iterator it = indexes.begin();
1180 for (; it != indexes.end(); ++it)
1181 if ((*it).column() == 0)
1182 urls << QUrl::fromLocalFile(filePath(*it));
1183 QMimeData *data = new QMimeData();
1184 data->setUrls(urls);
1185 return data;
1186}
1187
1188/*!
1189 Handles the \a data supplied by a drag and drop operation that ended with
1190 the given \a action over the row in the model specified by the \a row and
1191 \a column and by the \a parent index.
1192
1193 \sa supportedDropActions()
1194*/
1195bool QFileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
1196 int row, int column, const QModelIndex &parent)
1197{
1198 Q_UNUSED(row);
1199 Q_UNUSED(column);
1200 if (!parent.isValid() || isReadOnly())
1201 return false;
1202
1203 bool success = true;
1204 QString to = filePath(parent) + QDir::separator();
1205
1206 QList<QUrl> urls = data->urls();
1207 QList<QUrl>::const_iterator it = urls.constBegin();
1208
1209 switch (action) {
1210 case Qt::CopyAction:
1211 for (; it != urls.constEnd(); ++it) {
1212 QString path = (*it).toLocalFile();
1213 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
1214 }
1215 break;
1216 case Qt::LinkAction:
1217 for (; it != urls.constEnd(); ++it) {
1218 QString path = (*it).toLocalFile();
1219 success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
1220 }
1221 break;
1222 case Qt::MoveAction:
1223 for (; it != urls.constEnd(); ++it) {
1224 QString path = (*it).toLocalFile();
1225 success = QFile::copy(path, to + QFileInfo(path).fileName())
1226 && QFile::remove(path) && success;
1227 }
1228 break;
1229 default:
1230 return false;
1231 }
1232
1233 return success;
1234}
1235
1236/*!
1237 \reimp
1238*/
1239Qt::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*/
1248QString 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
1264QString 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_OS2) && (!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*/
1291QModelIndex 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 */
1312QFile::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 */
1336QModelIndex 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*/
1385QString 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*/
1396QDir 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*/
1408void 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*/
1419QFileIconProvider *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*/
1433void 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*/
1453QDir::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*/
1467void QFileSystemModel::setResolveSymlinks(bool enable)
1468{
1469 Q_D(QFileSystemModel);
1470 d->fileInfoGatherer.setResolveSymlinks(enable);
1471}
1472
1473bool 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*/
1488void QFileSystemModel::setReadOnly(bool enable)
1489{
1490 Q_D(QFileSystemModel);
1491 d->readOnly = enable;
1492}
1493
1494bool 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*/
1506void 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
1516bool 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*/
1525void 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*/
1564QStringList 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*/
1579bool 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 */
1595void 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<FileNameKey, 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*/
1636QFileSystemModelPrivate::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 */
1655void 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*/
1678class QFileSystemModelVisibleFinder
1679{
1680public:
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;
1689private:
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 */
1702void 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 */
1729void 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 */
1751void 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) {
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*/
1854void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName)
1855{
1856 resolvedSymLinks[fileName] = resolvedName;
1857}
1858
1859/*!
1860 \internal
1861*/
1862void 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*/
1883bool 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 */
1928bool 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
1946QT_END_NAMESPACE
1947
1948#include "moc_qfilesystemmodel.cpp"
1949
1950#endif // QT_NO_FILESYSTEMMODEL
Note: See TracBrowser for help on using the repository browser.