Imported Upstream version 1.1.0
[gammaray-debian.git] / 3rdparty / qt / resourcemodel.cpp
blobe60248622afa8e80e3e09fd377e6c8065a91c21f
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
42 #include "resourcemodel.h"
44 #include <qstack.h>
45 #include <qfile.h>
46 #include <qfilesystemmodel.h>
47 #include <qurl.h>
48 #include <qmimedata.h>
49 #include <qpair.h>
50 #include <qvector.h>
51 #include <qobject.h>
52 #include <qdatetime.h>
53 #include <qlocale.h>
54 #include <qstyle.h>
55 #include <qapplication.h>
56 #include <qdebug.h>
58 /*!
59 \enum ResourceModel::Roles
60 \value FileIconRole
61 \value FilePathRole
62 \value FileNameRole
65 QT_BEGIN_NAMESPACE
67 class ResourceModelPrivate
69 Q_DECLARE_PUBLIC(ResourceModel)
70 ResourceModel * const q_ptr;
72 public:
73 struct QDirNode
75 QDirNode() : parent(0), populated(false), stat(false) {}
76 ~QDirNode() { children.clear(); }
77 QDirNode *parent;
78 QFileInfo info;
79 QIcon icon; // cache the icon
80 mutable QVector<QDirNode> children;
81 mutable bool populated; // have we read the children
82 mutable bool stat;
85 ResourceModelPrivate(ResourceModel *qq)
86 : resolveSymlinks(true),
87 readOnly(true),
88 lazyChildCount(false),
89 allowAppendChild(true),
90 iconProvider(&defaultProvider),
91 shouldStat(true), // ### This is set to false by QFileDialog
92 q_ptr(qq)
93 { }
95 bool indexValid(const QModelIndex &index) const { return index.isValid(); }
97 void init();
98 QDirNode *node(int row, QDirNode *parent) const;
99 QVector<QDirNode> children(QDirNode *parent, bool stat) const;
101 void _q_refresh();
103 void savePersistentIndexes();
104 void restorePersistentIndexes();
106 QFileInfoList entryInfoList(const QString &path) const;
107 QStringList entryList(const QString &path) const;
109 QString name(const QModelIndex &index) const;
110 QString size(const QModelIndex &index) const;
111 QString type(const QModelIndex &index) const;
112 QString time(const QModelIndex &index) const;
114 void appendChild(ResourceModelPrivate::QDirNode *parent, const QString &path) const;
115 static QFileInfo resolvedInfo(QFileInfo info);
117 inline QDirNode *node(const QModelIndex &index) const;
118 inline void populate(QDirNode *parent) const;
119 inline void clear(QDirNode *parent) const;
121 void invalidate();
123 mutable QDirNode root;
124 bool resolveSymlinks;
125 bool readOnly;
126 bool lazyChildCount;
127 bool allowAppendChild;
129 QDir::Filters filters;
130 QDir::SortFlags sort;
131 QStringList nameFilters;
133 QFileIconProvider *iconProvider;
134 QFileIconProvider defaultProvider;
136 struct SavedPersistent {
137 QString path;
138 int column;
139 QPersistentModelIndexData *data;
140 QPersistentModelIndex index;
142 QList<SavedPersistent> savedPersistent;
143 QPersistentModelIndex toBeRefreshed;
145 bool shouldStat; // use the "carefull not to stat directories" mode
148 void qt_setDirModelShouldNotStat(ResourceModelPrivate *modelPrivate)
150 modelPrivate->shouldStat = false;
153 ResourceModelPrivate::QDirNode *ResourceModelPrivate::node(const QModelIndex &index) const
155 ResourceModelPrivate::QDirNode *n =
156 static_cast<ResourceModelPrivate::QDirNode*>(index.internalPointer());
157 Q_ASSERT(n);
158 return n;
161 void ResourceModelPrivate::populate(QDirNode *parent) const
163 Q_ASSERT(parent);
164 parent->children = children(parent, parent->stat);
165 parent->populated = true;
168 void ResourceModelPrivate::clear(QDirNode *parent) const
170 Q_ASSERT(parent);
171 parent->children.clear();
172 parent->populated = false;
175 void ResourceModelPrivate::invalidate()
177 QStack<const QDirNode*> nodes;
178 nodes.push(&root);
179 while (!nodes.empty()) {
180 const QDirNode *current = nodes.pop();
181 current->stat = false;
182 const QVector<QDirNode> children = current->children;
183 for (int i = 0; i < children.count(); ++i)
184 nodes.push(&children.at(i));
189 \class ResourceModel
190 \obsolete
191 \brief The ResourceModel class provides a data model for the local filesystem.
193 \ingroup model-view
195 The usage of ResourceModel is not recommended anymore. The
196 QFileSystemModel class is a more performant alternative.
198 This class provides access to the local filesystem, providing functions
199 for renaming and removing files and directories, and for creating new
200 directories. In the simplest case, it can be used with a suitable display
201 widget as part of a browser or filer.
203 ResourceModel keeps a cache with file information. The cache needs to be
204 updated with refresh().
206 ResourceModel can be accessed using the standard interface provided by
207 QAbstractItemModel, but it also provides some convenience functions
208 that are specific to a directory model. The fileInfo() and isDir()
209 functions provide information about the underlying files and directories
210 related to items in the model.
212 Directories can be created and removed using mkdir(), rmdir(), and the
213 model will be automatically updated to take the changes into account.
215 \note ResourceModel requires an instance of a GUI application.
217 \sa nameFilters(), setFilter(), filter(), QListView, QTreeView, QFileSystemModel,
218 {Dir View Example}, {Model Classes}
222 Constructs a new directory model with the given \a parent.
223 Only those files matching the \a nameFilters and the
224 \a filters are included in the model. The sort order is given by the
225 \a sort flags.
228 ResourceModel::ResourceModel(const QStringList &nameFilters,
229 QDir::Filters filters,
230 QDir::SortFlags sort,
231 QObject *parent)
232 : QAbstractItemModel(parent), d_ptr(new ResourceModelPrivate(this))
234 Q_D(ResourceModel);
235 // we always start with QDir::drives()
236 d->nameFilters = nameFilters.isEmpty() ? QStringList(QLatin1String("*")) : nameFilters;
237 d->filters = filters;
238 d->sort = sort;
239 d->root.parent = 0;
240 d->root.info = QFileInfo();
241 d->clear(&d->root);
245 Constructs a directory model with the given \a parent.
248 ResourceModel::ResourceModel(QObject *parent)
249 : QAbstractItemModel(parent), d_ptr(new ResourceModelPrivate(this))
251 Q_D(ResourceModel);
252 d->init();
256 Destroys this directory model.
259 ResourceModel::~ResourceModel()
265 Returns the model item index for the item in the \a parent with the
266 given \a row and \a column.
270 QModelIndex ResourceModel::index(int row, int column, const QModelIndex &parent) const
272 Q_D(const ResourceModel);
273 // note that rowCount does lazy population
274 if (column < 0 || column >= columnCount(parent) || row < 0 || parent.column() > 0)
275 return QModelIndex();
276 // make sure the list of children is up to date
277 ResourceModelPrivate::QDirNode *p = (d->indexValid(parent) ? d->node(parent) : &d->root);
278 Q_ASSERT(p);
279 if (!p->populated)
280 d->populate(p); // populate without stat'ing
281 if (row >= p->children.count())
282 return QModelIndex();
283 // now get the internal pointer for the index
284 ResourceModelPrivate::QDirNode *n = d->node(row, d->indexValid(parent) ? p : 0);
285 Q_ASSERT(n);
287 return createIndex(row, column, n);
291 Return the parent of the given \a child model item.
294 QModelIndex ResourceModel::parent(const QModelIndex &child) const
296 Q_D(const ResourceModel);
298 if (!d->indexValid(child))
299 return QModelIndex();
300 ResourceModelPrivate::QDirNode *node = d->node(child);
301 ResourceModelPrivate::QDirNode *par = (node ? node->parent : 0);
302 if (par == 0) // parent is the root node
303 return QModelIndex();
305 // get the parent's row
306 const QVector<ResourceModelPrivate::QDirNode> children =
307 par->parent ? par->parent->children : d->root.children;
308 Q_ASSERT(children.count() > 0);
309 int row = (par - &(children.at(0)));
310 Q_ASSERT(row >= 0);
312 return createIndex(row, 0, par);
316 Returns the number of rows in the \a parent model item.
320 int ResourceModel::rowCount(const QModelIndex &parent) const
322 Q_D(const ResourceModel);
323 if (parent.column() > 0)
324 return 0;
326 if (!parent.isValid()) {
327 // qDebug() << "Root" << d->root;
328 if (!d->root.populated) // lazy population
329 d->populate(&d->root);
330 return d->root.children.count();
332 if (parent.model() != this)
333 return 0;
334 ResourceModelPrivate::QDirNode *p = d->node(parent);
335 if (p->info.isDir() && !p->populated) // lazy population
336 d->populate(p);
337 return p->children.count();
341 Returns the number of columns in the \a parent model item.
345 int ResourceModel::columnCount(const QModelIndex &parent) const
347 if (parent.column() > 0)
348 return 0;
349 return 4;
353 Returns the data for the model item \a index with the given \a role.
355 QVariant ResourceModel::data(const QModelIndex &index, int role) const
357 Q_D(const ResourceModel);
358 if (!d->indexValid(index))
359 return QVariant();
361 if (role == Qt::DisplayRole || role == Qt::EditRole) {
362 switch (index.column()) {
363 case 0: return d->name(index);
364 case 1: return d->size(index);
365 case 2: return d->type(index);
366 case 3: return d->time(index);
367 default:
368 qWarning("data: invalid display value column %d", index.column());
369 return QVariant();
373 if (index.column() == 0) {
374 if (role == FileIconRole)
375 return fileIcon(index);
376 if (role == FilePathRole)
377 return filePath(index);
378 if (role == FileNameRole)
379 return fileName(index);
382 if (index.column() == 1 && Qt::TextAlignmentRole == role) {
383 return Qt::AlignRight;
385 return QVariant();
389 Sets the data for the model item \a index with the given \a role to
390 the data referenced by the \a value. Returns true if successful;
391 otherwise returns false.
393 \sa Qt::ItemDataRole
396 bool ResourceModel::setData(const QModelIndex &index, const QVariant &value, int role)
398 Q_D(ResourceModel);
399 if (!d->indexValid(index) || index.column() != 0
400 || (flags(index) & Qt::ItemIsEditable) == 0 || role != Qt::EditRole)
401 return false;
403 ResourceModelPrivate::QDirNode *node = d->node(index);
404 QDir dir = node->info.dir();
405 QString name = value.toString();
406 if (dir.rename(node->info.fileName(), name)) {
407 node->info = QFileInfo(dir, name);
408 QModelIndex sibling = index.sibling(index.row(), 3);
409 emit dataChanged(index, sibling);
411 d->toBeRefreshed = index.parent();
412 QMetaObject::invokeMethod(this, "_q_refresh", Qt::QueuedConnection);
414 return true;
417 return false;
421 Returns the data stored under the given \a role for the specified \a section
422 of the header with the given \a orientation.
425 QVariant ResourceModel::headerData(int section, Qt::Orientation orientation, int role) const
427 if (orientation == Qt::Horizontal) {
428 if (role != Qt::DisplayRole)
429 return QVariant();
430 switch (section) {
431 case 0: return tr("Name");
432 case 1: return tr("Size");
433 case 2: return
434 #ifdef Q_OS_MAC
435 tr("Kind", "Match OS X Finder");
436 #else
437 tr("Type", "All other platforms");
438 #endif
439 // Windows - Type
440 // OS X - Kind
441 // Konqueror - File Type
442 // Nautilus - Type
443 case 3: return tr("Date Modified");
444 default: return QVariant();
447 return QAbstractItemModel::headerData(section, orientation, role);
451 Returns true if the \a parent model item has children; otherwise
452 returns false.
455 bool ResourceModel::hasChildren(const QModelIndex &parent) const
457 Q_D(const ResourceModel);
458 if (parent.column() > 0)
459 return false;
461 if (!parent.isValid()) // the invalid index is the "My Computer" item
462 return true; // the drives
463 ResourceModelPrivate::QDirNode *p = d->node(parent);
464 Q_ASSERT(p);
466 if (d->lazyChildCount) // optimization that only checks for children if the node has been populated
467 return p->info.isDir();
468 return p->info.isDir() && rowCount(parent) > 0;
472 Returns the item flags for the given \a index in the model.
474 \sa Qt::ItemFlags
476 Qt::ItemFlags ResourceModel::flags(const QModelIndex &index) const
478 Q_D(const ResourceModel);
479 Qt::ItemFlags flags = QAbstractItemModel::flags(index);
480 if (!d->indexValid(index))
481 return flags;
482 flags |= Qt::ItemIsDragEnabled;
483 if (d->readOnly)
484 return flags;
485 ResourceModelPrivate::QDirNode *node = d->node(index);
486 if ((index.column() == 0) && node->info.isWritable()) {
487 flags |= Qt::ItemIsEditable;
488 if (fileInfo(index).isDir()) // is directory and is editable
489 flags |= Qt::ItemIsDropEnabled;
491 return flags;
495 Sort the model items in the \a column using the \a order given.
496 The order is a value defined in \l Qt::SortOrder.
499 void ResourceModel::sort(int column, Qt::SortOrder order)
501 QDir::SortFlags sort = QDir::DirsFirst | QDir::IgnoreCase;
502 if (order == Qt::DescendingOrder)
503 sort |= QDir::Reversed;
505 switch (column) {
506 case 0:
507 sort |= QDir::Name;
508 break;
509 case 1:
510 sort |= QDir::Size;
511 break;
512 case 2:
513 sort |= QDir::Type;
514 break;
515 case 3:
516 sort |= QDir::Time;
517 break;
518 default:
519 break;
522 setSorting(sort);
526 Returns a list of MIME types that can be used to describe a list of items
527 in the model.
530 QStringList ResourceModel::mimeTypes() const
532 return QStringList(QLatin1String("text/uri-list"));
536 Returns an object that contains a serialized description of the specified
537 \a indexes. The format used to describe the items corresponding to the
538 indexes is obtained from the mimeTypes() function.
540 If the list of indexes is empty, 0 is returned rather than a serialized
541 empty list.
544 QMimeData *ResourceModel::mimeData(const QModelIndexList &indexes) const
546 QList<QUrl> urls;
547 QList<QModelIndex>::const_iterator it = indexes.begin();
548 for (; it != indexes.end(); ++it)
549 if ((*it).column() == 0)
550 urls << QUrl::fromLocalFile(filePath(*it));
551 QMimeData *data = new QMimeData();
552 data->setUrls(urls);
553 return data;
557 Handles the \a data supplied by a drag and drop operation that ended with
558 the given \a action over the row in the model specified by the \a row and
559 \a column and by the \a parent index.
561 \sa supportedDropActions()
564 bool ResourceModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
565 int /* row */, int /* column */, const QModelIndex &parent)
567 Q_D(ResourceModel);
568 if (!d->indexValid(parent) || isReadOnly())
569 return false;
571 bool success = true;
572 QString to = filePath(parent) + QDir::separator();
573 QModelIndex _parent = parent;
575 QList<QUrl> urls = data->urls();
576 QList<QUrl>::const_iterator it = urls.constBegin();
578 switch (action) {
579 case Qt::CopyAction:
580 for (; it != urls.constEnd(); ++it) {
581 QString path = (*it).toLocalFile();
582 success = QFile::copy(path, to + QFileInfo(path).fileName()) && success;
584 break;
585 case Qt::LinkAction:
586 for (; it != urls.constEnd(); ++it) {
587 QString path = (*it).toLocalFile();
588 success = QFile::link(path, to + QFileInfo(path).fileName()) && success;
590 break;
591 case Qt::MoveAction:
592 for (; it != urls.constEnd(); ++it) {
593 QString path = (*it).toLocalFile();
594 if (QFile::copy(path, to + QFileInfo(path).fileName())
595 && QFile::remove(path)) {
596 QModelIndex idx=index(QFileInfo(path).path());
597 if (idx.isValid()) {
598 refresh(idx);
599 //the previous call to refresh may invalidate the _parent. so recreate a new QModelIndex
600 _parent = index(to);
602 } else {
603 success = false;
606 break;
607 default:
608 return false;
611 if (success)
612 refresh(_parent);
614 return success;
618 Returns the drop actions supported by this model.
620 \sa Qt::DropActions
623 Qt::DropActions ResourceModel::supportedDropActions() const
625 return Qt::CopyAction | Qt::MoveAction; // FIXME: LinkAction is not supported yet
629 Sets the \a provider of file icons for the directory model.
633 void ResourceModel::setIconProvider(QFileIconProvider *provider)
635 Q_D(ResourceModel);
636 d->iconProvider = provider;
640 Returns the file icon provider for this directory model.
643 QFileIconProvider *ResourceModel::iconProvider() const
645 Q_D(const ResourceModel);
646 return d->iconProvider;
650 Sets the name \a filters for the directory model.
653 void ResourceModel::setNameFilters(const QStringList &filters)
655 Q_D(ResourceModel);
656 d->nameFilters = filters;
657 emit layoutAboutToBeChanged();
658 if (d->shouldStat)
659 refresh(QModelIndex());
660 else
661 d->invalidate();
662 emit layoutChanged();
666 Returns a list of filters applied to the names in the model.
669 QStringList ResourceModel::nameFilters() const
671 Q_D(const ResourceModel);
672 return d->nameFilters;
676 Sets the directory model's filter to that specified by \a filters.
678 Note that the filter you set should always include the QDir::AllDirs enum value,
679 otherwise ResourceModel won't be able to read the directory structure.
681 \sa QDir::Filters
684 void ResourceModel::setFilter(QDir::Filters filters)
686 Q_D(ResourceModel);
687 d->filters = filters;
688 emit layoutAboutToBeChanged();
689 if (d->shouldStat)
690 refresh(QModelIndex());
691 else
692 d->invalidate();
693 emit layoutChanged();
697 Returns the filter specification for the directory model.
699 \sa QDir::Filters
702 QDir::Filters ResourceModel::filter() const
704 Q_D(const ResourceModel);
705 return d->filters;
709 Sets the directory model's sorting order to that specified by \a sort.
711 \sa QDir::SortFlags
714 void ResourceModel::setSorting(QDir::SortFlags sort)
716 Q_D(ResourceModel);
717 d->sort = sort;
718 emit layoutAboutToBeChanged();
719 if (d->shouldStat)
720 refresh(QModelIndex());
721 else
722 d->invalidate();
723 emit layoutChanged();
727 Returns the sorting method used for the directory model.
729 \sa QDir::SortFlags */
731 QDir::SortFlags ResourceModel::sorting() const
733 Q_D(const ResourceModel);
734 return d->sort;
738 \property ResourceModel::resolveSymlinks
739 \brief Whether the directory model should resolve symbolic links
741 This is only relevant on operating systems that support symbolic
742 links.
744 void ResourceModel::setResolveSymlinks(bool enable)
746 Q_D(ResourceModel);
747 d->resolveSymlinks = enable;
750 bool ResourceModel::resolveSymlinks() const
752 Q_D(const ResourceModel);
753 return d->resolveSymlinks;
757 \property ResourceModel::readOnly
758 \brief Whether the directory model allows writing to the file system
760 If this property is set to false, the directory model will allow renaming, copying
761 and deleting of files and directories.
763 This property is true by default
766 void ResourceModel::setReadOnly(bool enable)
768 Q_D(ResourceModel);
769 d->readOnly = enable;
772 bool ResourceModel::isReadOnly() const
774 Q_D(const ResourceModel);
775 return d->readOnly;
779 \property ResourceModel::lazyChildCount
780 \brief Whether the directory model optimizes the hasChildren function
781 to only check if the item is a directory.
783 If this property is set to false, the directory model will make sure that a directory
784 actually containes any files before reporting that it has children.
785 Otherwise the directory model will report that an item has children if the item
786 is a directory.
788 This property is false by default
791 void ResourceModel::setLazyChildCount(bool enable)
793 Q_D(ResourceModel);
794 d->lazyChildCount = enable;
797 bool ResourceModel::lazyChildCount() const
799 Q_D(const ResourceModel);
800 return d->lazyChildCount;
804 ResourceModel caches file information. This function updates the
805 cache. The \a parent parameter is the directory from which the
806 model is updated; the default value will update the model from
807 root directory of the file system (the entire model).
810 void ResourceModel::refresh(const QModelIndex &parent)
812 Q_D(ResourceModel);
814 ResourceModelPrivate::QDirNode *n = d->indexValid(parent) ? d->node(parent) : &(d->root);
816 int rows = n->children.count();
817 if (rows == 0) {
818 emit layoutAboutToBeChanged();
819 n->stat = true; // make sure that next time we read all the info
820 n->populated = false;
821 emit layoutChanged();
822 return;
825 beginResetModel();
826 // emit layoutAboutToBeChanged();
827 d->savePersistentIndexes();
828 // d->rowsAboutToBeRemoved(parent, 0, rows - 1);
829 n->stat = true; // make sure that next time we read all the info
830 d->clear(n);
831 // d->rowsRemoved(parent, 0, rows - 1);
832 d->restorePersistentIndexes();
833 // emit layoutChanged();
834 endResetModel();
838 \overload
840 Returns the model item index for the given \a path.
843 QModelIndex ResourceModel::index(const QString &path, int column) const
845 Q_D(const ResourceModel);
847 if (path.isEmpty() || path == QCoreApplication::translate("QFileDialog", "My Computer"))
848 return QModelIndex();
850 QString absolutePath = QDir(path).absolutePath();
851 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN)
852 absolutePath = absolutePath.toLower();
853 #endif
854 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
855 // On Windows, "filename......." and "filename" are equivalent
856 if (absolutePath.endsWith(QLatin1Char('.'))) {
857 int i;
858 for (i = absolutePath.count() - 1; i >= 0; --i) {
859 if (absolutePath.at(i) != QLatin1Char('.'))
860 break;
862 absolutePath = absolutePath.left(i+1);
864 #endif
866 QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts);
867 if ((pathElements.isEmpty() || !QFileInfo(path).exists())
868 #if !defined(Q_OS_WIN) || defined(Q_OS_WINCE)
869 && path != QLatin1String("/")
870 #endif
872 return QModelIndex();
874 QModelIndex idx; // start with "My Computer"
875 if (!d->root.populated) // make sure the root is populated
876 d->populate(&d->root);
878 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
879 if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path
880 QString host = pathElements.first();
881 int r = 0;
882 for (; r < d->root.children.count(); ++r)
883 if (d->root.children.at(r).info.fileName() == host)
884 break;
885 bool childAppended = false;
886 if (r >= d->root.children.count() && d->allowAppendChild) {
887 d->appendChild(&d->root, QLatin1String("//") + host);
888 childAppended = true;
890 idx = index(r, 0, QModelIndex());
891 pathElements.pop_front();
892 if (childAppended)
893 emit const_cast<ResourceModel*>(this)->layoutChanged();
894 } else
895 #endif
896 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN)
897 if (pathElements.at(0).endsWith(QLatin1Char(':'))) {
898 pathElements[0] += QLatin1Char('/');
900 #else
901 // add the "/" item, since it is a valid path element on unix
902 pathElements.prepend(QLatin1String("/"));
903 #endif
905 for (int i = 0; i < pathElements.count(); ++i) {
906 Q_ASSERT(!pathElements.at(i).isEmpty());
907 QString element = pathElements.at(i);
908 ResourceModelPrivate::QDirNode *parent = (idx.isValid() ? d->node(idx) : &d->root);
910 Q_ASSERT(parent);
911 if (!parent->populated)
912 d->populate(parent);
914 // search for the element in the child nodes first
915 int row = -1;
916 for (int j = parent->children.count() - 1; j >= 0; --j) {
917 const QFileInfo& fi = parent->children.at(j).info;
918 QString childFileName;
919 childFileName = idx.isValid() ? fi.fileName() : fi.absoluteFilePath();
920 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN)
921 childFileName = childFileName.toLower();
922 #endif
923 if (childFileName == element) {
924 if (i == pathElements.count() - 1)
925 parent->children[j].stat = true;
926 row = j;
927 break;
931 // we couldn't find the path element, we create a new node since we _know_ that the path is valid
932 if (row == -1) {
933 #if defined(Q_OS_WINCE)
934 QString newPath;
935 if (parent->info.isRoot())
936 newPath = parent->info.absoluteFilePath() + element;
937 else
938 newPath = parent->info.absoluteFilePath() + QLatin1Char('/') + element;
939 #else
940 QString newPath = parent->info.absoluteFilePath() + QLatin1Char('/') + element;
941 #endif
942 if (!d->allowAppendChild || !QFileInfo(newPath).isDir())
943 return QModelIndex();
944 d->appendChild(parent, newPath);
945 row = parent->children.count() - 1;
946 if (i == pathElements.count() - 1) // always stat children of the last element
947 parent->children[row].stat = true;
948 emit const_cast<ResourceModel*>(this)->layoutChanged();
951 Q_ASSERT(row >= 0);
952 idx = createIndex(row, 0, static_cast<void*>(&parent->children[row]));
953 Q_ASSERT(idx.isValid());
956 if (column != 0)
957 return idx.sibling(idx.row(), column);
958 return idx;
962 Returns true if the model item \a index represents a directory;
963 otherwise returns false.
966 bool ResourceModel::isDir(const QModelIndex &index) const
968 Q_D(const ResourceModel);
969 Q_ASSERT(d->indexValid(index));
970 ResourceModelPrivate::QDirNode *node = d->node(index);
971 return node->info.isDir();
975 Create a directory with the \a name in the \a parent model item.
978 QModelIndex ResourceModel::mkdir(const QModelIndex &parent, const QString &name)
980 Q_D(ResourceModel);
981 if (!d->indexValid(parent) || isReadOnly())
982 return QModelIndex();
984 ResourceModelPrivate::QDirNode *p = d->node(parent);
985 QString path = p->info.absoluteFilePath();
986 // For the indexOf() method to work, the new directory has to be a direct child of
987 // the parent directory.
989 QDir newDir(name);
990 QDir dir(path);
991 if (newDir.isRelative())
992 newDir = QDir(path + QLatin1Char('/') + name);
993 QString childName = newDir.dirName(); // Get the singular name of the directory
994 newDir.cdUp();
996 if (newDir.absolutePath() != dir.absolutePath() || !dir.mkdir(name))
997 return QModelIndex(); // nothing happened
999 refresh(parent);
1001 QStringList entryList = d->entryList(path);
1002 int r = entryList.indexOf(childName);
1003 QModelIndex i = index(r, 0, parent); // return an invalid index
1005 return i;
1009 Removes the directory corresponding to the model item \a index in the
1010 directory model and \bold{deletes the corresponding directory from the
1011 file system}, returning true if successful. If the directory cannot be
1012 removed, false is returned.
1014 \warning This function deletes directories from the file system; it does
1015 \bold{not} move them to a location where they can be recovered.
1017 \sa remove()
1020 bool ResourceModel::rmdir(const QModelIndex &index)
1022 Q_D(ResourceModel);
1023 if (!d->indexValid(index) || isReadOnly())
1024 return false;
1026 ResourceModelPrivate::QDirNode *n = d_func()->node(index);
1027 if (!n->info.isDir()) {
1028 qWarning("rmdir: the node is not a directory");
1029 return false;
1032 QModelIndex par = parent(index);
1033 ResourceModelPrivate::QDirNode *p = d_func()->node(par);
1034 QDir dir = p->info.dir(); // parent dir
1035 QString path = n->info.absoluteFilePath();
1036 if (!dir.rmdir(path))
1037 return false;
1039 refresh(par);
1041 return true;
1045 Removes the model item \a index from the directory model and \bold{deletes the
1046 corresponding file from the file system}, returning true if successful. If the
1047 item cannot be removed, false is returned.
1049 \warning This function deletes files from the file system; it does \bold{not}
1050 move them to a location where they can be recovered.
1052 \sa rmdir()
1055 bool ResourceModel::remove(const QModelIndex &index)
1057 Q_D(ResourceModel);
1058 if (!d->indexValid(index) || isReadOnly())
1059 return false;
1061 ResourceModelPrivate::QDirNode *n = d_func()->node(index);
1062 if (n->info.isDir())
1063 return false;
1065 QModelIndex par = parent(index);
1066 ResourceModelPrivate::QDirNode *p = d_func()->node(par);
1067 QDir dir = p->info.dir(); // parent dir
1068 QString path = n->info.absoluteFilePath();
1069 if (!dir.remove(path))
1070 return false;
1072 refresh(par);
1074 return true;
1078 Returns the path of the item stored in the model under the
1079 \a index given.
1083 QString ResourceModel::filePath(const QModelIndex &index) const
1085 Q_D(const ResourceModel);
1086 if (d->indexValid(index)) {
1087 QFileInfo fi = fileInfo(index);
1088 if (d->resolveSymlinks && fi.isSymLink())
1089 fi = d->resolvedInfo(fi);
1090 return QDir::cleanPath(fi.absoluteFilePath());
1092 return QString(); // root path
1096 Returns the name of the item stored in the model under the
1097 \a index given.
1101 QString ResourceModel::fileName(const QModelIndex &index) const
1103 Q_D(const ResourceModel);
1104 if (!d->indexValid(index))
1105 return QString();
1106 QFileInfo info = fileInfo(index);
1107 if (info.isRoot())
1108 return info.absoluteFilePath();
1109 if (d->resolveSymlinks && info.isSymLink())
1110 info = d->resolvedInfo(info);
1111 return info.fileName();
1115 Returns the icons for the item stored in the model under the given
1116 \a index.
1119 QIcon ResourceModel::fileIcon(const QModelIndex &index) const
1121 Q_D(const ResourceModel);
1122 if (!d->indexValid(index))
1123 return d->iconProvider->icon(QFileIconProvider::Computer);
1124 ResourceModelPrivate::QDirNode *node = d->node(index);
1125 if (node->icon.isNull())
1126 node->icon = d->iconProvider->icon(node->info);
1127 return node->icon;
1131 Returns the file information for the specified model \a index.
1133 \bold{Note:} If the model index represents a symbolic link in the
1134 underlying filing system, the file information returned will contain
1135 information about the symbolic link itself, regardless of whether
1136 resolveSymlinks is enabled or not.
1138 \sa QFileInfo::symLinkTarget()
1141 QFileInfo ResourceModel::fileInfo(const QModelIndex &index) const
1143 Q_D(const ResourceModel);
1144 Q_ASSERT(d->indexValid(index));
1146 ResourceModelPrivate::QDirNode *node = d->node(index);
1147 return node->info;
1151 \fn QObject *ResourceModel::parent() const
1152 \internal
1156 The root node is never seen outside the model.
1159 void ResourceModelPrivate::init()
1161 Q_Q(ResourceModel);
1162 filters = QDir::AllEntries | QDir::NoDotAndDotDot;
1163 sort = QDir::Name;
1164 nameFilters << QLatin1String("*");
1165 root.parent = 0;
1166 root.info = QFileInfo(":");
1167 clear(&root);
1168 QHash<int, QByteArray> roles = q->roleNames();
1169 roles.insertMulti(ResourceModel::FileIconRole, "fileIcon"); // == Qt::decoration
1170 roles.insert(ResourceModel::FilePathRole, "filePath");
1171 roles.insert(ResourceModel::FileNameRole, "fileName");
1172 q->setRoleNames(roles);
1175 ResourceModelPrivate::QDirNode *ResourceModelPrivate::node(int row, QDirNode *parent) const
1177 if (row < 0)
1178 return 0;
1180 bool isDir = !parent || parent->info.isDir();
1181 QDirNode *p = (parent ? parent : &root);
1182 if (isDir && !p->populated)
1183 populate(p); // will also resolve symlinks
1185 if (row >= p->children.count()) {
1186 qWarning("node: the row does not exist");
1187 return 0;
1190 return const_cast<QDirNode*>(&p->children.at(row));
1193 QVector<ResourceModelPrivate::QDirNode> ResourceModelPrivate::children(QDirNode *parent, bool stat) const
1195 Q_ASSERT(parent);
1196 QFileInfoList infoList;
1197 if (parent == &root) {
1198 parent = 0;
1199 infoList.append(root.info);
1200 } else if (parent->info.isDir()) {
1201 //resolve directory links only if requested.
1202 if (parent->info.isSymLink() && resolveSymlinks) {
1203 QString link = parent->info.symLinkTarget();
1204 if (link.size() > 1 && link.at(link.size() - 1) == QDir::separator())
1205 link.chop(1);
1206 if (stat)
1207 infoList = entryInfoList(link);
1208 else
1209 infoList = QDir(link).entryInfoList(nameFilters, QDir::AllEntries | QDir::System);
1210 } else {
1211 if (stat)
1212 infoList = entryInfoList(parent->info.absoluteFilePath());
1213 else
1214 infoList = QDir(parent->info.absoluteFilePath()).entryInfoList(nameFilters, QDir::AllEntries | QDir::System);
1218 QVector<QDirNode> nodes(infoList.count());
1219 for (int i = 0; i < infoList.count(); ++i) {
1220 QDirNode &node = nodes[i];
1221 node.parent = parent;
1222 node.info = infoList.at(i);
1223 node.populated = false;
1224 node.stat = shouldStat;
1227 return nodes;
1230 void ResourceModelPrivate::_q_refresh()
1232 Q_Q(ResourceModel);
1233 q->refresh(toBeRefreshed);
1234 toBeRefreshed = QModelIndex();
1237 void ResourceModelPrivate::savePersistentIndexes()
1239 // Q_Q(ResourceModel);
1240 savedPersistent.clear();
1241 // foreach (QPersistentModelIndexData *data, q->persistentIndexes()) {
1242 // SavedPersistent saved;
1243 // QModelIndex index = data->index;
1244 // saved.path = q->filePath(index);
1245 // saved.column = index.column();
1246 // saved.data = data;
1247 // saved.index = index;
1248 // savedPersistent.append(saved);
1249 // }
1252 void ResourceModelPrivate::restorePersistentIndexes()
1254 // Q_Q(ResourceModel);
1255 // bool allow = allowAppendChild;
1256 // allowAppendChild = false;
1257 // for (int i = 0; i < savedPersistent.count(); ++i) {
1258 // QPersistentModelIndexData *data = savedPersistent.at(i).data;
1259 // QString path = savedPersistent.at(i).path;
1260 // int column = savedPersistent.at(i).column;
1261 // QModelIndex idx = q->index(path, column);
1262 // if (idx != data->index || data->model == 0) {
1263 // //data->model may be equal to 0 if the model is getting destroyed
1264 // persistent.indexes.remove(data->index);
1265 // data->index = idx;
1266 // data->model = q;
1267 // if (idx.isValid())
1268 // persistent.indexes.insert(idx, data);
1269 // }
1270 // }
1271 savedPersistent.clear();
1272 // allowAppendChild = allow;
1275 QFileInfoList ResourceModelPrivate::entryInfoList(const QString &path) const
1277 const QDir dir(path);
1278 return dir.entryInfoList(nameFilters, filters, sort);
1281 QStringList ResourceModelPrivate::entryList(const QString &path) const
1283 const QDir dir(path);
1284 return dir.entryList(nameFilters, filters, sort);
1287 QString ResourceModelPrivate::name(const QModelIndex &index) const
1289 const QDirNode *n = node(index);
1290 const QFileInfo info = n->info;
1291 if (info.isRoot()) {
1292 QString name = info.absoluteFilePath();
1293 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
1294 if (name.startsWith(QLatin1Char('/'))) // UNC host
1295 return info.fileName();
1296 #endif
1297 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN)
1298 if (name.endsWith(QLatin1Char('/')))
1299 name.chop(1);
1300 #endif
1301 return name;
1303 return info.fileName();
1306 QString ResourceModelPrivate::size(const QModelIndex &index) const
1308 const QDirNode *n = node(index);
1309 if (n->info.isDir()) {
1310 #ifdef Q_OS_MAC
1311 return QLatin1String("--");
1312 #else
1313 return QLatin1String("");
1314 #endif
1315 // Windows - ""
1316 // OS X - "--"
1317 // Konqueror - "4 KB"
1318 // Nautilus - "9 items" (the number of children)
1321 // According to the Si standard KB is 1000 bytes, KiB is 1024
1322 // but on windows sizes are calulated by dividing by 1024 so we do what they do.
1323 const quint64 kb = 1024;
1324 const quint64 mb = 1024 * kb;
1325 const quint64 gb = 1024 * mb;
1326 const quint64 tb = 1024 * gb;
1327 quint64 bytes = n->info.size();
1328 if (bytes >= tb)
1329 return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3));
1330 if (bytes >= gb)
1331 return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2));
1332 if (bytes >= mb)
1333 return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1));
1334 if (bytes >= kb)
1335 return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb));
1336 return QFileSystemModel::tr("%1 byte(s)").arg(QLocale().toString(bytes));
1339 QString ResourceModelPrivate::type(const QModelIndex &index) const
1341 return iconProvider->type(node(index)->info);
1344 QString ResourceModelPrivate::time(const QModelIndex &index) const
1346 #ifndef QT_NO_DATESTRING
1347 return node(index)->info.lastModified().toString(Qt::LocalDate);
1348 #else
1349 Q_UNUSED(index);
1350 return QString();
1351 #endif
1354 void ResourceModelPrivate::appendChild(ResourceModelPrivate::QDirNode *parent, const QString &path) const
1356 ResourceModelPrivate::QDirNode node;
1357 node.populated = false;
1358 node.stat = shouldStat;
1359 node.parent = (parent == &root ? 0 : parent);
1360 node.info = QFileInfo(path);
1361 node.info.setCaching(true);
1363 // The following append(node) may reallocate the vector, thus
1364 // we need to update the pointers to the childnodes parent.
1365 ResourceModelPrivate *that = const_cast<ResourceModelPrivate *>(this);
1366 that->savePersistentIndexes();
1367 parent->children.append(node);
1368 for (int i = 0; i < parent->children.count(); ++i) {
1369 QDirNode *childNode = &parent->children[i];
1370 for (int j = 0; j < childNode->children.count(); ++j)
1371 childNode->children[j].parent = childNode;
1373 that->restorePersistentIndexes();
1376 QFileInfo ResourceModelPrivate::resolvedInfo(QFileInfo info)
1378 #ifdef Q_OS_WIN
1379 // On windows, we cannot create a shortcut to a shortcut.
1380 return QFileInfo(info.symLinkTarget());
1381 #else
1382 QStringList paths;
1383 do {
1384 QFileInfo link(info.symLinkTarget());
1385 if (link.isRelative())
1386 info.setFile(info.absolutePath(), link.filePath());
1387 else
1388 info = link;
1389 if (paths.contains(info.absoluteFilePath()))
1390 return QFileInfo();
1391 paths.append(info.absoluteFilePath());
1392 } while (info.isSymLink());
1393 return info;
1394 #endif
1397 QT_END_NAMESPACE
1399 #include "resourcemodel.moc"