source: trunk/src/gui/itemviews/qdirmodel.cpp@ 1056

Last change on this file since 1056 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 39.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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 "qdirmodel.h"
43
44#ifndef QT_NO_DIRMODEL
45#include <qstack.h>
46#include <qfile.h>
47#include <qfilesystemmodel.h>
48#include <qurl.h>
49#include <qmime.h>
50#include <qpair.h>
51#include <qvector.h>
52#include <qobject.h>
53#include <qdatetime.h>
54#include <qlocale.h>
55#include <qstyle.h>
56#include <qapplication.h>
57#include <private/qabstractitemmodel_p.h>
58#include <qdebug.h>
59
60/*!
61 \enum QDirModel::Roles
62 \value FileIconRole
63 \value FilePathRole
64 \value FileNameRole
65*/
66
67QT_BEGIN_NAMESPACE
68
69class QDirModelPrivate : public QAbstractItemModelPrivate
70{
71 Q_DECLARE_PUBLIC(QDirModel)
72
73public:
74 struct QDirNode
75 {
76 QDirNode() : parent(0), populated(false), stat(false) {}
77 ~QDirNode() { children.clear(); }
78 QDirNode *parent;
79 QFileInfo info;
80 QIcon icon; // cache the icon
81 mutable QVector<QDirNode> children;
82 mutable bool populated; // have we read the children
83 mutable bool stat;
84 };
85
86 QDirModelPrivate()
87 : resolveSymlinks(true),
88 readOnly(true),
89 lazyChildCount(false),
90 allowAppendChild(true),
91 iconProvider(&defaultProvider),
92 shouldStat(true) // ### This is set to false by QFileDialog
93 { }
94
95 void init();
96 QDirNode *node(int row, QDirNode *parent) const;
97 QVector<QDirNode> children(QDirNode *parent, bool stat) const;
98
99 void _q_refresh();
100
101 void savePersistentIndexes();
102 void restorePersistentIndexes();
103
104 QFileInfoList entryInfoList(const QString &path) const;
105 QStringList entryList(const QString &path) const;
106
107 QString name(const QModelIndex &index) const;
108 QString size(const QModelIndex &index) const;
109 QString type(const QModelIndex &index) const;
110 QString time(const QModelIndex &index) const;
111
112 void appendChild(QDirModelPrivate::QDirNode *parent, const QString &path) const;
113 static QFileInfo resolvedInfo(QFileInfo info);
114
115 inline QDirNode *node(const QModelIndex &index) const;
116 inline void populate(QDirNode *parent) const;
117 inline void clear(QDirNode *parent) const;
118
119 void invalidate();
120
121 mutable QDirNode root;
122 bool resolveSymlinks;
123 bool readOnly;
124 bool lazyChildCount;
125 bool allowAppendChild;
126
127 QDir::Filters filters;
128 QDir::SortFlags sort;
129 QStringList nameFilters;
130
131 QFileIconProvider *iconProvider;
132 QFileIconProvider defaultProvider;
133
134 struct SavedPersistent {
135 QString path;
136 int column;
137 QPersistentModelIndexData *data;
138 QPersistentModelIndex index;
139 };
140 QList<SavedPersistent> savedPersistent;
141 QPersistentModelIndex toBeRefreshed;
142
143 bool shouldStat; // use the "carefull not to stat directories" mode
144};
145
146void qt_setDirModelShouldNotStat(QDirModelPrivate *modelPrivate)
147{
148 modelPrivate->shouldStat = false;
149}
150
151QDirModelPrivate::QDirNode *QDirModelPrivate::node(const QModelIndex &index) const
152{
153 QDirModelPrivate::QDirNode *n =
154 static_cast<QDirModelPrivate::QDirNode*>(index.internalPointer());
155 Q_ASSERT(n);
156 return n;
157}
158
159void QDirModelPrivate::populate(QDirNode *parent) const
160{
161 Q_ASSERT(parent);
162 parent->children = children(parent, parent->stat);
163 parent->populated = true;
164}
165
166void QDirModelPrivate::clear(QDirNode *parent) const
167{
168 Q_ASSERT(parent);
169 parent->children.clear();
170 parent->populated = false;
171}
172
173void QDirModelPrivate::invalidate()
174{
175 QStack<const QDirNode*> nodes;
176 nodes.push(&root);
177 while (!nodes.empty()) {
178 const QDirNode *current = nodes.pop();
179 current->stat = false;
180 const QVector<QDirNode> children = current->children;
181 for (int i = 0; i < children.count(); ++i)
182 nodes.push(&children.at(i));
183 }
184}
185
186/*!
187 \class QDirModel
188 \obsolete
189 \brief The QDirModel class provides a data model for the local filesystem.
190
191 \ingroup model-view
192
193 The usage of QDirModel is not recommended anymore. The
194 QFileSystemModel class is a more performant alternative.
195
196 This class provides access to the local filesystem, providing functions
197 for renaming and removing files and directories, and for creating new
198 directories. In the simplest case, it can be used with a suitable display
199 widget as part of a browser or filer.
200
201 QDirModel keeps a cache with file information. The cache needs to be
202 updated with refresh().
203
204 QDirModel can be accessed using the standard interface provided by
205 QAbstractItemModel, but it also provides some convenience functions
206 that are specific to a directory model. The fileInfo() and isDir()
207 functions provide information about the underlying files and directories
208 related to items in the model.
209
210 Directories can be created and removed using mkdir(), rmdir(), and the
211 model will be automatically updated to take the changes into account.
212
213 \note QDirModel requires an instance of a GUI application.
214
215 \sa nameFilters(), setFilter(), filter(), QListView, QTreeView, QFileSystemModel,
216 {Dir View Example}, {Model Classes}
217*/
218
219/*!
220 Constructs a new directory model with the given \a parent.
221 Only those files matching the \a nameFilters and the
222 \a filters are included in the model. The sort order is given by the
223 \a sort flags.
224*/
225
226QDirModel::QDirModel(const QStringList &nameFilters,
227 QDir::Filters filters,
228 QDir::SortFlags sort,
229 QObject *parent)
230 : QAbstractItemModel(*new QDirModelPrivate, parent)
231{
232 Q_D(QDirModel);
233 // we always start with QDir::drives()
234 d->nameFilters = nameFilters.isEmpty() ? QStringList(QLatin1String("*")) : nameFilters;
235 d->filters = filters;
236 d->sort = sort;
237 d->root.parent = 0;
238 d->root.info = QFileInfo();
239 d->clear(&d->root);
240}
241
242/*!
243 Constructs a directory model with the given \a parent.
244*/
245
246QDirModel::QDirModel(QObject *parent)
247 : QAbstractItemModel(*new QDirModelPrivate, parent)
248{
249 Q_D(QDirModel);
250 d->init();
251}
252
253/*!
254 \internal
255*/
256QDirModel::QDirModel(QDirModelPrivate &dd, QObject *parent)
257 : QAbstractItemModel(dd, parent)
258{
259 Q_D(QDirModel);
260 d->init();
261}
262
263/*!
264 Destroys this directory model.
265*/
266
267QDirModel::~QDirModel()
268{
269
270}
271
272/*!
273 Returns the model item index for the item in the \a parent with the
274 given \a row and \a column.
275
276*/
277
278QModelIndex QDirModel::index(int row, int column, const QModelIndex &parent) const
279{
280 Q_D(const QDirModel);
281 // note that rowCount does lazy population
282 if (column < 0 || column >= columnCount(parent) || row < 0 || parent.column() > 0)
283 return QModelIndex();
284 // make sure the list of children is up to date
285 QDirModelPrivate::QDirNode *p = (d->indexValid(parent) ? d->node(parent) : &d->root);
286 Q_ASSERT(p);
287 if (!p->populated)
288 d->populate(p); // populate without stat'ing
289 if (row >= p->children.count())
290 return QModelIndex();
291 // now get the internal pointer for the index
292 QDirModelPrivate::QDirNode *n = d->node(row, d->indexValid(parent) ? p : 0);
293 Q_ASSERT(n);
294
295 return createIndex(row, column, n);
296}
297
298/*!
299 Return the parent of the given \a child model item.
300*/
301
302QModelIndex QDirModel::parent(const QModelIndex &child) const
303{
304 Q_D(const QDirModel);
305
306 if (!d->indexValid(child))
307 return QModelIndex();
308 QDirModelPrivate::QDirNode *node = d->node(child);
309 QDirModelPrivate::QDirNode *par = (node ? node->parent : 0);
310 if (par == 0) // parent is the root node
311 return QModelIndex();
312
313 // get the parent's row
314 const QVector<QDirModelPrivate::QDirNode> children =
315 par->parent ? par->parent->children : d->root.children;
316 Q_ASSERT(children.count() > 0);
317 int row = (par - &(children.at(0)));
318 Q_ASSERT(row >= 0);
319
320 return createIndex(row, 0, par);
321}
322
323/*!
324 Returns the number of rows in the \a parent model item.
325
326*/
327
328int QDirModel::rowCount(const QModelIndex &parent) const
329{
330 Q_D(const QDirModel);
331 if (parent.column() > 0)
332 return 0;
333
334 if (!parent.isValid()) {
335 if (!d->root.populated) // lazy population
336 d->populate(&d->root);
337 return d->root.children.count();
338 }
339 if (parent.model() != this)
340 return 0;
341 QDirModelPrivate::QDirNode *p = d->node(parent);
342 if (p->info.isDir() && !p->populated) // lazy population
343 d->populate(p);
344 return p->children.count();
345}
346
347/*!
348 Returns the number of columns in the \a parent model item.
349
350*/
351
352int QDirModel::columnCount(const QModelIndex &parent) const
353{
354 if (parent.column() > 0)
355 return 0;
356 return 4;
357}
358
359/*!
360 Returns the data for the model item \a index with the given \a role.
361*/
362QVariant QDirModel::data(const QModelIndex &index, int role) const
363{
364 Q_D(const QDirModel);
365 if (!d->indexValid(index))
366 return QVariant();
367
368 if (role == Qt::DisplayRole || role == Qt::EditRole) {
369 switch (index.column()) {
370 case 0: return d->name(index);
371 case 1: return d->size(index);
372 case 2: return d->type(index);
373 case 3: return d->time(index);
374 default:
375 qWarning("data: invalid display value column %d", index.column());
376 return QVariant();
377 }
378 }
379
380 if (index.column() == 0) {
381 if (role == FileIconRole)
382 return fileIcon(index);
383 if (role == FilePathRole)
384 return filePath(index);
385 if (role == FileNameRole)
386 return fileName(index);
387 }
388
389 if (index.column() == 1 && Qt::TextAlignmentRole == role) {
390 return Qt::AlignRight;
391 }
392 return QVariant();
393}
394
395/*!
396 Sets the data for the model item \a index with the given \a role to
397 the data referenced by the \a value. Returns true if successful;
398 otherwise returns false.
399
400 \sa Qt::ItemDataRole
401*/
402
403bool QDirModel::setData(const QModelIndex &index, const QVariant &value, int role)
404{
405 Q_D(QDirModel);
406 if (!d->indexValid(index) || index.column() != 0
407 || (flags(index) & Qt::ItemIsEditable) == 0 || role != Qt::EditRole)
408 return false;
409
410 QDirModelPrivate::QDirNode *node = d->node(index);
411 QDir dir = node->info.dir();
412 QString name = value.toString();
413 if (dir.rename(node->info.fileName(), name)) {
414 node->info = QFileInfo(dir, name);
415 QModelIndex sibling = index.sibling(index.row(), 3);
416 emit dataChanged(index, sibling);
417
418 d->toBeRefreshed = index.parent();
419 QMetaObject::invokeMethod(this, "_q_refresh", Qt::QueuedConnection);
420
421 return true;
422 }
423
424 return false;
425}
426
427/*!
428 Returns the data stored under the given \a role for the specified \a section
429 of the header with the given \a orientation.
430*/
431
432QVariant QDirModel::headerData(int section, Qt::Orientation orientation, int role) const
433{
434 if (orientation == Qt::Horizontal) {
435 if (role != Qt::DisplayRole)
436 return QVariant();
437 switch (section) {
438 case 0: return tr("Name");
439 case 1: return tr("Size");
440 case 2: return
441#ifdef Q_OS_MAC
442 tr("Kind", "Match OS X Finder");
443#else
444 tr("Type", "All other platforms");
445#endif
446 // Windows - Type
447 // OS X - Kind
448 // Konqueror - File Type
449 // Nautilus - Type
450 case 3: return tr("Date Modified");
451 default: return QVariant();
452 }
453 }
454 return QAbstractItemModel::headerData(section, orientation, role);
455}
456
457/*!
458 Returns true if the \a parent model item has children; otherwise
459 returns false.
460*/
461
462bool QDirModel::hasChildren(const QModelIndex &parent) const
463{
464 Q_D(const QDirModel);
465 if (parent.column() > 0)
466 return false;
467
468 if (!parent.isValid()) // the invalid index is the "My Computer" item
469 return true; // the drives
470 QDirModelPrivate::QDirNode *p = d->node(parent);
471 Q_ASSERT(p);
472
473 if (d->lazyChildCount) // optimization that only checks for children if the node has been populated
474 return p->info.isDir();
475 return p->info.isDir() && rowCount(parent) > 0;
476}
477
478/*!
479 Returns the item flags for the given \a index in the model.
480
481 \sa Qt::ItemFlags
482*/
483Qt::ItemFlags QDirModel::flags(const QModelIndex &index) const
484{
485 Q_D(const QDirModel);
486 Qt::ItemFlags flags = QAbstractItemModel::flags(index);
487 if (!d->indexValid(index))
488 return flags;
489 flags |= Qt::ItemIsDragEnabled;
490 if (d->readOnly)
491 return flags;
492 QDirModelPrivate::QDirNode *node = d->node(index);
493 if ((index.column() == 0) && node->info.isWritable()) {
494 flags |= Qt::ItemIsEditable;
495 if (fileInfo(index).isDir()) // is directory and is editable
496 flags |= Qt::ItemIsDropEnabled;
497 }
498 return flags;
499}
500
501/*!
502 Sort the model items in the \a column using the \a order given.
503 The order is a value defined in \l Qt::SortOrder.
504*/
505
506void QDirModel::sort(int column, Qt::SortOrder order)
507{
508 QDir::SortFlags sort = QDir::DirsFirst | QDir::IgnoreCase;
509 if (order == Qt::DescendingOrder)
510 sort |= QDir::Reversed;
511
512 switch (column) {
513 case 0:
514 sort |= QDir::Name;
515 break;
516 case 1:
517 sort |= QDir::Size;
518 break;
519 case 2:
520 sort |= QDir::Type;
521 break;
522 case 3:
523 sort |= QDir::Time;
524 break;
525 default:
526 break;
527 }
528
529 setSorting(sort);
530}
531
532/*!
533 Returns a list of MIME types that can be used to describe a list of items
534 in the model.
535*/
536
537QStringList QDirModel::mimeTypes() const
538{
539 return QStringList(QLatin1String("text/uri-list"));
540}
541
542/*!
543 Returns an object that contains a serialized description of the specified
544 \a indexes. The format used to describe the items corresponding to the
545 indexes is obtained from the mimeTypes() function.
546
547 If the list of indexes is empty, 0 is returned rather than a serialized
548 empty list.
549*/
550
551QMimeData *QDirModel::mimeData(const QModelIndexList &indexes) const
552{
553 QList<QUrl> urls;
554 QList<QModelIndex>::const_iterator it = indexes.begin();
555 for (; it != indexes.end(); ++it)
556 if ((*it).column() == 0)
557 urls << QUrl::fromLocalFile(filePath(*it));
558 QMimeData *data = new QMimeData();
559 data->setUrls(urls);
560 return data;
561}
562
563/*!
564 Handles the \a data supplied by a drag and drop operation that ended with
565 the given \a action over the row in the model specified by the \a row and
566 \a column and by the \a parent index.
567
568 \sa supportedDropActions()
569*/
570
571bool QDirModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
572 int /* row */, int /* column */, const QModelIndex &parent)
573{
574 Q_D(QDirModel);
575 if (!d->indexValid(parent) || isReadOnly())
576 return false;
577
578 bool success = true;
579 QString to = filePath(parent) + QDir::separator();
580 QModelIndex _parent = parent;
581
582 QList<QUrl> urls = data->urls();
583 QList<QUrl>::const_iterator it = urls.constBegin();
584
585 switch (action) {
586 case Qt::CopyAction:
587 for (; it != urls.constEnd(); ++it) {
588 QString path = (*it).toLocalFile();
589 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
590 }
591 break;
592 case Qt::LinkAction:
593 for (; it != urls.constEnd(); ++it) {
594 QString path = (*it).toLocalFile();
595 success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
596 }
597 break;
598 case Qt::MoveAction:
599 for (; it != urls.constEnd(); ++it) {
600 QString path = (*it).toLocalFile();
601 if (QFile::copy(path, to + QFileInfo(path).fileName())
602 && QFile::remove(path)) {
603 QModelIndex idx=index(QFileInfo(path).path());
604 if (idx.isValid()) {
605 refresh(idx);
606 //the previous call to refresh may invalidate the _parent. so recreate a new QModelIndex
607 _parent = index(to);
608 }
609 } else {
610 success = false;
611 }
612 }
613 break;
614 default:
615 return false;
616 }
617
618 if (success)
619 refresh(_parent);
620
621 return success;
622}
623
624/*!
625 Returns the drop actions supported by this model.
626
627 \sa Qt::DropActions
628*/
629
630Qt::DropActions QDirModel::supportedDropActions() const
631{
632 return Qt::CopyAction | Qt::MoveAction; // FIXME: LinkAction is not supported yet
633}
634
635/*!
636 Sets the \a provider of file icons for the directory model.
637
638*/
639
640void QDirModel::setIconProvider(QFileIconProvider *provider)
641{
642 Q_D(QDirModel);
643 d->iconProvider = provider;
644}
645
646/*!
647 Returns the file icon provider for this directory model.
648*/
649
650QFileIconProvider *QDirModel::iconProvider() const
651{
652 Q_D(const QDirModel);
653 return d->iconProvider;
654}
655
656/*!
657 Sets the name \a filters for the directory model.
658*/
659
660void QDirModel::setNameFilters(const QStringList &filters)
661{
662 Q_D(QDirModel);
663 d->nameFilters = filters;
664 emit layoutAboutToBeChanged();
665 if (d->shouldStat)
666 refresh(QModelIndex());
667 else
668 d->invalidate();
669 emit layoutChanged();
670}
671
672/*!
673 Returns a list of filters applied to the names in the model.
674*/
675
676QStringList QDirModel::nameFilters() const
677{
678 Q_D(const QDirModel);
679 return d->nameFilters;
680}
681
682/*!
683 Sets the directory model's filter to that specified by \a filters.
684
685 Note that the filter you set should always include the QDir::AllDirs enum value,
686 otherwise QDirModel won't be able to read the directory structure.
687
688 \sa QDir::Filters
689*/
690
691void QDirModel::setFilter(QDir::Filters filters)
692{
693 Q_D(QDirModel);
694 d->filters = filters;
695 emit layoutAboutToBeChanged();
696 if (d->shouldStat)
697 refresh(QModelIndex());
698 else
699 d->invalidate();
700 emit layoutChanged();
701}
702
703/*!
704 Returns the filter specification for the directory model.
705
706 \sa QDir::Filters
707*/
708
709QDir::Filters QDirModel::filter() const
710{
711 Q_D(const QDirModel);
712 return d->filters;
713}
714
715/*!
716 Sets the directory model's sorting order to that specified by \a sort.
717
718 \sa QDir::SortFlags
719*/
720
721void QDirModel::setSorting(QDir::SortFlags sort)
722{
723 Q_D(QDirModel);
724 d->sort = sort;
725 emit layoutAboutToBeChanged();
726 if (d->shouldStat)
727 refresh(QModelIndex());
728 else
729 d->invalidate();
730 emit layoutChanged();
731}
732
733/*!
734 Returns the sorting method used for the directory model.
735
736 \sa QDir::SortFlags */
737
738QDir::SortFlags QDirModel::sorting() const
739{
740 Q_D(const QDirModel);
741 return d->sort;
742}
743
744/*!
745 \property QDirModel::resolveSymlinks
746 \brief Whether the directory model should resolve symbolic links
747
748 This is only relevant on operating systems that support symbolic
749 links.
750*/
751void QDirModel::setResolveSymlinks(bool enable)
752{
753 Q_D(QDirModel);
754 d->resolveSymlinks = enable;
755}
756
757bool QDirModel::resolveSymlinks() const
758{
759 Q_D(const QDirModel);
760 return d->resolveSymlinks;
761}
762
763/*!
764 \property QDirModel::readOnly
765 \brief Whether the directory model allows writing to the file system
766
767 If this property is set to false, the directory model will allow renaming, copying
768 and deleting of files and directories.
769
770 This property is true by default
771*/
772
773void QDirModel::setReadOnly(bool enable)
774{
775 Q_D(QDirModel);
776 d->readOnly = enable;
777}
778
779bool QDirModel::isReadOnly() const
780{
781 Q_D(const QDirModel);
782 return d->readOnly;
783}
784
785/*!
786 \property QDirModel::lazyChildCount
787 \brief Whether the directory model optimizes the hasChildren function
788 to only check if the item is a directory.
789
790 If this property is set to false, the directory model will make sure that a directory
791 actually containes any files before reporting that it has children.
792 Otherwise the directory model will report that an item has children if the item
793 is a directory.
794
795 This property is false by default
796*/
797
798void QDirModel::setLazyChildCount(bool enable)
799{
800 Q_D(QDirModel);
801 d->lazyChildCount = enable;
802}
803
804bool QDirModel::lazyChildCount() const
805{
806 Q_D(const QDirModel);
807 return d->lazyChildCount;
808}
809
810/*!
811 QDirModel caches file information. This function updates the
812 cache. The \a parent parameter is the directory from which the
813 model is updated; the default value will update the model from
814 root directory of the file system (the entire model).
815*/
816
817void QDirModel::refresh(const QModelIndex &parent)
818{
819 Q_D(QDirModel);
820
821 QDirModelPrivate::QDirNode *n = d->indexValid(parent) ? d->node(parent) : &(d->root);
822
823 int rows = n->children.count();
824 if (rows == 0) {
825 emit layoutAboutToBeChanged();
826 n->stat = true; // make sure that next time we read all the info
827 n->populated = false;
828 emit layoutChanged();
829 return;
830 }
831
832 emit layoutAboutToBeChanged();
833 d->savePersistentIndexes();
834 d->rowsAboutToBeRemoved(parent, 0, rows - 1);
835 n->stat = true; // make sure that next time we read all the info
836 d->clear(n);
837 d->rowsRemoved(parent, 0, rows - 1);
838 d->restorePersistentIndexes();
839 emit layoutChanged();
840}
841
842/*!
843 \overload
844
845 Returns the model item index for the given \a path.
846*/
847
848QModelIndex QDirModel::index(const QString &path, int column) const
849{
850 Q_D(const QDirModel);
851
852 if (path.isEmpty() || path == QCoreApplication::translate("QFileDialog", "My Computer"))
853 return QModelIndex();
854
855 QString absolutePath = QDir(path).absolutePath();
856#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) || defined(Q_OS_OS2)
857 absolutePath = absolutePath.toLower();
858#endif
859#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_OS2)
860 // On Windows, "filename......." and "filename" are equivalent
861 if (absolutePath.endsWith(QLatin1Char('.'))) {
862 int i;
863 for (i = absolutePath.count() - 1; i >= 0; --i) {
864 if (absolutePath.at(i) != QLatin1Char('.'))
865 break;
866 }
867 absolutePath = absolutePath.left(i+1);
868 }
869#endif
870
871 QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts);
872 if ((pathElements.isEmpty() || !QFileInfo(path).exists())
873#if (!defined(Q_OS_WIN) || defined(Q_OS_WINCE)) && !defined(Q_OS_OS2)
874 && path != QLatin1String("/")
875#endif
876 )
877 return QModelIndex();
878
879 QModelIndex idx; // start with "My Computer"
880 if (!d->root.populated) // make sure the root is populated
881 d->populate(&d->root);
882
883#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_OS2)
884 if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path
885 QString host = pathElements.first();
886 int r = 0;
887 for (; r < d->root.children.count(); ++r)
888 if (d->root.children.at(r).info.fileName() == host)
889 break;
890 bool childAppended = false;
891 if (r >= d->root.children.count() && d->allowAppendChild) {
892 d->appendChild(&d->root, QLatin1String("//") + host);
893 childAppended = true;
894 }
895 idx = index(r, 0, QModelIndex());
896 pathElements.pop_front();
897 if (childAppended)
898 emit const_cast<QDirModel*>(this)->layoutChanged();
899 } else
900#endif
901#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) || defined(Q_OS_OS2)
902 if (pathElements.at(0).endsWith(QLatin1Char(':'))) {
903 pathElements[0] += QLatin1Char('/');
904 }
905#else
906 // add the "/" item, since it is a valid path element on unix
907 pathElements.prepend(QLatin1String("/"));
908#endif
909
910 for (int i = 0; i < pathElements.count(); ++i) {
911 Q_ASSERT(!pathElements.at(i).isEmpty());
912 QString element = pathElements.at(i);
913 QDirModelPrivate::QDirNode *parent = (idx.isValid() ? d->node(idx) : &d->root);
914
915 Q_ASSERT(parent);
916 if (!parent->populated)
917 d->populate(parent);
918
919 // search for the element in the child nodes first
920 int row = -1;
921 for (int j = parent->children.count() - 1; j >= 0; --j) {
922 const QFileInfo& fi = parent->children.at(j).info;
923 QString childFileName;
924 childFileName = idx.isValid() ? fi.fileName() : fi.absoluteFilePath();
925#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) || defined(Q_OS_OS2)
926 childFileName = childFileName.toLower();
927#endif
928 if (childFileName == element) {
929 if (i == pathElements.count() - 1)
930 parent->children[j].stat = true;
931 row = j;
932 break;
933 }
934 }
935
936 // we couldn't find the path element, we create a new node since we _know_ that the path is valid
937 if (row == -1) {
938#if defined(Q_OS_WINCE)
939 QString newPath;
940 if (parent->info.isRoot())
941 newPath = parent->info.absoluteFilePath() + element;
942 else
943 newPath = parent->info.absoluteFilePath() + QLatin1Char('/') + element;
944#else
945 QString newPath = parent->info.absoluteFilePath() + QLatin1Char('/') + element;
946#endif
947 if (!d->allowAppendChild || !QFileInfo(newPath).isDir())
948 return QModelIndex();
949 d->appendChild(parent, newPath);
950 row = parent->children.count() - 1;
951 if (i == pathElements.count() - 1) // always stat children of the last element
952 parent->children[row].stat = true;
953 emit const_cast<QDirModel*>(this)->layoutChanged();
954 }
955
956 Q_ASSERT(row >= 0);
957 idx = createIndex(row, 0, static_cast<void*>(&parent->children[row]));
958 Q_ASSERT(idx.isValid());
959 }
960
961 if (column != 0)
962 return idx.sibling(idx.row(), column);
963 return idx;
964}
965
966/*!
967 Returns true if the model item \a index represents a directory;
968 otherwise returns false.
969*/
970
971bool QDirModel::isDir(const QModelIndex &index) const
972{
973 Q_D(const QDirModel);
974 Q_ASSERT(d->indexValid(index));
975 QDirModelPrivate::QDirNode *node = d->node(index);
976 return node->info.isDir();
977}
978
979/*!
980 Create a directory with the \a name in the \a parent model item.
981*/
982
983QModelIndex QDirModel::mkdir(const QModelIndex &parent, const QString &name)
984{
985 Q_D(QDirModel);
986 if (!d->indexValid(parent) || isReadOnly())
987 return QModelIndex();
988
989 QDirModelPrivate::QDirNode *p = d->node(parent);
990 QString path = p->info.absoluteFilePath();
991 // For the indexOf() method to work, the new directory has to be a direct child of
992 // the parent directory.
993
994 QDir newDir(name);
995 QDir dir(path);
996 if (newDir.isRelative())
997 newDir = QDir(path + QLatin1Char('/') + name);
998 QString childName = newDir.dirName(); // Get the singular name of the directory
999 newDir.cdUp();
1000
1001 if (newDir.absolutePath() != dir.absolutePath() || !dir.mkdir(name))
1002 return QModelIndex(); // nothing happened
1003
1004 refresh(parent);
1005
1006 QStringList entryList = d->entryList(path);
1007 int r = entryList.indexOf(childName);
1008 QModelIndex i = index(r, 0, parent); // return an invalid index
1009
1010 return i;
1011}
1012
1013/*!
1014 Removes the directory corresponding to the model item \a index in the
1015 directory model and \bold{deletes the corresponding directory from the
1016 file system}, returning true if successful. If the directory cannot be
1017 removed, false is returned.
1018
1019 \warning This function deletes directories from the file system; it does
1020 \bold{not} move them to a location where they can be recovered.
1021
1022 \sa remove()
1023*/
1024
1025bool QDirModel::rmdir(const QModelIndex &index)
1026{
1027 Q_D(QDirModel);
1028 if (!d->indexValid(index) || isReadOnly())
1029 return false;
1030
1031 QDirModelPrivate::QDirNode *n = d_func()->node(index);
1032 if (!n->info.isDir()) {
1033 qWarning("rmdir: the node is not a directory");
1034 return false;
1035 }
1036
1037 QModelIndex par = parent(index);
1038 QDirModelPrivate::QDirNode *p = d_func()->node(par);
1039 QDir dir = p->info.dir(); // parent dir
1040 QString path = n->info.absoluteFilePath();
1041 if (!dir.rmdir(path))
1042 return false;
1043
1044 refresh(par);
1045
1046 return true;
1047}
1048
1049/*!
1050 Removes the model item \a index from the directory model and \bold{deletes the
1051 corresponding file from the file system}, returning true if successful. If the
1052 item cannot be removed, false is returned.
1053
1054 \warning This function deletes files from the file system; it does \bold{not}
1055 move them to a location where they can be recovered.
1056
1057 \sa rmdir()
1058*/
1059
1060bool QDirModel::remove(const QModelIndex &index)
1061{
1062 Q_D(QDirModel);
1063 if (!d->indexValid(index) || isReadOnly())
1064 return false;
1065
1066 QDirModelPrivate::QDirNode *n = d_func()->node(index);
1067 if (n->info.isDir())
1068 return false;
1069
1070 QModelIndex par = parent(index);
1071 QDirModelPrivate::QDirNode *p = d_func()->node(par);
1072 QDir dir = p->info.dir(); // parent dir
1073 QString path = n->info.absoluteFilePath();
1074 if (!dir.remove(path))
1075 return false;
1076
1077 refresh(par);
1078
1079 return true;
1080}
1081
1082/*!
1083 Returns the path of the item stored in the model under the
1084 \a index given.
1085
1086*/
1087
1088QString QDirModel::filePath(const QModelIndex &index) const
1089{
1090 Q_D(const QDirModel);
1091 if (d->indexValid(index)) {
1092 QFileInfo fi = fileInfo(index);
1093 if (d->resolveSymlinks && fi.isSymLink())
1094 fi = d->resolvedInfo(fi);
1095 return QDir::cleanPath(fi.absoluteFilePath());
1096 }
1097 return QString(); // root path
1098}
1099
1100/*!
1101 Returns the name of the item stored in the model under the
1102 \a index given.
1103
1104*/
1105
1106QString QDirModel::fileName(const QModelIndex &index) const
1107{
1108 Q_D(const QDirModel);
1109 if (!d->indexValid(index))
1110 return QString();
1111 QFileInfo info = fileInfo(index);
1112 if (info.isRoot())
1113 return info.absoluteFilePath();
1114 if (d->resolveSymlinks && info.isSymLink())
1115 info = d->resolvedInfo(info);
1116 return info.fileName();
1117}
1118
1119/*!
1120 Returns the icons for the item stored in the model under the given
1121 \a index.
1122*/
1123
1124QIcon QDirModel::fileIcon(const QModelIndex &index) const
1125{
1126 Q_D(const QDirModel);
1127 if (!d->indexValid(index))
1128 return d->iconProvider->icon(QFileIconProvider::Computer);
1129 QDirModelPrivate::QDirNode *node = d->node(index);
1130 if (node->icon.isNull())
1131 node->icon = d->iconProvider->icon(node->info);
1132 return node->icon;
1133}
1134
1135/*!
1136 Returns the file information for the specified model \a index.
1137
1138 \bold{Note:} If the model index represents a symbolic link in the
1139 underlying filing system, the file information returned will contain
1140 information about the symbolic link itself, regardless of whether
1141 resolveSymlinks is enabled or not.
1142
1143 \sa QFileInfo::symLinkTarget()
1144*/
1145
1146QFileInfo QDirModel::fileInfo(const QModelIndex &index) const
1147{
1148 Q_D(const QDirModel);
1149 Q_ASSERT(d->indexValid(index));
1150
1151 QDirModelPrivate::QDirNode *node = d->node(index);
1152 return node->info;
1153}
1154
1155/*!
1156 \fn QObject *QDirModel::parent() const
1157 \internal
1158*/
1159
1160/*
1161 The root node is never seen outside the model.
1162*/
1163
1164void QDirModelPrivate::init()
1165{
1166 Q_Q(QDirModel);
1167 filters = QDir::AllEntries | QDir::NoDotAndDotDot;
1168 sort = QDir::Name;
1169 nameFilters << QLatin1String("*");
1170 root.parent = 0;
1171 root.info = QFileInfo();
1172 clear(&root);
1173 QHash<int, QByteArray> roles = q->roleNames();
1174 roles.insertMulti(QDirModel::FileIconRole, "fileIcon"); // == Qt::decoration
1175 roles.insert(QDirModel::FilePathRole, "filePath");
1176 roles.insert(QDirModel::FileNameRole, "fileName");
1177 q->setRoleNames(roles);
1178}
1179
1180QDirModelPrivate::QDirNode *QDirModelPrivate::node(int row, QDirNode *parent) const
1181{
1182 if (row < 0)
1183 return 0;
1184
1185 bool isDir = !parent || parent->info.isDir();
1186 QDirNode *p = (parent ? parent : &root);
1187 if (isDir && !p->populated)
1188 populate(p); // will also resolve symlinks
1189
1190 if (row >= p->children.count()) {
1191 qWarning("node: the row does not exist");
1192 return 0;
1193 }
1194
1195 return const_cast<QDirNode*>(&p->children.at(row));
1196}
1197
1198QVector<QDirModelPrivate::QDirNode> QDirModelPrivate::children(QDirNode *parent, bool stat) const
1199{
1200 Q_ASSERT(parent);
1201 QFileInfoList infoList;
1202 if (parent == &root) {
1203 parent = 0;
1204 infoList = QDir::drives();
1205 } else if (parent->info.isDir()) {
1206 //resolve directory links only if requested.
1207 if (parent->info.isSymLink() && resolveSymlinks) {
1208 QString link = parent->info.symLinkTarget();
1209 if (link.size() > 1 && link.at(link.size() - 1) == QDir::separator())
1210 link.chop(1);
1211 if (stat)
1212 infoList = entryInfoList(link);
1213 else
1214 infoList = QDir(link).entryInfoList(nameFilters, QDir::AllEntries | QDir::System);
1215 } else {
1216 if (stat)
1217 infoList = entryInfoList(parent->info.absoluteFilePath());
1218 else
1219 infoList = QDir(parent->info.absoluteFilePath()).entryInfoList(nameFilters, QDir::AllEntries | QDir::System);
1220 }
1221 }
1222
1223 QVector<QDirNode> nodes(infoList.count());
1224 for (int i = 0; i < infoList.count(); ++i) {
1225 QDirNode &node = nodes[i];
1226 node.parent = parent;
1227 node.info = infoList.at(i);
1228 node.populated = false;
1229 node.stat = shouldStat;
1230 }
1231
1232 return nodes;
1233}
1234
1235void QDirModelPrivate::_q_refresh()
1236{
1237 Q_Q(QDirModel);
1238 q->refresh(toBeRefreshed);
1239 toBeRefreshed = QModelIndex();
1240}
1241
1242void QDirModelPrivate::savePersistentIndexes()
1243{
1244 Q_Q(QDirModel);
1245 savedPersistent.clear();
1246 foreach (QPersistentModelIndexData *data, persistent.indexes) {
1247 SavedPersistent saved;
1248 QModelIndex index = data->index;
1249 saved.path = q->filePath(index);
1250 saved.column = index.column();
1251 saved.data = data;
1252 saved.index = index;
1253 savedPersistent.append(saved);
1254 }
1255}
1256
1257void QDirModelPrivate::restorePersistentIndexes()
1258{
1259 Q_Q(QDirModel);
1260 bool allow = allowAppendChild;
1261 allowAppendChild = false;
1262 for (int i = 0; i < savedPersistent.count(); ++i) {
1263 QPersistentModelIndexData *data = savedPersistent.at(i).data;
1264 QString path = savedPersistent.at(i).path;
1265 int column = savedPersistent.at(i).column;
1266 QModelIndex idx = q->index(path, column);
1267 if (idx != data->index || data->model == 0) {
1268 //data->model may be equal to 0 if the model is getting destroyed
1269 persistent.indexes.remove(data->index);
1270 data->index = idx;
1271 data->model = q;
1272 if (idx.isValid())
1273 persistent.indexes.insert(idx, data);
1274 }
1275 }
1276 savedPersistent.clear();
1277 allowAppendChild = allow;
1278}
1279
1280QFileInfoList QDirModelPrivate::entryInfoList(const QString &path) const
1281{
1282 const QDir dir(path);
1283 return dir.entryInfoList(nameFilters, filters, sort);
1284}
1285
1286QStringList QDirModelPrivate::entryList(const QString &path) const
1287{
1288 const QDir dir(path);
1289 return dir.entryList(nameFilters, filters, sort);
1290}
1291
1292QString QDirModelPrivate::name(const QModelIndex &index) const
1293{
1294 const QDirNode *n = node(index);
1295 const QFileInfo info = n->info;
1296 if (info.isRoot()) {
1297 QString name = info.absoluteFilePath();
1298#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_OS2)
1299 if (name.startsWith(QLatin1Char('/'))) // UNC host
1300 return info.fileName();
1301#endif
1302#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) || defined(Q_OS_OS2)
1303 if (name.endsWith(QLatin1Char('/')))
1304 name.chop(1);
1305#endif
1306 return name;
1307 }
1308 return info.fileName();
1309}
1310
1311QString QDirModelPrivate::size(const QModelIndex &index) const
1312{
1313 const QDirNode *n = node(index);
1314 if (n->info.isDir()) {
1315#ifdef Q_OS_MAC
1316 return QLatin1String("--");
1317#else
1318 return QLatin1String("");
1319#endif
1320 // Windows - ""
1321 // OS X - "--"
1322 // Konqueror - "4 KB"
1323 // Nautilus - "9 items" (the number of children)
1324 }
1325
1326 // According to the Si standard KB is 1000 bytes, KiB is 1024
1327 // but on windows sizes are calulated by dividing by 1024 so we do what they do.
1328 const quint64 kb = 1024;
1329 const quint64 mb = 1024 * kb;
1330 const quint64 gb = 1024 * mb;
1331 const quint64 tb = 1024 * gb;
1332 quint64 bytes = n->info.size();
1333 if (bytes >= tb)
1334 return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3));
1335 if (bytes >= gb)
1336 return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2));
1337 if (bytes >= mb)
1338 return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1));
1339 if (bytes >= kb)
1340 return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb));
1341 return QFileSystemModel::tr("%1 byte(s)").arg(QLocale().toString(bytes));
1342}
1343
1344QString QDirModelPrivate::type(const QModelIndex &index) const
1345{
1346 return iconProvider->type(node(index)->info);
1347}
1348
1349QString QDirModelPrivate::time(const QModelIndex &index) const
1350{
1351#ifndef QT_NO_DATESTRING
1352 return node(index)->info.lastModified().toString(Qt::LocalDate);
1353#else
1354 Q_UNUSED(index);
1355 return QString();
1356#endif
1357}
1358
1359void QDirModelPrivate::appendChild(QDirModelPrivate::QDirNode *parent, const QString &path) const
1360{
1361 QDirModelPrivate::QDirNode node;
1362 node.populated = false;
1363 node.stat = shouldStat;
1364 node.parent = (parent == &root ? 0 : parent);
1365 node.info = QFileInfo(path);
1366 node.info.setCaching(true);
1367
1368 // The following append(node) may reallocate the vector, thus
1369 // we need to update the pointers to the childnodes parent.
1370 QDirModelPrivate *that = const_cast<QDirModelPrivate *>(this);
1371 that->savePersistentIndexes();
1372 parent->children.append(node);
1373 for (int i = 0; i < parent->children.count(); ++i) {
1374 QDirNode *childNode = &parent->children[i];
1375 for (int j = 0; j < childNode->children.count(); ++j)
1376 childNode->children[j].parent = childNode;
1377 }
1378 that->restorePersistentIndexes();
1379}
1380
1381QFileInfo QDirModelPrivate::resolvedInfo(QFileInfo info)
1382{
1383#ifdef Q_OS_WIN
1384 // On windows, we cannot create a shortcut to a shortcut.
1385 return QFileInfo(info.symLinkTarget());
1386#else
1387 QStringList paths;
1388 do {
1389 QFileInfo link(info.symLinkTarget());
1390 if (link.isRelative())
1391 info.setFile(info.absolutePath(), link.filePath());
1392 else
1393 info = link;
1394 if (paths.contains(info.absoluteFilePath()))
1395 return QFileInfo();
1396 paths.append(info.absoluteFilePath());
1397 } while (info.isSymLink());
1398 return info;
1399#endif
1400}
1401
1402QT_END_NAMESPACE
1403
1404#include "moc_qdirmodel.cpp"
1405
1406#endif // QT_NO_DIRMODEL
Note: See TracBrowser for help on using the repository browser.