1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
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
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.
40 ****************************************************************************/
42 #include "resourcemodel.h"
46 #include <qfilesystemmodel.h>
48 #include <qmimedata.h>
52 #include <qdatetime.h>
55 #include <qapplication.h>
59 \enum ResourceModel::Roles
67 class ResourceModelPrivate
69 Q_DECLARE_PUBLIC(ResourceModel
)
70 ResourceModel
* const q_ptr
;
75 QDirNode() : parent(0), populated(false), stat(false) {}
76 ~QDirNode() { children
.clear(); }
79 QIcon icon
; // cache the icon
80 mutable QVector
<QDirNode
> children
;
81 mutable bool populated
; // have we read the children
85 ResourceModelPrivate(ResourceModel
*qq
)
86 : resolveSymlinks(true),
88 lazyChildCount(false),
89 allowAppendChild(true),
90 iconProvider(&defaultProvider
),
91 shouldStat(true), // ### This is set to false by QFileDialog
95 bool indexValid(const QModelIndex
&index
) const { return index
.isValid(); }
98 QDirNode
*node(int row
, QDirNode
*parent
) const;
99 QVector
<QDirNode
> children(QDirNode
*parent
, bool stat
) const;
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;
123 mutable QDirNode root
;
124 bool resolveSymlinks
;
127 bool allowAppendChild
;
129 QDir::Filters filters
;
130 QDir::SortFlags sort
;
131 QStringList nameFilters
;
133 QFileIconProvider
*iconProvider
;
134 QFileIconProvider defaultProvider
;
136 struct SavedPersistent
{
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());
161 void ResourceModelPrivate::populate(QDirNode
*parent
) const
164 parent
->children
= children(parent
, parent
->stat
);
165 parent
->populated
= true;
168 void ResourceModelPrivate::clear(QDirNode
*parent
) const
171 parent
->children
.clear();
172 parent
->populated
= false;
175 void ResourceModelPrivate::invalidate()
177 QStack
<const QDirNode
*> nodes
;
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
));
191 \brief The ResourceModel class provides a data model for the local filesystem.
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
228 ResourceModel::ResourceModel(const QStringList
&nameFilters
,
229 QDir::Filters filters
,
230 QDir::SortFlags sort
,
232 : QAbstractItemModel(parent
), d_ptr(new ResourceModelPrivate(this))
235 // we always start with QDir::drives()
236 d
->nameFilters
= nameFilters
.isEmpty() ? QStringList(QLatin1String("*")) : nameFilters
;
237 d
->filters
= filters
;
240 d
->root
.info
= QFileInfo();
245 Constructs a directory model with the given \a parent.
248 ResourceModel::ResourceModel(QObject
*parent
)
249 : QAbstractItemModel(parent
), d_ptr(new ResourceModelPrivate(this))
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
);
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);
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)));
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)
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)
334 ResourceModelPrivate::QDirNode
*p
= d
->node(parent
);
335 if (p
->info
.isDir() && !p
->populated
) // lazy population
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)
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
))
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
);
368 qWarning("data: invalid display value column %d", index
.column());
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
;
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.
396 bool ResourceModel::setData(const QModelIndex
&index
, const QVariant
&value
, int role
)
399 if (!d
->indexValid(index
) || index
.column() != 0
400 || (flags(index
) & Qt::ItemIsEditable
) == 0 || role
!= Qt::EditRole
)
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
);
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
)
431 case 0: return tr("Name");
432 case 1: return tr("Size");
435 tr("Kind", "Match OS X Finder");
437 tr("Type", "All other platforms");
441 // Konqueror - File 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
455 bool ResourceModel::hasChildren(const QModelIndex
&parent
) const
457 Q_D(const ResourceModel
);
458 if (parent
.column() > 0)
461 if (!parent
.isValid()) // the invalid index is the "My Computer" item
462 return true; // the drives
463 ResourceModelPrivate::QDirNode
*p
= d
->node(parent
);
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.
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
))
482 flags
|= Qt::ItemIsDragEnabled
;
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
;
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
;
526 Returns a list of MIME types that can be used to describe a list of items
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
544 QMimeData
*ResourceModel::mimeData(const QModelIndexList
&indexes
) const
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();
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
)
568 if (!d
->indexValid(parent
) || isReadOnly())
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();
580 for (; it
!= urls
.constEnd(); ++it
) {
581 QString path
= (*it
).toLocalFile();
582 success
= QFile::copy(path
, to
+ QFileInfo(path
).fileName()) && success
;
586 for (; it
!= urls
.constEnd(); ++it
) {
587 QString path
= (*it
).toLocalFile();
588 success
= QFile::link(path
, to
+ QFileInfo(path
).fileName()) && success
;
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());
599 //the previous call to refresh may invalidate the _parent. so recreate a new QModelIndex
618 Returns the drop actions supported by this model.
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
)
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
)
656 d
->nameFilters
= filters
;
657 emit
layoutAboutToBeChanged();
659 refresh(QModelIndex());
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.
684 void ResourceModel::setFilter(QDir::Filters filters
)
687 d
->filters
= filters
;
688 emit
layoutAboutToBeChanged();
690 refresh(QModelIndex());
693 emit
layoutChanged();
697 Returns the filter specification for the directory model.
702 QDir::Filters
ResourceModel::filter() const
704 Q_D(const ResourceModel
);
709 Sets the directory model's sorting order to that specified by \a sort.
714 void ResourceModel::setSorting(QDir::SortFlags sort
)
718 emit
layoutAboutToBeChanged();
720 refresh(QModelIndex());
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
);
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
744 void ResourceModel::setResolveSymlinks(bool enable
)
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
)
769 d
->readOnly
= enable
;
772 bool ResourceModel::isReadOnly() const
774 Q_D(const ResourceModel
);
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
788 This property is false by default
791 void ResourceModel::setLazyChildCount(bool enable
)
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
)
814 ResourceModelPrivate::QDirNode
*n
= d
->indexValid(parent
) ? d
->node(parent
) : &(d
->root
);
816 int rows
= n
->children
.count();
818 emit
layoutAboutToBeChanged();
819 n
->stat
= true; // make sure that next time we read all the info
820 n
->populated
= false;
821 emit
layoutChanged();
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
831 // d->rowsRemoved(parent, 0, rows - 1);
832 d
->restorePersistentIndexes();
833 // emit layoutChanged();
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();
854 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
855 // On Windows, "filename......." and "filename" are equivalent
856 if (absolutePath
.endsWith(QLatin1Char('.'))) {
858 for (i
= absolutePath
.count() - 1; i
>= 0; --i
) {
859 if (absolutePath
.at(i
) != QLatin1Char('.'))
862 absolutePath
= absolutePath
.left(i
+1);
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("/")
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();
882 for (; r
< d
->root
.children
.count(); ++r
)
883 if (d
->root
.children
.at(r
).info
.fileName() == host
)
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();
893 emit
const_cast<ResourceModel
*>(this)->layoutChanged();
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('/');
901 // add the "/" item, since it is a valid path element on unix
902 pathElements
.prepend(QLatin1String("/"));
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
);
911 if (!parent
->populated
)
914 // search for the element in the child nodes first
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();
923 if (childFileName
== element
) {
924 if (i
== pathElements
.count() - 1)
925 parent
->children
[j
].stat
= true;
931 // we couldn't find the path element, we create a new node since we _know_ that the path is valid
933 #if defined(Q_OS_WINCE)
935 if (parent
->info
.isRoot())
936 newPath
= parent
->info
.absoluteFilePath() + element
;
938 newPath
= parent
->info
.absoluteFilePath() + QLatin1Char('/') + element
;
940 QString newPath
= parent
->info
.absoluteFilePath() + QLatin1Char('/') + element
;
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();
952 idx
= createIndex(row
, 0, static_cast<void*>(&parent
->children
[row
]));
953 Q_ASSERT(idx
.isValid());
957 return idx
.sibling(idx
.row(), column
);
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
)
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.
991 if (newDir
.isRelative())
992 newDir
= QDir(path
+ QLatin1Char('/') + name
);
993 QString childName
= newDir
.dirName(); // Get the singular name of the directory
996 if (newDir
.absolutePath() != dir
.absolutePath() || !dir
.mkdir(name
))
997 return QModelIndex(); // nothing happened
1001 QStringList entryList
= d
->entryList(path
);
1002 int r
= entryList
.indexOf(childName
);
1003 QModelIndex i
= index(r
, 0, parent
); // return an invalid index
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.
1020 bool ResourceModel::rmdir(const QModelIndex
&index
)
1023 if (!d
->indexValid(index
) || isReadOnly())
1026 ResourceModelPrivate::QDirNode
*n
= d_func()->node(index
);
1027 if (!n
->info
.isDir()) {
1028 qWarning("rmdir: the node is not a directory");
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
))
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.
1055 bool ResourceModel::remove(const QModelIndex
&index
)
1058 if (!d
->indexValid(index
) || isReadOnly())
1061 ResourceModelPrivate::QDirNode
*n
= d_func()->node(index
);
1062 if (n
->info
.isDir())
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
))
1078 Returns the path of the item stored in the model under the
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
1101 QString
ResourceModel::fileName(const QModelIndex
&index
) const
1103 Q_D(const ResourceModel
);
1104 if (!d
->indexValid(index
))
1106 QFileInfo info
= fileInfo(index
);
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
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
);
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
);
1151 \fn QObject *ResourceModel::parent() const
1156 The root node is never seen outside the model.
1159 void ResourceModelPrivate::init()
1162 filters
= QDir::AllEntries
| QDir::NoDotAndDotDot
;
1164 nameFilters
<< QLatin1String("*");
1166 root
.info
= QFileInfo(":");
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
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");
1190 return const_cast<QDirNode
*>(&p
->children
.at(row
));
1193 QVector
<ResourceModelPrivate::QDirNode
> ResourceModelPrivate::children(QDirNode
*parent
, bool stat
) const
1196 QFileInfoList infoList
;
1197 if (parent
== &root
) {
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())
1207 infoList
= entryInfoList(link
);
1209 infoList
= QDir(link
).entryInfoList(nameFilters
, QDir::AllEntries
| QDir::System
);
1212 infoList
= entryInfoList(parent
->info
.absoluteFilePath());
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
;
1230 void ResourceModelPrivate::_q_refresh()
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);
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;
1267 // if (idx.isValid())
1268 // persistent.indexes.insert(idx, data);
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();
1297 #if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN)
1298 if (name
.endsWith(QLatin1Char('/')))
1303 return info
.fileName();
1306 QString
ResourceModelPrivate::size(const QModelIndex
&index
) const
1308 const QDirNode
*n
= node(index
);
1309 if (n
->info
.isDir()) {
1311 return QLatin1String("--");
1313 return QLatin1String("");
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();
1329 return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes
) / tb
, 'f', 3));
1331 return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes
) / gb
, 'f', 2));
1333 return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes
) / mb
, 'f', 1));
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
);
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
)
1379 // On windows, we cannot create a shortcut to a shortcut.
1380 return QFileInfo(info
.symLinkTarget());
1384 QFileInfo
link(info
.symLinkTarget());
1385 if (link
.isRelative())
1386 info
.setFile(info
.absolutePath(), link
.filePath());
1389 if (paths
.contains(info
.absoluteFilePath()))
1391 paths
.append(info
.absoluteFilePath());
1392 } while (info
.isSymLink());
1399 #include "resourcemodel.moc"