0aabb7ae74800ab7b00aff5347eb4aa44d1b35d6
[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 <private/qqmlengine_p.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 qml-working-with-data
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 doc/src/snippets/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     setRoleNames(d->roleNames);
278
279     d->init();
280 }
281
282 QQuickFolderListModel::~QQuickFolderListModel()
283 {
284 }
285
286 QVariant QQuickFolderListModel::data(const QModelIndex &index, int role) const
287 {
288     Q_D(const QQuickFolderListModel);
289     QVariant rv;
290
291     if (index.row() >= d->data.size())
292         return rv;
293
294     switch (role)
295     {
296         case FileNameRole:
297             rv = d->data.at(index.row()).fileName();
298             break;
299         case FilePathRole:
300             rv = d->data.at(index.row()).filePath();
301             break;
302         case FileBaseNameRole:
303             rv = d->data.at(index.row()).baseName();
304             break;
305         case FileSuffixRole:
306             rv = d->data.at(index.row()).suffix();
307             break;
308         case FileSizeRole:
309             rv = d->data.at(index.row()).size();
310             break;
311         case FileLastModifiedRole:
312             rv = d->data.at(index.row()).lastModified().date().toString(Qt::ISODate) + " " + d->data.at(index.row()).lastModified().time().toString();
313             break;
314         case FileLastReadRole:
315             rv = d->data.at(index.row()).lastRead().date().toString(Qt::ISODate) + " " + d->data.at(index.row()).lastRead().time().toString();
316             break;
317         case FileIsDirRole:
318             rv = d->data.at(index.row()).isDir();
319             break;
320         default:
321             break;
322     }
323     return rv;
324 }
325
326 /*!
327     \qmlproperty int FolderListModel::count
328
329     Returns the number of items in the current folder that match the
330     filter criteria.
331 */
332 int QQuickFolderListModel::rowCount(const QModelIndex &parent) const
333 {
334     Q_D(const QQuickFolderListModel);
335     Q_UNUSED(parent);
336     return d->data.size();
337 }
338
339 QModelIndex QQuickFolderListModel::index(int row, int , const QModelIndex &) const
340 {
341     return createIndex(row, 0);
342 }
343
344 /*!
345     \qmlproperty string FolderListModel::folder
346
347     The \a folder property holds a URL for the folder that the model is
348     currently providing.
349
350     The value is a URL expressed as a string, and must be a \c file: or \c qrc:
351     URL, or a relative URL.
352
353     By default, the value is an invalid URL.
354 */
355 QUrl QQuickFolderListModel::folder() const
356 {
357     Q_D(const QQuickFolderListModel);
358     return d->currentDir;
359 }
360
361 void QQuickFolderListModel::setFolder(const QUrl &folder)
362 {
363     Q_D(QQuickFolderListModel);
364
365     if (folder == d->currentDir)
366         return;
367
368     QString localPath = QQmlEnginePrivate::urlToLocalFileOrQrc(folder);
369     QString resolvedPath = QDir::cleanPath(QUrl(localPath).path());
370
371     beginResetModel();
372
373     //Remove the old path for the file system watcher
374     if (!d->currentDir.isEmpty())
375         d->fileInfoThread.removePath(d->currentDir.path());
376
377     d->currentDir = folder;
378
379     QFileInfo info(resolvedPath);
380     if (!info.exists() || !info.isDir()) {
381         d->data.clear();
382         endResetModel();
383         emit rowCountChanged();
384         return;
385     }
386
387     d->fileInfoThread.setPath(resolvedPath);
388 }
389
390
391 /*!
392    \qmlproperty string QQuickFolderListModel::rootFolder
393
394    When the rootFolder is set, then this folder will
395    be threated as the root in the file system, so that
396    you can only travers sub folders from this rootFolder.
397 */
398 QUrl QQuickFolderListModel::rootFolder() const
399 {
400     Q_D(const QQuickFolderListModel);
401     return d->rootDir;
402 }
403
404 void QQuickFolderListModel::setRootFolder(const QUrl &path)
405 {
406     Q_D(QQuickFolderListModel);
407
408     if (path.isEmpty())
409         return;
410
411     QString localPath = QQmlEnginePrivate::urlToLocalFileOrQrc(path);
412     QString resolvedPath = QDir::cleanPath(QUrl(localPath).path());
413
414     QFileInfo info(resolvedPath);
415     if (!info.exists() || !info.isDir())
416         return;
417
418     d->fileInfoThread.setRootPath(resolvedPath);
419     d->rootDir = path;
420 }
421
422
423 /*!
424     \qmlproperty url FolderListModel::parentFolder
425
426     Returns the URL of the parent of of the current \l folder.
427 */
428 QUrl QQuickFolderListModel::parentFolder() const
429 {
430     Q_D(const QQuickFolderListModel);
431
432     QString localFile = d->currentDir.toLocalFile();
433     if (!localFile.isEmpty()) {
434         QDir dir(localFile);
435 #if defined(Q_OS_WIN)
436         if (dir.isRoot())
437             dir.setPath("");
438         else
439 #endif
440             dir.cdUp();
441         localFile = dir.path();
442     } else {
443         int pos = d->currentDir.path().lastIndexOf(QLatin1Char('/'));
444         if (pos == -1)
445             return QUrl();
446         localFile = d->currentDir.path().left(pos);
447     }
448     return QUrl::fromLocalFile(localFile);
449 }
450
451 /*!
452     \qmlproperty list<string> FolderListModel::nameFilters
453
454     The \a nameFilters property contains a list of file name filters.
455     The filters may include the ? and * wildcards.
456
457     The example below filters on PNG and JPEG files:
458
459     \qml
460     FolderListModel {
461         nameFilters: [ "*.png", "*.jpg" ]
462     }
463     \endqml
464
465     \note Directories are not excluded by filters.
466 */
467 QStringList QQuickFolderListModel::nameFilters() const
468 {
469     Q_D(const QQuickFolderListModel);
470     return d->nameFilters;
471 }
472
473 void QQuickFolderListModel::setNameFilters(const QStringList &filters)
474 {
475     Q_D(QQuickFolderListModel);
476     d->fileInfoThread.setNameFilters(filters);
477     d->nameFilters = filters;
478 }
479
480 void QQuickFolderListModel::classBegin()
481 {
482 }
483
484 void QQuickFolderListModel::componentComplete()
485 {
486     Q_D(QQuickFolderListModel);
487
488     if (!d->currentDir.isValid() || d->currentDir.toLocalFile().isEmpty() || !QDir().exists(d->currentDir.toLocalFile()))
489         setFolder(QUrl(QLatin1String("file://")+QDir::currentPath()));
490 }
491
492 /*!
493     \qmlproperty enumeration FolderListModel::sortField
494
495     The \a sortField property contains field to use for sorting.  sortField
496     may be one of:
497     \list
498     \li Unsorted - no sorting is applied.
499     \li Name - sort by filename
500     \li LastModified - sort by time modified
501     \li Size - sort by file size
502     \li Type - sort by file type (extension)
503     \endlist
504
505     \sa sortReversed
506 */
507 QQuickFolderListModel::SortField QQuickFolderListModel::sortField() const
508 {
509     Q_D(const QQuickFolderListModel);
510     return d->sortField;
511 }
512
513 void QQuickFolderListModel::setSortField(SortField field)
514 {
515     Q_D(QQuickFolderListModel);
516     if (field != d->sortField) {
517         d->sortField = field;
518         d->updateSorting();
519     }
520 }
521
522 int QQuickFolderListModel::roleFromString(const QString &roleName) const
523 {
524     Q_D(const QQuickFolderListModel);
525     return d->roleNames.key(roleName.toLatin1(), -1);
526 }
527
528 /*!
529     \qmlproperty bool FolderListModel::sortReversed
530
531     If set to true, reverses the sort order.  The default is false.
532
533     \sa sortField
534 */
535 bool QQuickFolderListModel::sortReversed() const
536 {
537     Q_D(const QQuickFolderListModel);
538     return d->sortReversed;
539 }
540
541 void QQuickFolderListModel::setSortReversed(bool rev)
542 {
543     Q_D(QQuickFolderListModel);
544
545     if (rev != d->sortReversed) {
546         d->sortReversed = rev;
547         d->updateSorting();
548     }
549 }
550
551 /*!
552     \qmlmethod bool FolderListModel::isFolder(int index)
553
554     Returns true if the entry \a index is a folder; otherwise
555     returns false.
556 */
557 bool QQuickFolderListModel::isFolder(int index) const
558 {
559     if (index != -1) {
560         QModelIndex idx = createIndex(index, 0);
561         if (idx.isValid()) {
562             QVariant var = data(idx, FileIsDirRole);
563             if (var.isValid())
564                 return var.toBool();
565         }
566     }
567     return false;
568 }
569
570 /*!
571     \qmlproperty bool FolderListModel::showDirs
572
573     If true, directories are included in the model; otherwise only files
574     are included.
575
576     By default, this property is true.
577
578     Note that the nameFilters are not applied to directories.
579
580     \sa showDotAndDotDot
581 */
582 bool QQuickFolderListModel::showDirs() const
583 {
584     Q_D(const QQuickFolderListModel);
585     return d->showDirs;
586 }
587
588 void  QQuickFolderListModel::setShowDirs(bool on)
589 {
590     Q_D(QQuickFolderListModel);
591
592     d->fileInfoThread.setShowDirs(on);
593     d->showDirs = on;
594 }
595
596 /*!
597     \qmlproperty bool FolderListModel::showDirsFirst
598
599     If true, if directories are included in the model they will
600     always be shown first, then the files.
601
602     By default, this property is false.
603
604 */
605 bool QQuickFolderListModel::showDirsFirst() const
606 {
607     Q_D(const QQuickFolderListModel);
608     return d->showDirsFirst;
609 }
610
611 void  QQuickFolderListModel::setShowDirsFirst(bool on)
612 {
613     Q_D(QQuickFolderListModel);
614
615     d->fileInfoThread.setShowDirsFirst(on);
616     d->showDirsFirst = on;
617 }
618
619
620 /*!
621     \qmlproperty bool FolderListModel::showDotAndDotDot
622
623     If true, the "." and ".." directories are included in the model; otherwise
624     they are excluded.
625
626     By default, this property is false.
627
628     \sa showDirs
629 */
630 bool QQuickFolderListModel::showDotAndDotDot() const
631 {
632     Q_D(const QQuickFolderListModel);
633     return d->showDots;
634 }
635
636 void  QQuickFolderListModel::setShowDotAndDotDot(bool on)
637 {
638     Q_D(QQuickFolderListModel);
639
640     if (on != d->showDots) {
641         d->fileInfoThread.setShowDotDot(on);
642     }
643 }
644
645 /*!
646     \qmlproperty bool FolderListModel::showOnlyReadable
647
648     If true, only readable files and directories are shown; otherwise all files
649     and directories are shown.
650
651     By default, this property is false.
652
653     \sa showDirs
654 */
655 bool QQuickFolderListModel::showOnlyReadable() const
656 {
657     Q_D(const QQuickFolderListModel);
658     return d->showOnlyReadable;
659 }
660
661 void QQuickFolderListModel::setShowOnlyReadable(bool on)
662 {
663     Q_D(QQuickFolderListModel);
664
665     if (on != d->showOnlyReadable) {
666         d->fileInfoThread.setShowOnlyReadable(on);
667     }
668 }
669
670 /*!
671     \qmlmethod QVariant QQuickFolderListModel::get(int idx, const QString &property) const
672
673     Get the folder property for the given index. The following properties
674     are available.
675
676     \list
677         \li \c fileName
678         \li \c filePath
679         \li \c fileBaseName
680         \li \c fileSuffix
681         \li \c fileSize
682         \li \c fileModified
683         \li \c fileAccessed
684         \li \c fileIsDir
685     \endlist
686 */
687 QVariant QQuickFolderListModel::get(int idx, const QString &property) const
688 {
689     int role = roleFromString(property);
690     if (role >= 0 && idx >= 0)
691         return data(index(idx, 0), role);
692     else
693         return QVariant();
694 }
695
696 #include "moc_qquickfolderlistmodel.cpp"
697
698 //![code]
699 QT_END_NAMESPACE