294fe184d22dd2bb6b883f0bd8f4c4232abeff75
[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 <QDebug>
47 #include <qqmlcontext.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 resolvedPath = QDir::cleanPath(folder.path());
369
370     beginResetModel();
371
372     //Remove the old path for the file system watcher
373     if (!d->currentDir.isEmpty())
374         d->fileInfoThread.removePath(d->currentDir.path());
375
376     d->currentDir = folder;
377
378     QFileInfo info(resolvedPath);
379     if (!info.exists() || !info.isDir()) {
380         d->data.clear();
381         endResetModel();
382         emit rowCountChanged();
383         return;
384     }
385
386     d->fileInfoThread.setPath(resolvedPath);
387 }
388
389
390 /*!
391    \qmlproperty string QQuickFolderListModel::rootFolder
392
393    When the rootFolder is set, then this folder will
394    be threated as the root in the file system, so that
395    you can only travers sub folders from this rootFolder.
396 */
397 QUrl QQuickFolderListModel::rootFolder() const
398 {
399     Q_D(const QQuickFolderListModel);
400     return d->rootDir;
401 }
402
403 void QQuickFolderListModel::setRootFolder(const QUrl &path)
404 {
405     Q_D(QQuickFolderListModel);
406
407     if (path.isEmpty())
408         return;
409
410     QString resolvedPath = QDir::cleanPath(path.path());
411
412     QFileInfo info(resolvedPath);
413     if (!info.exists() || !info.isDir())
414         return;
415
416     d->fileInfoThread.setRootPath(resolvedPath);
417     d->rootDir = path;
418 }
419
420
421 /*!
422     \qmlproperty url FolderListModel::parentFolder
423
424     Returns the URL of the parent of of the current \l folder.
425 */
426 QUrl QQuickFolderListModel::parentFolder() const
427 {
428     Q_D(const QQuickFolderListModel);
429
430     QString localFile = d->currentDir.toLocalFile();
431     if (!localFile.isEmpty()) {
432         QDir dir(localFile);
433 #if defined(Q_OS_WIN)
434         if (dir.isRoot())
435             dir.setPath("");
436         else
437 #endif
438             dir.cdUp();
439         localFile = dir.path();
440     } else {
441         int pos = d->currentDir.path().lastIndexOf(QLatin1Char('/'));
442         if (pos == -1)
443             return QUrl();
444         localFile = d->currentDir.path().left(pos);
445     }
446     return QUrl::fromLocalFile(localFile);
447 }
448
449 /*!
450     \qmlproperty list<string> FolderListModel::nameFilters
451
452     The \a nameFilters property contains a list of file name filters.
453     The filters may include the ? and * wildcards.
454
455     The example below filters on PNG and JPEG files:
456
457     \qml
458     FolderListModel {
459         nameFilters: [ "*.png", "*.jpg" ]
460     }
461     \endqml
462
463     \note Directories are not excluded by filters.
464 */
465 QStringList QQuickFolderListModel::nameFilters() const
466 {
467     Q_D(const QQuickFolderListModel);
468     return d->nameFilters;
469 }
470
471 void QQuickFolderListModel::setNameFilters(const QStringList &filters)
472 {
473     Q_D(QQuickFolderListModel);
474     d->fileInfoThread.setNameFilters(filters);
475     d->nameFilters = filters;
476 }
477
478 void QQuickFolderListModel::classBegin()
479 {
480 }
481
482 void QQuickFolderListModel::componentComplete()
483 {
484     Q_D(QQuickFolderListModel);
485
486     if (!d->currentDir.isValid() || d->currentDir.toLocalFile().isEmpty() || !QDir().exists(d->currentDir.toLocalFile()))
487         setFolder(QUrl(QLatin1String("file://")+QDir::currentPath()));
488 }
489
490 /*!
491     \qmlproperty enumeration FolderListModel::sortField
492
493     The \a sortField property contains field to use for sorting.  sortField
494     may be one of:
495     \list
496     \li Unsorted - no sorting is applied.
497     \li Name - sort by filename
498     \li LastModified - sort by time modified
499     \li Size - sort by file size
500     \li Type - sort by file type (extension)
501     \endlist
502
503     \sa sortReversed
504 */
505 QQuickFolderListModel::SortField QQuickFolderListModel::sortField() const
506 {
507     Q_D(const QQuickFolderListModel);
508     return d->sortField;
509 }
510
511 void QQuickFolderListModel::setSortField(SortField field)
512 {
513     Q_D(QQuickFolderListModel);
514     if (field != d->sortField) {
515         d->sortField = field;
516         d->updateSorting();
517     }
518 }
519
520 int QQuickFolderListModel::roleFromString(const QString &roleName) const
521 {
522     Q_D(const QQuickFolderListModel);
523     return d->roleNames.key(roleName.toLatin1(), -1);
524 }
525
526 /*!
527     \qmlproperty bool FolderListModel::sortReversed
528
529     If set to true, reverses the sort order.  The default is false.
530
531     \sa sortField
532 */
533 bool QQuickFolderListModel::sortReversed() const
534 {
535     Q_D(const QQuickFolderListModel);
536     return d->sortReversed;
537 }
538
539 void QQuickFolderListModel::setSortReversed(bool rev)
540 {
541     Q_D(QQuickFolderListModel);
542
543     if (rev != d->sortReversed) {
544         d->sortReversed = rev;
545         d->updateSorting();
546     }
547 }
548
549 /*!
550     \qmlmethod bool FolderListModel::isFolder(int index)
551
552     Returns true if the entry \a index is a folder; otherwise
553     returns false.
554 */
555 bool QQuickFolderListModel::isFolder(int index) const
556 {
557     if (index != -1) {
558         QModelIndex idx = createIndex(index, 0);
559         if (idx.isValid()) {
560             QVariant var = data(idx, FileIsDirRole);
561             if (var.isValid())
562                 return var.toBool();
563         }
564     }
565     return false;
566 }
567
568 /*!
569     \qmlproperty bool FolderListModel::showDirs
570
571     If true, directories are included in the model; otherwise only files
572     are included.
573
574     By default, this property is true.
575
576     Note that the nameFilters are not applied to directories.
577
578     \sa showDotAndDotDot
579 */
580 bool QQuickFolderListModel::showDirs() const
581 {
582     Q_D(const QQuickFolderListModel);
583     return d->showDirs;
584 }
585
586 void  QQuickFolderListModel::setShowDirs(bool on)
587 {
588     Q_D(QQuickFolderListModel);
589
590     d->fileInfoThread.setShowDirs(on);
591     d->showDirs = on;
592 }
593
594 /*!
595     \qmlproperty bool FolderListModel::showDirsFirst
596
597     If true, if directories are included in the model they will
598     always be shown first, then the files.
599
600     By default, this property is false.
601
602 */
603 bool QQuickFolderListModel::showDirsFirst() const
604 {
605     Q_D(const QQuickFolderListModel);
606     return d->showDirsFirst;
607 }
608
609 void  QQuickFolderListModel::setShowDirsFirst(bool on)
610 {
611     Q_D(QQuickFolderListModel);
612
613     d->fileInfoThread.setShowDirsFirst(on);
614     d->showDirsFirst = on;
615 }
616
617
618 /*!
619     \qmlproperty bool FolderListModel::showDotAndDotDot
620
621     If true, the "." and ".." directories are included in the model; otherwise
622     they are excluded.
623
624     By default, this property is false.
625
626     \sa showDirs
627 */
628 bool QQuickFolderListModel::showDotAndDotDot() const
629 {
630     Q_D(const QQuickFolderListModel);
631     return d->showDots;
632 }
633
634 void  QQuickFolderListModel::setShowDotAndDotDot(bool on)
635 {
636     Q_D(QQuickFolderListModel);
637
638     if (on != d->showDots) {
639         d->fileInfoThread.setShowDotDot(on);
640     }
641 }
642
643 /*!
644     \qmlproperty bool FolderListModel::showOnlyReadable
645
646     If true, only readable files and directories are shown; otherwise all files
647     and directories are shown.
648
649     By default, this property is false.
650
651     \sa showDirs
652 */
653 bool QQuickFolderListModel::showOnlyReadable() const
654 {
655     Q_D(const QQuickFolderListModel);
656     return d->showOnlyReadable;
657 }
658
659 void QQuickFolderListModel::setShowOnlyReadable(bool on)
660 {
661     Q_D(QQuickFolderListModel);
662
663     if (on != d->showOnlyReadable) {
664         d->fileInfoThread.setShowOnlyReadable(on);
665     }
666 }
667
668 /*!
669     \qmlmethod QVariant QQuickFolderListModel::get(int idx, const QString &property) const
670
671     Get the folder property for the given index. The following properties
672     are available.
673
674     \list
675         \li \c fileName
676         \li \c filePath
677         \li \c fileBaseName
678         \li \c fileSuffix
679         \li \c fileSize
680         \li \c fileModified
681         \li \c fileAccessed
682         \li \c fileIsDir
683     \endlist
684 */
685 QVariant QQuickFolderListModel::get(int idx, const QString &property) const
686 {
687     int role = roleFromString(property);
688     if (role >= 0 && idx >= 0)
689         return data(index(idx, 0), role);
690     else
691         return QVariant();
692 }
693
694 #include "moc_qquickfolderlistmodel.cpp"
695
696 //![code]
697 QT_END_NAMESPACE