Doc: Grouped Qt Quick types into several groups
[profile/ivi/qtdeclarative.git] / src / imports / folderlistmodel / qquickfolderlistmodel.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 //![code]
43 #include "qquickfolderlistmodel.h"
44 #include "fileinfothread_p.h"
45 #include "fileproperty_p.h"
46 #include <qqmlcontext.h>
47 #include <qqmlfile.h>
48
49 QT_BEGIN_NAMESPACE
50
51 class QQuickFolderListModelPrivate
52 {
53     Q_DECLARE_PUBLIC(QQuickFolderListModel)
54
55 public:
56     QQuickFolderListModelPrivate(QQuickFolderListModel *q)
57         : q_ptr(q),
58           sortField(QQuickFolderListModel::Name), sortReversed(false), showDirs(true), showDirsFirst(false), showDots(false), showOnlyReadable(false)
59     {
60         nameFilters << QLatin1String("*");
61     }
62
63
64     QQuickFolderListModel *q_ptr;
65     QUrl currentDir;
66     QUrl rootDir;
67     FileInfoThread fileInfoThread;
68     QList<FileProperty> data;
69     QHash<int, QByteArray> roleNames;
70     QQuickFolderListModel::SortField sortField;
71     QStringList nameFilters;
72     bool sortReversed;
73     bool showDirs;
74     bool showDirsFirst;
75     bool showDots;
76     bool showOnlyReadable;
77
78     ~QQuickFolderListModelPrivate() {}
79     void init();
80     void updateSorting();
81
82     // private slots
83     void _q_directoryChanged(const QString &directory, const QList<FileProperty> &list);
84     void _q_directoryUpdated(const QString &directory, const QList<FileProperty> &list, int fromIndex, int toIndex);
85     void _q_sortFinished(const QList<FileProperty> &list);
86 };
87
88
89 void QQuickFolderListModelPrivate::init()
90 {
91     Q_Q(QQuickFolderListModel);
92     qRegisterMetaType<QList<FileProperty> >("QList<FileProperty>");
93     q->connect(&fileInfoThread, SIGNAL(directoryChanged(QString, QList<FileProperty>)),
94                q, SLOT(_q_directoryChanged(QString, QList<FileProperty>)));
95     q->connect(&fileInfoThread, SIGNAL(directoryUpdated(QString, QList<FileProperty>, int, int)),
96                q, SLOT(_q_directoryUpdated(QString, QList<FileProperty>, int, int)));
97     q->connect(&fileInfoThread, SIGNAL(sortFinished(QList<FileProperty>)),
98                q, SLOT(_q_sortFinished(QList<FileProperty>)));
99 }
100
101
102 void QQuickFolderListModelPrivate::updateSorting()
103 {
104     Q_Q(QQuickFolderListModel);
105
106     QDir::SortFlags flags = 0;
107
108     switch (sortField) {
109         case QQuickFolderListModel::Unsorted:
110             flags |= QDir::Unsorted;
111             break;
112         case QQuickFolderListModel::Name:
113             flags |= QDir::Name;
114             break;
115         case QQuickFolderListModel::Time:
116             flags |= QDir::Time;
117             break;
118         case QQuickFolderListModel::Size:
119             flags |= QDir::Size;
120             break;
121         case QQuickFolderListModel::Type:
122             flags |= QDir::Type;
123             break;
124         default:
125             break;
126     }
127
128     emit q->layoutAboutToBeChanged();
129
130     if (sortReversed)
131         flags |= QDir::Reversed;
132
133     fileInfoThread.setSortFlags(flags);
134 }
135
136 void QQuickFolderListModelPrivate::_q_directoryChanged(const QString &directory, const QList<FileProperty> &list)
137 {
138     Q_Q(QQuickFolderListModel);
139     Q_UNUSED(directory);
140
141     data = list;
142     q->endResetModel();
143     emit q->rowCountChanged();
144     emit q->folderChanged();
145 }
146
147
148 void QQuickFolderListModelPrivate::_q_directoryUpdated(const QString &directory, const QList<FileProperty> &list, int fromIndex, int toIndex)
149 {
150     Q_Q(QQuickFolderListModel);
151     Q_UNUSED(directory);
152
153     QModelIndex parent;
154     if (data.size() > list.size()) {
155         //File(s) removed. Since I do not know how many
156         //or where I need to update the whole list from the first item.
157         data = list;
158         q->beginRemoveRows(parent, fromIndex, toIndex);
159         q->endRemoveRows();
160         q->beginInsertRows(parent, fromIndex, list.size()-1);
161         q->endInsertRows();
162         emit q->rowCountChanged();
163     } else if (data.size() < list.size()) {
164         //qDebug() << "File added. FromIndex: " << fromIndex << " toIndex: " << toIndex << " list size: " << list.size();
165         //File(s) added. Calculate how many and insert
166         //from the first changed one.
167         toIndex = fromIndex + (list.size() - data.size()-1);
168         q->beginInsertRows(parent, fromIndex, toIndex);
169         q->endInsertRows();
170         data = list;
171         emit q->rowCountChanged();
172         QModelIndex modelIndexFrom = q->createIndex(fromIndex, 0);
173         QModelIndex modelIndexTo = q->createIndex(toIndex, 0);
174         emit q->dataChanged(modelIndexFrom, modelIndexTo);
175     } else {
176         //qDebug() << "File has been updated";
177         QModelIndex modelIndexFrom = q->createIndex(fromIndex, 0);
178         QModelIndex modelIndexTo = q->createIndex(toIndex, 0);
179         data = list;
180         emit q->dataChanged(modelIndexFrom, modelIndexTo);
181     }
182 }
183
184 void QQuickFolderListModelPrivate::_q_sortFinished(const QList<FileProperty> &list)
185 {
186     Q_Q(QQuickFolderListModel);
187
188     QModelIndex parent;
189     q->beginRemoveRows(parent, 0, data.size()-1);
190     data.clear();
191     q->endRemoveRows();
192
193     q->beginInsertRows(parent, 0, list.size()-1);
194     data = list;
195     q->endInsertRows();
196 }
197
198
199 /*!
200     \qmlclass FolderListModel QQuickFolderListModel
201     \ingroup qtquick-models
202     \brief The FolderListModel provides a model of the contents of a file system folder.
203
204     FolderListModel provides access to information about the contents of a folder
205     in the local file system, exposing a list of files to views and other data components.
206
207     \note This type is made available by importing the \c Qt.labs.folderlistmodel module.
208     \e{Elements in the Qt.labs module are not guaranteed to remain compatible
209     in future versions.}
210
211     \b{import Qt.labs.folderlistmodel 1.0}
212
213     The \l folder property specifies the folder to access. Information about the
214     files and directories in the folder is supplied via the model's interface.
215     Components access names and paths via the following roles:
216
217     \list
218     \li \c fileName
219     \li \c filePath
220     \li \c fileBaseName
221     \li \c fileSuffix
222     \li \c fileSize
223     \li \c fileModified
224     \li \c fileAccessed
225     \li \c fileIsDir
226     \endlist
227
228     Additionally a file entry can be differentiated from a folder entry via the
229     isFolder() method.
230
231     \section1 Filtering
232
233     Various properties can be set to filter the number of files and directories
234     exposed by the model.
235
236     The \l nameFilters property can be set to contain a list of wildcard filters
237     that are applied to names of files and directories, causing only those that
238     match the filters to be exposed.
239
240     Directories can be included or excluded using the \l showDirs property, and
241     navigation directories can also be excluded by setting the \l showDotAndDotDot
242     property to false.
243
244     It is sometimes useful to limit the files and directories exposed to those
245     that the user can access. The \l showOnlyReadable property can be set to
246     enable this feature.
247
248     \section1 Example Usage
249
250     The following example shows a FolderListModel being used to provide a list
251     of QML files in a \l ListView:
252
253     \snippet qml/folderlistmodel.qml 0
254
255     \section1 Path Separators
256
257     Qt uses "/" as a universal directory separator in the same way that "/" is
258     used as a path separator in URLs. If you always use "/" as a directory
259     separator, Qt will translate your paths to conform to the underlying
260     operating system.
261
262     \sa {QML Data Models}
263 */
264
265 QQuickFolderListModel::QQuickFolderListModel(QObject *parent)
266     : QAbstractListModel(parent), d_ptr(new QQuickFolderListModelPrivate(this))
267 {
268     Q_D(QQuickFolderListModel);
269     d->roleNames[FileNameRole] = "fileName";
270     d->roleNames[FilePathRole] = "filePath";
271     d->roleNames[FileBaseNameRole] = "fileBaseName";
272     d->roleNames[FileSuffixRole] = "fileSuffix";
273     d->roleNames[FileSizeRole] = "fileSize";
274     d->roleNames[FileLastModifiedRole] = "fileModified";
275     d->roleNames[FileLastReadRole] = "fileAccessed";
276     d->roleNames[FileIsDirRole] = "fileIsDir";
277     d->init();
278 }
279
280 QQuickFolderListModel::~QQuickFolderListModel()
281 {
282 }
283
284 QVariant QQuickFolderListModel::data(const QModelIndex &index, int role) const
285 {
286     Q_D(const QQuickFolderListModel);
287     QVariant rv;
288
289     if (index.row() >= d->data.size())
290         return rv;
291
292     switch (role)
293     {
294         case FileNameRole:
295             rv = d->data.at(index.row()).fileName();
296             break;
297         case FilePathRole:
298             rv = d->data.at(index.row()).filePath();
299             break;
300         case FileBaseNameRole:
301             rv = d->data.at(index.row()).baseName();
302             break;
303         case FileSuffixRole:
304             rv = d->data.at(index.row()).suffix();
305             break;
306         case FileSizeRole:
307             rv = d->data.at(index.row()).size();
308             break;
309         case FileLastModifiedRole:
310             rv = d->data.at(index.row()).lastModified().date().toString(Qt::ISODate) + " " + d->data.at(index.row()).lastModified().time().toString();
311             break;
312         case FileLastReadRole:
313             rv = d->data.at(index.row()).lastRead().date().toString(Qt::ISODate) + " " + d->data.at(index.row()).lastRead().time().toString();
314             break;
315         case FileIsDirRole:
316             rv = d->data.at(index.row()).isDir();
317             break;
318         default:
319             break;
320     }
321     return rv;
322 }
323
324 QHash<int, QByteArray> QQuickFolderListModel::roleNames() const
325 {
326     Q_D(const QQuickFolderListModel);
327     return d->roleNames;
328 }
329
330 /*!
331     \qmlproperty int FolderListModel::count
332
333     Returns the number of items in the current folder that match the
334     filter criteria.
335 */
336 int QQuickFolderListModel::rowCount(const QModelIndex &parent) const
337 {
338     Q_D(const QQuickFolderListModel);
339     Q_UNUSED(parent);
340     return d->data.size();
341 }
342
343 QModelIndex QQuickFolderListModel::index(int row, int , const QModelIndex &) const
344 {
345     return createIndex(row, 0);
346 }
347
348 /*!
349     \qmlproperty string FolderListModel::folder
350
351     The \a folder property holds a URL for the folder that the model is
352     currently providing.
353
354     The value is a URL expressed as a string, and must be a \c file: or \c qrc:
355     URL, or a relative URL.
356
357     By default, the value is an invalid URL.
358 */
359 QUrl QQuickFolderListModel::folder() const
360 {
361     Q_D(const QQuickFolderListModel);
362     return d->currentDir;
363 }
364
365 void QQuickFolderListModel::setFolder(const QUrl &folder)
366 {
367     Q_D(QQuickFolderListModel);
368
369     if (folder == d->currentDir)
370         return;
371
372     QString localPath = QQmlFile::urlToLocalFileOrQrc(folder);
373     QString resolvedPath = QDir::cleanPath(QUrl(localPath).path());
374
375     beginResetModel();
376
377     //Remove the old path for the file system watcher
378     if (!d->currentDir.isEmpty())
379         d->fileInfoThread.removePath(d->currentDir.path());
380
381     d->currentDir = folder;
382
383     QFileInfo info(resolvedPath);
384     if (!info.exists() || !info.isDir()) {
385         d->data.clear();
386         endResetModel();
387         emit rowCountChanged();
388         return;
389     }
390
391     d->fileInfoThread.setPath(resolvedPath);
392 }
393
394
395 /*!
396    \qmlproperty string QQuickFolderListModel::rootFolder
397
398    When the rootFolder is set, then this folder will
399    be threated as the root in the file system, so that
400    you can only travers sub folders from this rootFolder.
401 */
402 QUrl QQuickFolderListModel::rootFolder() const
403 {
404     Q_D(const QQuickFolderListModel);
405     return d->rootDir;
406 }
407
408 void QQuickFolderListModel::setRootFolder(const QUrl &path)
409 {
410     Q_D(QQuickFolderListModel);
411
412     if (path.isEmpty())
413         return;
414
415     QString localPath = QQmlFile::urlToLocalFileOrQrc(path);
416     QString resolvedPath = QDir::cleanPath(QUrl(localPath).path());
417
418     QFileInfo info(resolvedPath);
419     if (!info.exists() || !info.isDir())
420         return;
421
422     d->fileInfoThread.setRootPath(resolvedPath);
423     d->rootDir = path;
424 }
425
426
427 /*!
428     \qmlproperty url FolderListModel::parentFolder
429
430     Returns the URL of the parent of of the current \l folder.
431 */
432 QUrl QQuickFolderListModel::parentFolder() const
433 {
434     Q_D(const QQuickFolderListModel);
435
436     QString localFile = d->currentDir.toLocalFile();
437     if (!localFile.isEmpty()) {
438         QDir dir(localFile);
439 #if defined(Q_OS_WIN)
440         if (dir.isRoot())
441             dir.setPath("");
442         else
443 #endif
444             dir.cdUp();
445         localFile = dir.path();
446     } else {
447         int pos = d->currentDir.path().lastIndexOf(QLatin1Char('/'));
448         if (pos == -1)
449             return QUrl();
450         localFile = d->currentDir.path().left(pos);
451     }
452     return QUrl::fromLocalFile(localFile);
453 }
454
455 /*!
456     \qmlproperty list<string> FolderListModel::nameFilters
457
458     The \a nameFilters property contains a list of file name filters.
459     The filters may include the ? and * wildcards.
460
461     The example below filters on PNG and JPEG files:
462
463     \qml
464     FolderListModel {
465         nameFilters: [ "*.png", "*.jpg" ]
466     }
467     \endqml
468
469     \note Directories are not excluded by filters.
470 */
471 QStringList QQuickFolderListModel::nameFilters() const
472 {
473     Q_D(const QQuickFolderListModel);
474     return d->nameFilters;
475 }
476
477 void QQuickFolderListModel::setNameFilters(const QStringList &filters)
478 {
479     Q_D(QQuickFolderListModel);
480     d->fileInfoThread.setNameFilters(filters);
481     d->nameFilters = filters;
482 }
483
484 void QQuickFolderListModel::classBegin()
485 {
486 }
487
488 void QQuickFolderListModel::componentComplete()
489 {
490     Q_D(QQuickFolderListModel);
491
492     if (!d->currentDir.isValid() || d->currentDir.toLocalFile().isEmpty() || !QDir().exists(d->currentDir.toLocalFile()))
493         setFolder(QUrl(QLatin1String("file://")+QDir::currentPath()));
494 }
495
496 /*!
497     \qmlproperty enumeration FolderListModel::sortField
498
499     The \a sortField property contains field to use for sorting.  sortField
500     may be one of:
501     \list
502     \li Unsorted - no sorting is applied.
503     \li Name - sort by filename
504     \li LastModified - sort by time modified
505     \li Size - sort by file size
506     \li Type - sort by file type (extension)
507     \endlist
508
509     \sa sortReversed
510 */
511 QQuickFolderListModel::SortField QQuickFolderListModel::sortField() const
512 {
513     Q_D(const QQuickFolderListModel);
514     return d->sortField;
515 }
516
517 void QQuickFolderListModel::setSortField(SortField field)
518 {
519     Q_D(QQuickFolderListModel);
520     if (field != d->sortField) {
521         d->sortField = field;
522         d->updateSorting();
523     }
524 }
525
526 int QQuickFolderListModel::roleFromString(const QString &roleName) const
527 {
528     Q_D(const QQuickFolderListModel);
529     return d->roleNames.key(roleName.toLatin1(), -1);
530 }
531
532 /*!
533     \qmlproperty bool FolderListModel::sortReversed
534
535     If set to true, reverses the sort order.  The default is false.
536
537     \sa sortField
538 */
539 bool QQuickFolderListModel::sortReversed() const
540 {
541     Q_D(const QQuickFolderListModel);
542     return d->sortReversed;
543 }
544
545 void QQuickFolderListModel::setSortReversed(bool rev)
546 {
547     Q_D(QQuickFolderListModel);
548
549     if (rev != d->sortReversed) {
550         d->sortReversed = rev;
551         d->updateSorting();
552     }
553 }
554
555 /*!
556     \qmlmethod bool FolderListModel::isFolder(int index)
557
558     Returns true if the entry \a index is a folder; otherwise
559     returns false.
560 */
561 bool QQuickFolderListModel::isFolder(int index) const
562 {
563     if (index != -1) {
564         QModelIndex idx = createIndex(index, 0);
565         if (idx.isValid()) {
566             QVariant var = data(idx, FileIsDirRole);
567             if (var.isValid())
568                 return var.toBool();
569         }
570     }
571     return false;
572 }
573
574 /*!
575     \qmlproperty bool FolderListModel::showDirs
576
577     If true, directories are included in the model; otherwise only files
578     are included.
579
580     By default, this property is true.
581
582     Note that the nameFilters are not applied to directories.
583
584     \sa showDotAndDotDot
585 */
586 bool QQuickFolderListModel::showDirs() const
587 {
588     Q_D(const QQuickFolderListModel);
589     return d->showDirs;
590 }
591
592 void  QQuickFolderListModel::setShowDirs(bool on)
593 {
594     Q_D(QQuickFolderListModel);
595
596     d->fileInfoThread.setShowDirs(on);
597     d->showDirs = on;
598 }
599
600 /*!
601     \qmlproperty bool FolderListModel::showDirsFirst
602
603     If true, if directories are included in the model they will
604     always be shown first, then the files.
605
606     By default, this property is false.
607
608 */
609 bool QQuickFolderListModel::showDirsFirst() const
610 {
611     Q_D(const QQuickFolderListModel);
612     return d->showDirsFirst;
613 }
614
615 void  QQuickFolderListModel::setShowDirsFirst(bool on)
616 {
617     Q_D(QQuickFolderListModel);
618
619     d->fileInfoThread.setShowDirsFirst(on);
620     d->showDirsFirst = on;
621 }
622
623
624 /*!
625     \qmlproperty bool FolderListModel::showDotAndDotDot
626
627     If true, the "." and ".." directories are included in the model; otherwise
628     they are excluded.
629
630     By default, this property is false.
631
632     \sa showDirs
633 */
634 bool QQuickFolderListModel::showDotAndDotDot() const
635 {
636     Q_D(const QQuickFolderListModel);
637     return d->showDots;
638 }
639
640 void  QQuickFolderListModel::setShowDotAndDotDot(bool on)
641 {
642     Q_D(QQuickFolderListModel);
643
644     if (on != d->showDots) {
645         d->fileInfoThread.setShowDotDot(on);
646     }
647 }
648
649 /*!
650     \qmlproperty bool FolderListModel::showOnlyReadable
651
652     If true, only readable files and directories are shown; otherwise all files
653     and directories are shown.
654
655     By default, this property is false.
656
657     \sa showDirs
658 */
659 bool QQuickFolderListModel::showOnlyReadable() const
660 {
661     Q_D(const QQuickFolderListModel);
662     return d->showOnlyReadable;
663 }
664
665 void QQuickFolderListModel::setShowOnlyReadable(bool on)
666 {
667     Q_D(QQuickFolderListModel);
668
669     if (on != d->showOnlyReadable) {
670         d->fileInfoThread.setShowOnlyReadable(on);
671     }
672 }
673
674 /*!
675     \qmlmethod QVariant QQuickFolderListModel::get(int idx, const QString &property) const
676
677     Get the folder property for the given index. The following properties
678     are available.
679
680     \list
681         \li \c fileName
682         \li \c filePath
683         \li \c fileBaseName
684         \li \c fileSuffix
685         \li \c fileSize
686         \li \c fileModified
687         \li \c fileAccessed
688         \li \c fileIsDir
689     \endlist
690 */
691 QVariant QQuickFolderListModel::get(int idx, const QString &property) const
692 {
693     int role = roleFromString(property);
694     if (role >= 0 && idx >= 0)
695         return data(index(idx, 0), role);
696     else
697         return QVariant();
698 }
699
700 #include "moc_qquickfolderlistmodel.cpp"
701
702 //![code]
703 QT_END_NAMESPACE