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

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

trunk: Merged in qt 4.6.2 sources.

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