409c2563f128461fa1c321cb7f811a2623db0d03
[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     \qmltype FolderListModel
201     \instantiates QQuickFolderListModel
202     \ingroup qtquick-models
203     \brief The FolderListModel provides a model of the contents of a file system folder.
204
205     FolderListModel provides access to information about the contents of a folder
206     in the local file system, exposing a list of files to views and other data components.
207
208     \note This type is made available by importing the \c Qt.labs.folderlistmodel module.
209     \e{Elements in the Qt.labs module are not guaranteed to remain compatible
210     in future versions.}
211
212     \b{import Qt.labs.folderlistmodel 1.0}
213
214     The \l folder property specifies the folder to access. Information about the
215     files and directories in the folder is supplied via the model's interface.
216     Components access names and paths via the following roles:
217
218     \list
219     \li \c fileName
220     \li \c filePath
221     \li \c fileBaseName
222     \li \c fileSuffix
223     \li \c fileSize
224     \li \c fileModified
225     \li \c fileAccessed
226     \li \c fileIsDir
227     \endlist
228
229     Additionally a file entry can be differentiated from a folder entry via the
230     isFolder() method.
231
232     \section1 Filtering
233
234     Various properties can be set to filter the number of files and directories
235     exposed by the model.
236
237     The \l nameFilters property can be set to contain a list of wildcard filters
238     that are applied to names of files and directories, causing only those that
239     match the filters to be exposed.
240
241     Directories can be included or excluded using the \l showDirs property, and
242     navigation directories can also be excluded by setting the \l showDotAndDotDot
243     property to false.
244
245     It is sometimes useful to limit the files and directories exposed to those
246     that the user can access. The \l showOnlyReadable property can be set to
247     enable this feature.
248
249     \section1 Example Usage
250
251     The following example shows a FolderListModel being used to provide a list
252     of QML files in a \l ListView:
253
254     \snippet qml/folderlistmodel.qml 0
255
256     \section1 Path Separators
257
258     Qt uses "/" as a universal directory separator in the same way that "/" is
259     used as a path separator in URLs. If you always use "/" as a directory
260     separator, Qt will translate your paths to conform to the underlying
261     operating system.
262
263     \sa {QML Data Models}
264 */
265
266 QQuickFolderListModel::QQuickFolderListModel(QObject *parent)
267     : QAbstractListModel(parent), d_ptr(new QQuickFolderListModelPrivate(this))
268 {
269     Q_D(QQuickFolderListModel);
270     d->roleNames[FileNameRole] = "fileName";
271     d->roleNames[FilePathRole] = "filePath";
272     d->roleNames[FileBaseNameRole] = "fileBaseName";
273     d->roleNames[FileSuffixRole] = "fileSuffix";
274     d->roleNames[FileSizeRole] = "fileSize";
275     d->roleNames[FileLastModifiedRole] = "fileModified";
276     d->roleNames[FileLastReadRole] = "fileAccessed";
277     d->roleNames[FileIsDirRole] = "fileIsDir";
278     d->init();
279 }
280
281 QQuickFolderListModel::~QQuickFolderListModel()
282 {
283 }
284
285 QVariant QQuickFolderListModel::data(const QModelIndex &index, int role) const
286 {
287     Q_D(const QQuickFolderListModel);
288     QVariant rv;
289
290     if (index.row() >= d->data.size())
291         return rv;
292
293     switch (role)
294     {
295         case FileNameRole:
296             rv = d->data.at(index.row()).fileName();
297             break;
298         case FilePathRole:
299             rv = d->data.at(index.row()).filePath();
300             break;
301         case FileBaseNameRole:
302             rv = d->data.at(index.row()).baseName();
303             break;
304         case FileSuffixRole:
305             rv = d->data.at(index.row()).suffix();
306             break;
307         case FileSizeRole:
308             rv = d->data.at(index.row()).size();
309             break;
310         case FileLastModifiedRole:
311             rv = d->data.at(index.row()).lastModified().date().toString(Qt::ISODate) + " " + d->data.at(index.row()).lastModified().time().toString();
312             break;
313         case FileLastReadRole:
314             rv = d->data.at(index.row()).lastRead().date().toString(Qt::ISODate) + " " + d->data.at(index.row()).lastRead().time().toString();
315             break;
316         case FileIsDirRole:
317             rv = d->data.at(index.row()).isDir();
318             break;
319         default:
320             break;
321     }
322     return rv;
323 }
324
325 QHash<int, QByteArray> QQuickFolderListModel::roleNames() const
326 {
327     Q_D(const QQuickFolderListModel);
328     return d->roleNames;
329 }
330
331 /*!
332     \qmlproperty int FolderListModel::count
333
334     Returns the number of items in the current folder that match the
335     filter criteria.
336 */
337 int QQuickFolderListModel::rowCount(const QModelIndex &parent) const
338 {
339     Q_D(const QQuickFolderListModel);
340     Q_UNUSED(parent);
341     return d->data.size();
342 }
343
344 QModelIndex QQuickFolderListModel::index(int row, int , const QModelIndex &) const
345 {
346     return createIndex(row, 0);
347 }
348
349 /*!
350     \qmlproperty string FolderListModel::folder
351
352     The \a folder property holds a URL for the folder that the model is
353     currently providing.
354
355     The value is a URL expressed as a string, and must be a \c file: or \c qrc:
356     URL, or a relative URL.
357
358     By default, the value is an invalid URL.
359 */
360 QUrl QQuickFolderListModel::folder() const
361 {
362     Q_D(const QQuickFolderListModel);
363     return d->currentDir;
364 }
365
366 void QQuickFolderListModel::setFolder(const QUrl &folder)
367 {
368     Q_D(QQuickFolderListModel);
369
370     if (folder == d->currentDir)
371         return;
372
373     QString localPath = QQmlFile::urlToLocalFileOrQrc(folder);
374     QString resolvedPath = QDir::cleanPath(QUrl(localPath).path());
375
376     beginResetModel();
377
378     //Remove the old path for the file system watcher
379     if (!d->currentDir.isEmpty())
380         d->fileInfoThread.removePath(d->currentDir.path());
381
382     d->currentDir = folder;
383
384     QFileInfo info(resolvedPath);
385     if (!info.exists() || !info.isDir()) {
386         d->data.clear();
387         endResetModel();
388         emit rowCountChanged();
389         return;
390     }
391
392     d->fileInfoThread.setPath(resolvedPath);
393 }
394
395
396 /*!
397    \qmlproperty string QQuickFolderListModel::rootFolder
398
399    When the rootFolder is set, then this folder will
400    be threated as the root in the file system, so that
401    you can only travers sub folders from this rootFolder.
402 */
403 QUrl QQuickFolderListModel::rootFolder() const
404 {
405     Q_D(const QQuickFolderListModel);
406     return d->rootDir;
407 }
408
409 void QQuickFolderListModel::setRootFolder(const QUrl &path)
410 {
411     Q_D(QQuickFolderListModel);
412
413     if (path.isEmpty())
414         return;
415
416     QString localPath = QQmlFile::urlToLocalFileOrQrc(path);
417     QString resolvedPath = QDir::cleanPath(QUrl(localPath).path());
418
419     QFileInfo info(resolvedPath);
420     if (!info.exists() || !info.isDir())
421         return;
422
423     d->fileInfoThread.setRootPath(resolvedPath);
424     d->rootDir = path;
425 }
426
427
428 /*!
429     \qmlproperty url FolderListModel::parentFolder
430
431     Returns the URL of the parent of of the current \l folder.
432 */
433 QUrl QQuickFolderListModel::parentFolder() const
434 {
435     Q_D(const QQuickFolderListModel);
436
437     QString localFile = d->currentDir.toLocalFile();
438     if (!localFile.isEmpty()) {
439         QDir dir(localFile);
440 #if defined(Q_OS_WIN)
441         if (dir.isRoot())
442             dir.setPath("");
443         else
444 #endif
445             dir.cdUp();
446         localFile = dir.path();
447     } else {
448         int pos = d->currentDir.path().lastIndexOf(QLatin1Char('/'));
449         if (pos == -1)
450             return QUrl();
451         localFile = d->currentDir.path().left(pos);
452     }
453     return QUrl::fromLocalFile(localFile);
454 }
455
456 /*!
457     \qmlproperty list<string> FolderListModel::nameFilters
458
459     The \a nameFilters property contains a list of file name filters.
460     The filters may include the ? and * wildcards.
461
462     The example below filters on PNG and JPEG files:
463
464     \qml
465     FolderListModel {
466         nameFilters: [ "*.png", "*.jpg" ]
467     }
468     \endqml
469
470     \note Directories are not excluded by filters.
471 */
472 QStringList QQuickFolderListModel::nameFilters() const
473 {
474     Q_D(const QQuickFolderListModel);
475     return d->nameFilters;
476 }
477
478 void QQuickFolderListModel::setNameFilters(const QStringList &filters)
479 {
480     Q_D(QQuickFolderListModel);
481     d->fileInfoThread.setNameFilters(filters);
482     d->nameFilters = filters;
483 }
484
485 void QQuickFolderListModel::classBegin()
486 {
487 }
488
489 void QQuickFolderListModel::componentComplete()
490 {
491     Q_D(QQuickFolderListModel);
492
493     if (!d->currentDir.isValid() || d->currentDir.toLocalFile().isEmpty() || !QDir().exists(d->currentDir.toLocalFile()))
494         setFolder(QUrl(QLatin1String("file://")+QDir::currentPath()));
495 }
496
497 /*!
498     \qmlproperty enumeration FolderListModel::sortField
499
500     The \a sortField property contains field to use for sorting.  sortField
501     may be one of:
502     \list
503     \li Unsorted - no sorting is applied.
504     \li Name - sort by filename
505     \li LastModified - sort by time modified
506     \li Size - sort by file size
507     \li Type - sort by file type (extension)
508     \endlist
509
510     \sa sortReversed
511 */
512 QQuickFolderListModel::SortField QQuickFolderListModel::sortField() const
513 {
514     Q_D(const QQuickFolderListModel);
515     return d->sortField;
516 }
517
518 void QQuickFolderListModel::setSortField(SortField field)
519 {
520     Q_D(QQuickFolderListModel);
521     if (field != d->sortField) {
522         d->sortField = field;
523         d->updateSorting();
524     }
525 }
526
527 int QQuickFolderListModel::roleFromString(const QString &roleName) const
528 {
529     Q_D(const QQuickFolderListModel);
530     return d->roleNames.key(roleName.toLatin1(), -1);
531 }
532
533 /*!
534     \qmlproperty bool FolderListModel::sortReversed
535
536     If set to true, reverses the sort order.  The default is false.
537
538     \sa sortField
539 */
540 bool QQuickFolderListModel::sortReversed() const
541 {
542     Q_D(const QQuickFolderListModel);
543     return d->sortReversed;
544 }
545
546 void QQuickFolderListModel::setSortReversed(bool rev)
547 {
548     Q_D(QQuickFolderListModel);
549
550     if (rev != d->sortReversed) {
551         d->sortReversed = rev;
552         d->updateSorting();
553     }
554 }
555
556 /*!
557     \qmlmethod bool FolderListModel::isFolder(int index)
558
559     Returns true if the entry \a index is a folder; otherwise
560     returns false.
561 */
562 bool QQuickFolderListModel::isFolder(int index) const
563 {
564     if (index != -1) {
565         QModelIndex idx = createIndex(index, 0);
566         if (idx.isValid()) {
567             QVariant var = data(idx, FileIsDirRole);
568             if (var.isValid())
569                 return var.toBool();
570         }
571     }
572     return false;
573 }
574
575 /*!
576     \qmlproperty bool FolderListModel::showDirs
577
578     If true, directories are included in the model; otherwise only files
579     are included.
580
581     By default, this property is true.
582
583     Note that the nameFilters are not applied to directories.
584
585     \sa showDotAndDotDot
586 */
587 bool QQuickFolderListModel::showDirs() const
588 {
589     Q_D(const QQuickFolderListModel);
590     return d->showDirs;
591 }
592
593 void  QQuickFolderListModel::setShowDirs(bool on)
594 {
595     Q_D(QQuickFolderListModel);
596
597     d->fileInfoThread.setShowDirs(on);
598     d->showDirs = on;
599 }
600
601 /*!
602     \qmlproperty bool FolderListModel::showDirsFirst
603
604     If true, if directories are included in the model they will
605     always be shown first, then the files.
606
607     By default, this property is false.
608
609 */
610 bool QQuickFolderListModel::showDirsFirst() const
611 {
612     Q_D(const QQuickFolderListModel);
613     return d->showDirsFirst;
614 }
615
616 void  QQuickFolderListModel::setShowDirsFirst(bool on)
617 {
618     Q_D(QQuickFolderListModel);
619
620     d->fileInfoThread.setShowDirsFirst(on);
621     d->showDirsFirst = on;
622 }
623
624
625 /*!
626     \qmlproperty bool FolderListModel::showDotAndDotDot
627
628     If true, the "." and ".." directories are included in the model; otherwise
629     they are excluded.
630
631     By default, this property is false.
632
633     \sa showDirs
634 */
635 bool QQuickFolderListModel::showDotAndDotDot() const
636 {
637     Q_D(const QQuickFolderListModel);
638     return d->showDots;
639 }
640
641 void  QQuickFolderListModel::setShowDotAndDotDot(bool on)
642 {
643     Q_D(QQuickFolderListModel);
644
645     if (on != d->showDots) {
646         d->fileInfoThread.setShowDotDot(on);
647     }
648 }
649
650 /*!
651     \qmlproperty bool FolderListModel::showOnlyReadable
652
653     If true, only readable files and directories are shown; otherwise all files
654     and directories are shown.
655
656     By default, this property is false.
657
658     \sa showDirs
659 */
660 bool QQuickFolderListModel::showOnlyReadable() const
661 {
662     Q_D(const QQuickFolderListModel);
663     return d->showOnlyReadable;
664 }
665
666 void QQuickFolderListModel::setShowOnlyReadable(bool on)
667 {
668     Q_D(QQuickFolderListModel);
669
670     if (on != d->showOnlyReadable) {
671         d->fileInfoThread.setShowOnlyReadable(on);
672     }
673 }
674
675 /*!
676     \qmlmethod QVariant QQuickFolderListModel::get(int idx, const QString &property) const
677
678     Get the folder property for the given index. The following properties
679     are available.
680
681     \list
682         \li \c fileName
683         \li \c filePath
684         \li \c fileBaseName
685         \li \c fileSuffix
686         \li \c fileSize
687         \li \c fileModified
688         \li \c fileAccessed
689         \li \c fileIsDir
690     \endlist
691 */
692 QVariant QQuickFolderListModel::get(int idx, const QString &property) const
693 {
694     int role = roleFromString(property);
695     if (role >= 0 && idx >= 0)
696         return data(index(idx, 0), role);
697     else
698         return QVariant();
699 }
700
701 #include "moc_qquickfolderlistmodel.cpp"
702
703 //![code]
704 QT_END_NAMESPACE