1/****************************************************************************
2**
3** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtDocGallery module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qdeclarativegalleryquerymodel.h"
43
44#include "qdeclarativegalleryfilter.h"
45
46#include <qgalleryresultset.h>
47
48#include <QtQml/qqmlinfo.h>
49#include <QtQml/qjsengine.h>
50#include <QtCore/qcoreapplication.h>
51
52QT_BEGIN_NAMESPACE_DOCGALLERY
53
54QDeclarativeGalleryQueryModel::QDeclarativeGalleryQueryModel(QObject *parent)
55 : QAbstractListModel(parent)
56 , m_resultSet(0)
57 , m_status(Null)
58 , m_rowCount(0)
59 , m_updateStatus(Incomplete)
60{
61 connect(sender: &m_request, SIGNAL(stateChanged(QGalleryAbstractRequest::State)),
62 receiver: this, SLOT(_q_stateChanged()));
63 connect(sender: &m_request, SIGNAL(progressChanged(int,int)), receiver: this, SIGNAL(progressChanged()));
64
65 connect(sender: &m_request, SIGNAL(resultSetChanged(QGalleryResultSet*)),
66 receiver: this, SLOT(_q_setResultSet(QGalleryResultSet*)));
67}
68
69QDeclarativeGalleryQueryModel::~QDeclarativeGalleryQueryModel()
70{
71}
72
73void QDeclarativeGalleryQueryModel::componentComplete()
74{
75 m_updateStatus = NoUpdate;
76
77 if (m_filter) {
78 connect(sender: m_filter.data(), SIGNAL(filterChanged()), receiver: this, SLOT(deferredExecute()));
79
80 m_request.setFilter(m_filter.data()->filter());
81 }
82 m_request.execute();
83}
84
85qreal QDeclarativeGalleryQueryModel::progress() const
86{
87 const int max = m_request.maximumProgress();
88
89 return max > 0 ? qreal(m_request.currentProgress()) / max : qreal(0.0);
90}
91
92void QDeclarativeGalleryQueryModel::setPropertyNames(const QStringList &names)
93{
94 if (m_updateStatus == Incomplete) {
95 m_request.setPropertyNames(names);
96
97 emit propertyNamesChanged();
98 }
99}
100
101void QDeclarativeGalleryQueryModel::setSortPropertyNames(const QStringList &names)
102{
103 if (m_request.sortPropertyNames() != names) {
104 m_request.setSortPropertyNames(names);
105
106 deferredExecute();
107
108 emit sortPropertyNamesChanged();
109 }
110}
111
112void QDeclarativeGalleryQueryModel::setAutoUpdate(bool enabled)
113{
114 if (m_request.autoUpdate() != enabled) {
115 m_request.setAutoUpdate(enabled);
116
117 if (enabled)
118 deferredExecute();
119 else if (m_status == Idle)
120 m_request.cancel();
121
122 emit autoUpdateChanged();
123 }
124}
125
126void QDeclarativeGalleryQueryModel::setScope(Scope scope)
127{
128 if (m_request.scope() != QGalleryQueryRequest::Scope(scope)) {
129 m_request.setScope(QGalleryQueryRequest::Scope(scope));
130
131 deferredExecute();
132
133 emit scopeChanged();
134 }
135}
136
137void QDeclarativeGalleryQueryModel::setRootItem(const QVariant &itemId)
138{
139 if (m_request.rootItem() != itemId) {
140 m_request.setRootItem(itemId);
141
142 deferredExecute();
143
144 emit rootItemChanged();
145 }
146}
147
148void QDeclarativeGalleryQueryModel::setFilter(QDeclarativeGalleryFilterBase *filter)
149{
150 if (m_filter)
151 disconnect(sender: m_filter.data(), SIGNAL(filterChanged()), receiver: this, SLOT(deferredExecute()));
152
153 m_filter = filter;
154
155 if (m_filter)
156 connect(sender: m_filter.data(), SIGNAL(filterChanged()), receiver: this, SLOT(deferredExecute()));
157
158 deferredExecute();
159
160 emit filterChanged();
161}
162
163void QDeclarativeGalleryQueryModel::setOffset(int offset)
164{
165 if (m_request.offset() != offset) {
166 m_request.setOffset(offset);
167
168 deferredExecute();
169
170 emit offsetChanged();
171 }
172}
173
174void QDeclarativeGalleryQueryModel::setLimit(int limit)
175{
176 if (m_request.limit() != limit) {
177 m_request.setLimit(limit);
178
179 deferredExecute();
180
181 emit limitChanged();
182 }
183}
184
185void QDeclarativeGalleryQueryModel::reload()
186{
187 if (m_updateStatus == PendingUpdate)
188 m_updateStatus = CanceledUpdate;
189
190 m_request.setFilter(m_filter ? m_filter.data()->filter() : QGalleryFilter());
191
192 m_request.execute();
193}
194
195void QDeclarativeGalleryQueryModel::cancel()
196{
197 if (m_updateStatus == PendingUpdate)
198 m_updateStatus = CanceledUpdate;
199
200 m_request.cancel();
201}
202
203void QDeclarativeGalleryQueryModel::clear()
204{
205 if (m_updateStatus == PendingUpdate)
206 m_updateStatus = CanceledUpdate;
207
208 m_request.clear();
209}
210
211
212int QDeclarativeGalleryQueryModel::rowCount(const QModelIndex &parent) const
213{
214 return !parent.isValid() ? m_rowCount : 0;
215}
216
217QVariant QDeclarativeGalleryQueryModel::data(const QModelIndex &index, int role) const
218{
219 if (index.isValid()) {
220 if (m_resultSet->currentIndex() != index.row())
221 m_resultSet->fetch(index: index.row());
222
223 switch (role) {
224 case ItemId:
225 return m_resultSet->itemId();
226 case ItemType:
227 return itemType(type: m_resultSet->itemType());
228 default:
229 {
230 QVariant value = m_resultSet->metaData(key: role - MetaDataOffset);
231
232 return value.isNull()
233 ? QVariant(m_resultSet->propertyType(key: role - MetaDataOffset))
234 : value;
235 }
236 }
237 } else {
238 return QVariant();
239 }
240}
241
242bool QDeclarativeGalleryQueryModel::setData(
243 const QModelIndex &index, const QVariant &value, int role)
244{
245 if (index.isValid() && (role -= MetaDataOffset) >= 0) {
246 if (m_resultSet->currentIndex() != index.row() && !m_resultSet->fetch(index: index.row()))
247 return false;
248
249 return m_resultSet->setMetaData(key: role, value);
250 } else {
251 return false;
252 }
253
254}
255
256QModelIndex QDeclarativeGalleryQueryModel::index(int row, int column, const QModelIndex &parent) const
257{
258 return !parent.isValid() && row >= 0 && row < m_rowCount && column == 0
259 ? createIndex(arow: row, acolumn: column)
260 : QModelIndex();
261}
262
263QJSValue QDeclarativeGalleryQueryModel::get(const QJSValue &index) const
264{
265 QJSEngine *scriptEngine = index.engine();
266
267 if (!scriptEngine)
268 return QJSValue();
269
270 const int i = index.toInt();
271
272 if (i < 0 || i >= m_rowCount || (i != m_resultSet->currentIndex() && !m_resultSet->fetch(index: i)))
273 return QJSValue();
274
275 QJSValue object = scriptEngine->newObject();
276
277 object.setProperty(
278 name: QLatin1String("itemId"), value: scriptEngine->toScriptValue(value: m_resultSet->itemId()));
279 object.setProperty(
280 name: QLatin1String("itemUrl"), value: scriptEngine->toScriptValue(value: m_resultSet->itemUrl()));
281
282 typedef QVector<QPair<int, QString> >::const_iterator iterator;
283 for (iterator it = m_propertyNames.constBegin(), end = m_propertyNames.constEnd();
284 it != end;
285 ++it) {
286 QVariant value = m_resultSet->metaData(key: it->first);
287
288 if (value.isNull())
289 value = QVariant(m_resultSet->propertyType(key: it->first));
290
291 object.setProperty(name: it->second, value: scriptEngine->toScriptValue(value));
292 }
293
294 return object;
295}
296
297QVariant QDeclarativeGalleryQueryModel::property(int index, const QString &property) const
298{
299 if (index < 0
300 || index >= m_rowCount
301 || (m_resultSet->currentIndex() != index && !m_resultSet->fetch(index))) {
302 return QVariant();
303 }
304
305 if (property == QLatin1String("itemId")) {
306 return m_resultSet->itemId();
307 } else if (property == QLatin1String("itemType")) {
308 return itemType(type: m_resultSet->itemType());
309 } else {
310 const int propertyKey = m_resultSet->propertyKey(property);
311
312 const QVariant value = m_resultSet->metaData(key: propertyKey);
313
314 return value.isNull()
315 ? QVariant(m_resultSet->propertyType(key: propertyKey))
316 : value;
317 }
318}
319
320void QDeclarativeGalleryQueryModel::set(int index, const QJSValue &values)
321{
322 if (index < 0
323 || index >= m_rowCount
324 || (m_resultSet->currentIndex() != index && !m_resultSet->fetch(index))) {
325 return;
326 }
327
328 QVariant variant = values.toVariant();
329
330 if (variant.type() == QVariant::List) {
331 const QVariantList list = variant.toList();
332 for (int i = 0; i < list.size(); ++i)
333 m_resultSet->setMetaData(key: m_resultSet->propertyKey(property: QString::number(i)), value: list[i]);
334 } else if (variant.type() == QVariant::Map) {
335 const QVariantMap map = variant.toMap();
336 const QVariantMap::const_iterator end = map.end();
337 for (QVariantMap::const_iterator it = map.begin(); it != end; ++it)
338 m_resultSet->setMetaData(key: m_resultSet->propertyKey(property: it.key()), value: it.value());
339 }
340}
341
342void QDeclarativeGalleryQueryModel::setProperty(
343 int index, const QString &property, const QVariant &value)
344{
345 if (index < 0
346 || index >= m_rowCount
347 || (m_resultSet->currentIndex() != index && !m_resultSet->fetch(index))) {
348 return;
349 }
350
351 m_resultSet->setMetaData(key: m_resultSet->propertyKey(property), value);
352}
353
354
355
356void QDeclarativeGalleryQueryModel::deferredExecute()
357{
358 if (m_updateStatus == NoUpdate) {
359 m_updateStatus = PendingUpdate;
360
361 QCoreApplication::postEvent(receiver: this, event: new QEvent(QEvent::UpdateRequest));
362 } else if (m_updateStatus == CanceledUpdate) {
363 m_updateStatus = PendingUpdate;
364 }
365}
366
367bool QDeclarativeGalleryQueryModel::event(QEvent *event)
368{
369 if (event->type() == QEvent::UpdateRequest) {
370 UpdateStatus status = m_updateStatus;
371 m_updateStatus = NoUpdate;
372
373 if (status == PendingUpdate) {
374 m_request.setFilter(m_filter ? m_filter.data()->filter() : QGalleryFilter());
375 m_request.execute();
376 }
377
378 return true;
379 } else {
380 return QAbstractListModel::event(event);
381 }
382}
383
384void QDeclarativeGalleryQueryModel::_q_stateChanged()
385{
386 m_status = Status(m_request.state());
387
388 if (m_status == Error) {
389 const QString message = m_request.errorString();
390
391 if (!message.isEmpty()) {
392 qmlInfo(me: this) << message;
393 } else {
394 switch (m_request.error()) {
395 case QDocumentGallery::ConnectionError:
396 qmlInfo(me: this) << tr(s: "An error was encountered connecting to the document gallery");
397 break;
398 case QDocumentGallery::ItemTypeError:
399 qmlInfo(me: this) << (m_request.rootType().isEmpty()
400 ? tr(s: "DocumentGallery.InvalidType is not a supported item type")
401 : tr(s: "DocumentGallery.%1 is not a supported item type")
402 .arg(a: m_request.rootType()));
403 break;
404 case QDocumentGallery::ItemIdError:
405 qmlInfo(me: this) << tr(s: "The value of rootItem is not a valid item ID");
406 break;
407 case QDocumentGallery::FilterError:
408 qmlInfo(me: this) << tr(s: "The value of filter is unsupported");
409 default:
410 break;
411 }
412 }
413 emit statusChanged();
414 } else if (m_status == Idle && !m_request.autoUpdate()) {
415 m_request.cancel();
416 } else {
417 emit statusChanged();
418 }
419}
420
421void QDeclarativeGalleryQueryModel::_q_setResultSet(QGalleryResultSet *resultSet)
422{
423 if (m_rowCount > 0) {
424 beginRemoveRows(parent: QModelIndex(), first: 0, last: m_rowCount - 1);
425 m_rowCount = 0;
426 m_resultSet = resultSet;
427 endRemoveRows();
428 } else {
429 m_resultSet = resultSet;
430 }
431
432 if (m_resultSet) {
433 QHash<int, QByteArray> roleNames;
434 m_propertyNames.clear();
435
436 QStringList propertyNames = m_request.propertyNames();
437
438 typedef QStringList::const_iterator iterator;
439 for (iterator it = propertyNames.constBegin(), end = propertyNames.constEnd();
440 it != end;
441 ++it) {
442 const int key = m_resultSet->propertyKey(property: *it);
443
444 if (key >= 0) {
445 roleNames.insert(akey: key + MetaDataOffset, avalue: it->toLatin1());
446 m_propertyNames.append(t: qMakePair(x: key, y: *it));
447 }
448 }
449 roleNames.insert(akey: ItemId, avalue: QByteArray("itemId"));
450 roleNames.insert(akey: ItemType, avalue: QByteArray("itemType"));
451
452 setRoleNames(roleNames);
453
454 connect(sender: m_resultSet, SIGNAL(itemsInserted(int,int)),
455 receiver: this, SLOT(_q_itemsInserted(int,int)));
456 connect(sender: m_resultSet, SIGNAL(itemsRemoved(int,int)),
457 receiver: this, SLOT(_q_itemsRemoved(int,int)));
458 connect(sender: m_resultSet, SIGNAL(itemsMoved(int,int,int)),
459 receiver: this, SLOT(_q_itemsMoved(int,int,int)));
460 connect(sender: m_resultSet, SIGNAL(metaDataChanged(int,int,QList<int>)),
461 receiver: this, SLOT(_q_itemsChanged(int,int)));
462
463 const int rowCount = m_resultSet->itemCount();
464 if (rowCount > 0) {
465 beginInsertRows(parent: QModelIndex(), first: 0, last: rowCount - 1);
466 m_rowCount = rowCount;
467 endInsertRows();
468 }
469 }
470
471 emit countChanged();
472}
473
474void QDeclarativeGalleryQueryModel::_q_itemsInserted(int index, int count)
475{
476 beginInsertRows(parent: QModelIndex(), first: index, last: index + count - 1);
477 m_rowCount += count;
478 endInsertRows();
479
480 emit countChanged();
481}
482
483void QDeclarativeGalleryQueryModel::_q_itemsRemoved(int index, int count)
484{
485 beginRemoveRows(parent: QModelIndex(), first: index, last: index + count - 1);
486 m_rowCount -= count;
487 endRemoveRows();
488
489 emit countChanged();
490}
491
492void QDeclarativeGalleryQueryModel::_q_itemsMoved(int from, int to, int count)
493{
494 beginMoveRows(sourceParent: QModelIndex(), sourceFirst: from, sourceLast: from + count - 1, destinationParent: QModelIndex(), destinationRow: to);
495 endMoveRows();
496}
497
498void QDeclarativeGalleryQueryModel::_q_itemsChanged(int index, int count)
499{
500 emit dataChanged(topLeft: createIndex(arow: index, acolumn: 0), bottomRight: createIndex(arow: index + count - 1, acolumn: 0));
501}
502
503/*!
504 \qmltype DocumentGalleryModel
505 \instantiates QDeclarativeDocumentGalleryModel
506
507 \inmodule QtDocGallery
508
509 \brief The DocumentGalleryModel element is used to specify a model
510 containing items from the document gallery.
511
512 \ingroup qml-gallery
513
514 This element is part of the \b {QtMobility.gallery 1.1} module.
515
516 The properties that should be returned for each item by the query are
517 specified in \l properties. In addition all queries return the following
518 properties:
519
520 \list
521 \li itemId The ID of an item.
522 \li itemType The type of an item.
523 \endlist
524
525 \qml
526 import Qt 4.7
527 import QtMobility.gallery 1.1
528
529 Rectangle {
530 width: 1024
531 height: 768
532
533 GridView {
534 anchors.fill: parent
535 cellWidth: 128
536 cellHeight: 128
537
538 model: DocumentGalleryModel {
539 rootType: DocumentGallery.Image
540 properties: [ "url" ]
541 filter: GalleryWildcardFilter {
542 property: "fileName";
543 value: "*.jpg";
544 }
545 }
546
547 delegate: Image {
548 source: url
549 width: 128
550 height: 128
551 }
552 }
553 }
554 \endqml
555
556 \sa DocumentGalleryItem, DocumentGalleryType
557*/
558
559QDeclarativeDocumentGalleryModel::QDeclarativeDocumentGalleryModel(QObject *parent)
560 : QDeclarativeGalleryQueryModel(parent)
561{
562 m_request.setRootType(QDocumentGallery::File);
563}
564
565QDeclarativeDocumentGalleryModel::~QDeclarativeDocumentGalleryModel()
566{
567}
568
569void QDeclarativeDocumentGalleryModel::classBegin()
570{
571 m_request.setGallery(QDeclarativeDocumentGallery::gallery(object: this));
572}
573
574/*!
575 \qmlproperty enum DocumentGalleryModel::status
576
577 This property holds the status of a query. It can be one of:
578
579 \list
580 \li Null No query parameters have been specified.
581 \li Active Items matching the query parameters are being fetched from the
582 gallery.
583 \li Finished The query has finished
584 \li Idle The query is finished and will be automatically updated as new
585 items become available.
586 \li Canceling The query was canceled but hasn't yet reached the
587 canceled status.
588 \li Canceled The query was canceled.
589 \li Error Information about a type could not be retrieved due to an error.
590 \endlist
591*/
592
593/*!
594 \qmlproperty real DocumentGalleryModel::progress
595
596 This property holds the current progress of the request, from 0.0 (started)
597 to 1.0 (finished).
598*/
599
600/*!
601 \qmlproperty QStringList DocumentGalleryModel::properties
602
603 This property holds the item properties a query should return values for.
604 All supported properties are listed \l QDocumentGallery.
605*/
606
607/*!
608 \qmlproperty QStringList DocumentGalleryModel::sortProperties
609
610 This property holds the properties the results of a query should be sorted
611 on.
612
613 Prefixing a property name with the '+' character indicates it should be sorted
614 in ascending order, and a '-' character prefix indicates a descending order. If
615 there is no prefix ascending order is assumed.
616*/
617
618/*!
619 \qmlproperty bool DocumentGalleryModel::autoUpdate
620
621 This property holds whether a query should refresh its results
622 automatically.
623*/
624
625/*!
626 \qmlproperty int DocumentGalleryModel::offset
627
628 This property holds the offset of the first item returned by a query.
629*/
630
631/*!
632 \qmlproperty int DocumentGalleryModel::limit
633
634 This property contains the maximum number of items returned by a query.
635*/
636
637/*!
638 \qmlproperty enum DocumentGalleryModel::rootType
639
640 This property contains the type of item a query should return.
641 It can be one of:
642
643 \list
644 \li DocumentGallery.InvalidType
645 \li DocumentGallery.File
646 \li DocumentGallery.Folder
647 \li DocumentGallery.Document
648 \li DocumentGallery.Text
649 \li DocumentGallery.Audio
650 \li DocumentGallery.Image
651 \li DocumentGallery.Video
652 \li DocumentGallery.Playlist
653 \li DocumentGallery.Artist
654 \li DocumentGallery.AlbumArtist
655 \li DocumentGallery.Album
656 \li DocumentGallery.AudioGenre
657 \li DocumentGallery.PhotoAlbum
658 \endlist
659
660 The default value is DocumentGallery.File
661*/
662
663QDeclarativeDocumentGallery::ItemType QDeclarativeDocumentGalleryModel::rootType() const
664{
665 return QDeclarativeDocumentGallery::itemTypeFromString(string: m_request.rootType());
666}
667
668void QDeclarativeDocumentGalleryModel::setRootType(QDeclarativeDocumentGallery::ItemType itemType)
669{
670 if (m_updateStatus == Incomplete) {
671 m_request.setRootType(QDeclarativeDocumentGallery::toString(type: itemType));
672
673 emit rootTypeChanged();
674 }
675}
676
677/*!
678 \qmlproperty GalleryFilter DocumentGalleryModel::filter
679
680 This property contains criteria to used to filter the results of a query.
681*/
682
683/*!
684 \qmlproperty variant DocumentGalleryModel::rootItem
685
686 This property contains the id of an item that a query should return the
687 descendants of.
688*/
689
690/*!
691 \qmlproperty enum DocumentGalleryModel::scope
692
693 The property contains whether a query should count the direct descendants
694 of the \l rootItem or all descendants.
695*/
696
697/*!
698 \qmlmethod DocumentGalleryModel::reload()
699
700 Re-queries the gallery.
701*/
702
703/*!
704 \qmlmethod DocumentGalleryModel::cancel()
705
706 Cancels an executing query.
707*/
708
709/*!
710 \qmlmethod DocumentGalleryModel::clear()
711
712 Clears the results of a query.
713*/
714
715/*!
716 \qmlproperty int DocumentGalleryModel::count
717
718 This property holds the number of results returned by a query.
719*/
720
721/*!
722 \qmlmethod DocumentGalleryModel::get(int index)
723
724 Returns the result at \a index in a query model.
725
726 \code
727 query.get(0).title
728 \endcode
729*/
730
731/*!
732 \qmlmethod DocumentGalleryModel::property(int index, string property)
733
734 Returns the value of \a property from the result at \a index.
735
736 \code
737 query.getProperty(0, "title")
738 \endcode
739*/
740
741/*!
742 \qmlmethod DocumentGalleryModel::set(int index, jsobject dict)
743
744 Changes the item at \a index in the list model with the values in \a dict.
745 Properties not appearing in \a dict are left unchanged.
746*/
747
748/*!
749 \qmlmethod DocumentGalleryModel::setProperty(int index, string property, variant value)
750
751 Changes the \a property of the result at \a index in a model to \a value.
752
753 \code
754 model.setProperty(0, "rating", 4)
755 \endcode
756*/
757
758QVariant QDeclarativeDocumentGalleryModel::itemType(const QString &type) const
759{
760 return QVariant::fromValue(value: QDeclarativeDocumentGallery::itemTypeFromString(string: type));
761}
762
763#include "moc_qdeclarativegalleryquerymodel.cpp"
764
765QT_END_NAMESPACE_DOCGALLERY
766

source code of qtdocgallery/src/imports/gallery/qdeclarativegalleryquerymodel.cpp