1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL21$
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 The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
20** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22** following information to ensure the GNU Lesser General Public License
23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** As a special exception, The Qt Company gives you certain additional
27** rights. These rights are described in The Qt Company LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** $QT_END_LICENSE$
31**
32****************************************************************************/
33
34#include "qdeclarativecontactmodel_p.h"
35
36#include <QtCore/qfile.h>
37#include <QtCore/qhash.h>
38#include <QtCore/qmap.h>
39#include <QtCore/qpointer.h>
40#include <QtCore/qurl.h>
41#include <QtCore/qmimedatabase.h>
42#include <QtCore/qmimetype.h>
43#include <QtCore/qtemporaryfile.h>
44#include <QtCore/qdir.h>
45
46#include <QtGui/qcolor.h>
47#include <QtGui/qpixmap.h>
48
49#include <QtQml/qqmlinfo.h>
50#include <QtQml/qqmlengine.h>
51
52#include <QtContacts/qcontactdetails.h>
53#include <QtContacts/qcontactmanager.h>
54#include <QtContacts/qcontactmanagerengine.h>
55#include <QtContacts/qcontactdetailfilter.h>
56#include <QtContacts/qcontactidfilter.h>
57#include <QtContacts/qcontactintersectionfilter.h>
58#include <QtContacts/qcontactrequests.h>
59
60#include <QtVersit/qversitreader.h>
61#include <QtVersit/qversitwriter.h>
62#include <QtVersit/qversitcontactimporter.h>
63#include <QtVersit/qversitcontactexporter.h>
64
65QTCONTACTS_USE_NAMESPACE
66QTVERSIT_USE_NAMESPACE
67
68QT_BEGIN_NAMESPACE
69
70/*!
71 \qmltype ContactModel
72 \instantiates QDeclarativeContactModel
73 \brief The ContactModel element provides access to contacts from the contacts store.
74 \ingroup qml-contacts-main
75 \inqmlmodule QtContacts
76
77 This element is part of the \b{QtContacts} module.
78
79 ContactModel provides a model of contacts from the contacts store.
80 The contents of the model can be specified with \l filter, \l sortOrders and \l fetchHint properties.
81 Whether the model is automatically updated when the store or \l contacts changes, can be
82 controlled with \l ContactModel::autoUpdate property.
83
84 There are two ways of accessing the contact data: via model by using views and delegates,
85 or alternatively via \l contacts list property. Of the two, the model access is preferred.
86 Direct list access (i.e. non-model) is not guaranteed to be in order set by \l sortOrder.
87
88 At the moment the model roles provided by ContactModel are display, decoration and \c contact.
89 Through the \c contact role can access any data provided by the Contact element.
90
91 \sa RelationshipModel, Contact, {QContactManager}
92*/
93
94// Helper class to store contact binary data into a temporary file
95//
96// QContactVcard only supports URL values for images. During a vcard import if the contact cotains
97// an avatar, ringtone or any property formated in a binary data, QVersit will use the
98// ContactExporterResourceHandler to store the binary data.
99// The default implementation of QVersitResourceHandler does not store any data and that
100// causes avatar data loss during the import process.
101// This class will store the data into a temporary file and removes the file when the model gets destroyed.
102class ContactExporterResourceHandler : public QVersitResourceHandler
103{
104public:
105 ContactExporterResourceHandler()
106 {
107 }
108
109 ~ContactExporterResourceHandler()
110 {
111 foreach (const QString& fileName, m_files)
112 QFile::remove(fileName);
113
114 m_files.clear();
115 }
116
117 bool saveResource(const QByteArray& contents,
118 const QVersitProperty& property,
119 QString* location)
120 {
121 const QMimeType mt = QMimeDatabase().mimeTypeForData(data: contents);
122 QString extension(QStringLiteral("data"));
123 if (mt.isValid())
124 extension = mt.suffixes()[0];
125
126 // use property.name() to create a new file for each binary property (avatar, ringtone, etc...)
127 QTemporaryFile tmpFile(QString::fromLatin1(str: "%1/%2_XXXXXX.%3")
128 .arg(a: QDir::tempPath())
129 .arg(a: property.name().toLower())
130 .arg(a: extension));
131 tmpFile.setAutoRemove(false);
132 if (tmpFile.open()) {
133 // the location expect a string in url format ex: file:///tmp/filename.png
134 *location = QUrl::fromLocalFile(localfile: tmpFile.fileName()).toString();
135 m_files << *location;
136 tmpFile.write(data: contents);
137 tmpFile.close();
138 return true;
139 }
140 return false;
141 }
142
143 bool loadResource(const QString &location, QByteArray *contents, QString *mimeType)
144 {
145 if (location.isEmpty())
146 return false;
147
148 QFile file(location);
149 if (!file.open(flags: QIODevice::ReadOnly))
150 return false;
151
152 *contents = file.readAll();
153 const QMimeType mt = QMimeDatabase().mimeTypeForData(data: *contents);
154 if (mt.isValid())
155 *mimeType = mt.suffixes()[0];
156
157 return !contents->isEmpty();
158 }
159
160 QStringList m_files;
161};
162
163
164class QDeclarativeContactModelPrivate
165{
166public:
167 QDeclarativeContactModelPrivate()
168 :m_manager(0),
169 m_fetchHint(0),
170 m_filter(0),
171 m_error(QContactManager::NoError),
172 m_autoUpdate(true),
173 m_componentCompleted(false),
174 m_progressiveLoading(true),
175 m_updatePendingFlag(QDeclarativeContactModelPrivate::NonePending)
176 {
177 }
178 ~QDeclarativeContactModelPrivate()
179 {
180 if (m_manager)
181 delete m_manager;
182 }
183
184 enum UpdateTypePending {
185 NonePending = 0x0,
186 UpdatingContactsPending = 0x1,
187 UpdatingCollectionsPending = 0x2
188 };
189
190 QList<QDeclarativeContact*> m_contacts;
191 QMap<QContactId, QDeclarativeContact*> m_contactMap;
192 QMap<QContactId, QDeclarativeContact*> m_contactFetchedMap;
193 QContactManager* m_manager;
194 QDeclarativeContactFetchHint* m_fetchHint;
195 QList<QDeclarativeContactSortOrder*> m_sortOrders;
196 QDeclarativeContactFilter* m_filter;
197
198 QVersitReader m_reader;
199 QVersitWriter m_writer;
200 QStringList m_importProfiles;
201 ContactExporterResourceHandler m_resourceHandler;
202
203 QContactManager::Error m_error;
204
205 bool m_autoUpdate;
206 bool m_componentCompleted;
207 QUrl m_lastExportUrl;
208 QUrl m_lastImportUrl;
209 QAtomicInt m_lastRequestId;
210 QHash<QContactAbstractRequest *, int> m_requestIdHash;
211 QList<QContactFetchRequest*> m_pendingRequests;
212 QList<QContact> m_pendingContacts;
213 QList<QDeclarativeContactCollection*> m_collections;
214 bool m_progressiveLoading;
215 int m_updatePendingFlag;
216};
217
218QDeclarativeContactModel::QDeclarativeContactModel(QObject *parent) :
219 QAbstractListModel(parent),
220 d(new QDeclarativeContactModelPrivate)
221{
222 QHash<int, QByteArray> roleNames;
223 roleNames = QAbstractItemModel::roleNames();
224 roleNames.insert(key: ContactRole, value: "contact");
225 setRoleNames(roleNames);
226
227 connect(asender: this, SIGNAL(managerChanged()), SLOT(doUpdate()));
228 connect(asender: this, SIGNAL(filterChanged()), SLOT(doContactUpdate()));
229 connect(asender: this, SIGNAL(fetchHintChanged()), SLOT(doContactUpdate()));
230 connect(asender: this, SIGNAL(sortOrdersChanged()), SLOT(doContactUpdate()));
231
232 //import vcard
233 connect(sender: &d->m_reader, SIGNAL(stateChanged(QVersitReader::State)), receiver: this, SLOT(startImport(QVersitReader::State)));
234 connect(sender: &d->m_writer, SIGNAL(stateChanged(QVersitWriter::State)), receiver: this, SLOT(contactsExported(QVersitWriter::State)));
235}
236
237QDeclarativeContactModel::~QDeclarativeContactModel()
238{
239}
240
241/*!
242 \qmlproperty string ContactModel::manager
243
244 This property holds the manager uri of the contact backend engine.
245 */
246QString QDeclarativeContactModel::manager() const
247{
248 if (d->m_manager)
249 return d->m_manager->managerName();
250 return QString();
251}
252void QDeclarativeContactModel::setManager(const QString& managerName)
253{
254 if (d->m_manager && (managerName == d->m_manager->managerName() || managerName == d->m_manager->managerUri()))
255 return;
256
257 if (d->m_manager) {
258 cancelUpdate();
259 delete d->m_manager;
260 }
261
262 d->m_manager = new QContactManager(managerName);
263
264 connect(sender: d->m_manager, SIGNAL(dataChanged()), receiver: this, SLOT(doUpdate()));
265 connect(sender: d->m_manager, SIGNAL(contactsAdded(QList<QContactId>)), receiver: this, SLOT(onContactsAdded(QList<QContactId>)));
266 connect(sender: d->m_manager, SIGNAL(contactsRemoved(QList<QContactId>)), receiver: this, SLOT(onContactsRemoved(QList<QContactId>)));
267 connect(sender: d->m_manager, SIGNAL(contactsChanged(QList<QContactId>,QList<QContactDetail::DetailType>)), receiver: this, SLOT(onContactsChanged(QList<QContactId>)));
268 connect(sender: d->m_manager, SIGNAL(collectionsAdded(QList<QContactCollectionId>)), receiver: this, SLOT(fetchCollections()));
269 connect(sender: d->m_manager, SIGNAL(collectionsChanged(QList<QContactCollectionId>)), receiver: this, SLOT(fetchCollections()));
270 connect(sender: d->m_manager, SIGNAL(collectionsRemoved(QList<QContactCollectionId>)), receiver: this, SLOT(fetchCollections()));
271
272
273 if (d->m_error != QContactManager::NoError) {
274 d->m_error = QContactManager::NoError;
275 emit errorChanged();
276 }
277
278 emit managerChanged();
279}
280
281void QDeclarativeContactModel::componentComplete()
282{
283 if (!d->m_manager)
284 setManager(QString());
285
286 d->m_componentCompleted = true;
287
288 if (d->m_autoUpdate)
289 update();
290}
291/*!
292 \qmlproperty bool ContactModel::autoUpdate
293
294 This property indicates whether or not the contact model should be updated automatically, default value is true.
295 */
296void QDeclarativeContactModel::setAutoUpdate(bool autoUpdate)
297{
298 if (autoUpdate == d->m_autoUpdate)
299 return;
300 d->m_autoUpdate = autoUpdate;
301 emit autoUpdateChanged();
302}
303
304bool QDeclarativeContactModel::autoUpdate() const
305{
306 return d->m_autoUpdate;
307}
308
309void QDeclarativeContactModel::update()
310{
311 if (!d->m_componentCompleted || d->m_updatePendingFlag)
312 return;
313 // Disallow possible duplicate request triggering
314 d->m_updatePendingFlag = (QDeclarativeContactModelPrivate::UpdatingContactsPending | QDeclarativeContactModelPrivate::UpdatingCollectionsPending);
315 QMetaObject::invokeMethod(obj: this, member: "fetchCollections", type: Qt::QueuedConnection);
316}
317
318/*!
319 \qmlmethod ContactModel::updateContacts()
320
321 Manually update the contact model contacts.
322
323 \sa ContactModel::update
324 \sa ContactModel::updateCollections
325 \sa ContactModel::autoUpdate
326 */
327void QDeclarativeContactModel::updateContacts()
328{
329 if (!d->m_componentCompleted || d->m_updatePendingFlag)
330 return;
331 // Disallow possible duplicate request triggering
332 d->m_updatePendingFlag = QDeclarativeContactModelPrivate::UpdatingContactsPending;
333 QMetaObject::invokeMethod(obj: this, member: "fetchAgain", type: Qt::QueuedConnection);
334}
335
336/*!
337 \qmlmethod ContactModel::updateCollections()
338
339 Manually update the contact model collections.
340
341 \sa ContactModel::update
342 \sa ContactModel::updateContacts
343 \sa ContactModel::autoUpdate
344 */
345void QDeclarativeContactModel::updateCollections()
346{
347 if (!d->m_componentCompleted || d->m_updatePendingFlag)
348 return;
349 // Disallow possible duplicate request triggering
350 d->m_updatePendingFlag = QDeclarativeContactModelPrivate::UpdatingCollectionsPending;
351 QMetaObject::invokeMethod(obj: this, member: "fetchCollections", type: Qt::QueuedConnection);
352}
353
354/*!
355 \qmlmethod ContactModel::cancelUpdate()
356
357 Cancel the running contact model content update request.
358
359 \sa ContactModel::autoUpdate
360 \sa ContactModel::update
361 */
362void QDeclarativeContactModel::cancelUpdate()
363{
364 foreach (QContactFetchRequest *req, d->m_pendingRequests) {
365 req->cancel();
366 req->deleteLater();
367 }
368 d->m_pendingRequests.clear();;
369 d->m_updatePendingFlag = QDeclarativeContactModelPrivate::NonePending;
370}
371
372void QDeclarativeContactModel::doContactUpdate()
373{
374 if (d->m_autoUpdate)
375 updateContacts();
376}
377
378/*!
379 \qmlproperty string ContactModel::error
380
381 This property holds the latest error code returned by the contact manager.
382
383 This property is read only.
384 */
385QString QDeclarativeContactModel::error() const
386{
387 if (d->m_manager) {
388 switch (d->m_error) {
389 case QContactManager::DoesNotExistError:
390 return QStringLiteral("DoesNotExist");
391 case QContactManager::AlreadyExistsError:
392 return QStringLiteral("AlreadyExists");
393 case QContactManager::InvalidDetailError:
394 return QStringLiteral("InvalidDetail");
395 case QContactManager::InvalidRelationshipError:
396 return QStringLiteral("InvalidRelationship");
397 case QContactManager::LockedError:
398 return QStringLiteral("LockedError");
399 case QContactManager::DetailAccessError:
400 return QStringLiteral("DetailAccessError");
401 case QContactManager::PermissionsError:
402 return QStringLiteral("PermissionsError");
403 case QContactManager::OutOfMemoryError:
404 return QStringLiteral("OutOfMemory");
405 case QContactManager::NotSupportedError:
406 return QStringLiteral("NotSupported");
407 case QContactManager::BadArgumentError:
408 return QStringLiteral("BadArgument");
409 case QContactManager::UnspecifiedError:
410 return QStringLiteral("UnspecifiedError");
411 case QContactManager::VersionMismatchError:
412 return QStringLiteral("VersionMismatch");
413 case QContactManager::LimitReachedError:
414 return QStringLiteral("LimitReached");
415 case QContactManager::InvalidContactTypeError:
416 return QStringLiteral("InvalidContactType");
417 default:
418 break;
419 }
420 }
421 return QStringLiteral("NoError");
422}
423
424
425/*!
426 \qmlproperty list<string> ContactModel::availableManagers
427
428 This property holds the list of available manager names.
429 This property is read only.
430 */
431QStringList QDeclarativeContactModel::availableManagers() const
432{
433 return QContactManager::availableManagers();
434}
435static QString urlToLocalFileName(const QUrl& url)
436{
437 if (!url.isValid()) {
438 return url.toString();
439 } else if (url.scheme() == "qrc") {
440 return url.toString().remove(i: 0, len: 5).prepend(c: ':');
441 } else {
442 return url.toLocalFile();
443 }
444
445}
446
447/*!
448 \qmlproperty enumeration ContactModel::ImportError
449
450 Defines the errors cases for \l ContactModel::importContacts() -function.
451
452 \list
453 \li ContactModel::ImportNoError Completed successfully, no error.
454 \li ContactModel::ImportUnspecifiedError Unspecified error.
455 \li ContactModel::ImportIOError Input/output error.
456 \li ContactModel::ImportOutOfMemoryError Out of memory error.
457 \li ContactModel::ImportNotReadyError Not ready for importing. Only one import operation can be active at a time.
458 \li ContactModel::ImportParseError Error during parsing.
459 \endlist
460*/
461
462/*!
463 \qmlsignal ContactModel::onImportCompleted(ImportError error, URL url, list<string> ids)
464
465 This signal is emitted, when \l ContactModel::importContacts() completes. The success of operation
466 can be seen on \a error which is defined in \l ContactModel::ImportError. \a url indicates the
467 file, which was imported. \a ids contains the imported contacts ids.
468
469 If the operation was successful, contacts are now imported to backend. If \l ContactModel::autoUpdate
470 is enabled, \l ContactModel::modelChanged will be emitted when imported contacts are also visible on
471 \l ContactModel's data model.
472
473 \sa ContactModel::importContacts
474 */
475
476/*!
477 \qmlmethod void ContactModel::importContacts(url url, list<string> profiles)
478
479 Import contacts from a vcard by the given \a url and optional \a profiles.
480 Only one import operation can be active at a time.
481 Supported profiles are:
482 \list
483 \li "Sync" Imports contacts in sync mode, currently, this is the same as passing in an empty list, and is generally what you want.
484 \li "Backup" imports contacts in backup mode, use this mode if the vCard was generated by exporting in backup mode.
485
486 \endlist
487
488 \sa QVersitContactHandlerFactory
489 \sa QVersitContactHandlerFactory::ProfileSync()
490 \sa QVersitContactHandlerFactory::ProfileBackup()
491
492 */
493void QDeclarativeContactModel::importContacts(const QUrl& url, const QStringList& profiles)
494{
495 // Reader is capable of handling only one request at the time.
496 ImportError importError = ImportNotReadyError;
497 if (d->m_reader.state() != QVersitReader::ActiveState) {
498
499 d->m_importProfiles = profiles;
500
501 //TODO: need to allow download vcard from network
502 QFile* file = new QFile(urlToLocalFileName(url));
503 bool ok = file->open(flags: QIODevice::ReadOnly);
504 if (ok) {
505 d->m_reader.setDevice(file);
506 if (d->m_reader.startReading()) {
507 d->m_lastImportUrl = url;
508 return;
509 }
510 importError = QDeclarativeContactModel::ImportError(d->m_reader.error());
511 } else {
512 importError = ImportIOError;
513 }
514 }
515 emit importCompleted(error: importError, url, ids: QStringList());
516}
517
518/*!
519 \qmlmethod void ContactModel::exportContacts(url url, list<string> profiles, list<variant> declarativeContacts)
520
521 Export all contacts of this model into a vcard file to the given \a url by optional \a profiles.
522 The optional \a declarativeContacts list can be used to export an arbitrary list of QDeclarativeContact objects
523 not necessarily belonging to the data set of this model.
524 At the moment only the local file url is supported in export method.
525 Also, only one export operation can be active at a time.
526 Supported profiles are:
527 \list
528 \li "Sync" exports contacts in sync mode, currently, this is the same as passing in an empty list, and is generally what you want.
529 \li "Backup" exports contacts in backup mode, this will add non-standard properties to the generated vCard
530 to try to save every detail of the contacts. Only use this if the vCard is going to be imported using the backup profile.
531#include "moc_qdeclarativecontactmodel_p.cpp"
532 \endlist
533
534 \sa QVersitContactHandlerFactory
535 \sa QVersitContactHandlerFactory::ProfileSync()
536 \sa QVersitContactHandlerFactory::ProfileBackup()
537 */
538void QDeclarativeContactModel::exportContacts(const QUrl& url, const QStringList& profiles, const QVariantList &declarativeContacts)
539{
540 // Writer is capable of handling only one request at the time.
541 ExportError exportError = ExportNotReadyError;
542 if (d->m_writer.state() != QVersitWriter::ActiveState) {
543 QString profile = profiles.isEmpty()? QString() : profiles.at(i: 0);
544 //only one profile string supported now.
545 QVersitContactExporter exporter(profile);
546 exporter.setResourceHandler(&d->m_resourceHandler);
547
548 QList<QContact> contacts;
549 if (declarativeContacts.isEmpty()) {
550 foreach (QDeclarativeContact* dc, d->m_contacts) {
551 contacts.append(t: dc->contact());
552 }
553
554 } else {
555 foreach (const QVariant &contactVariant, declarativeContacts) {
556 QObject *rawObject = contactVariant.value<QObject*>();
557 QDeclarativeContact *dc = qobject_cast<QDeclarativeContact*>(object: rawObject);
558 if (dc) {
559 contacts.append(t: dc->contact());
560 }
561 }
562 }
563
564 exporter.exportContacts(contacts, versitType: QVersitDocument::VCard30Type);
565 QList<QVersitDocument> documents = exporter.documents();
566 QFile* file = new QFile(urlToLocalFileName(url));
567 bool ok = file->open(flags: QIODevice::WriteOnly);
568 if (ok) {
569 d->m_writer.setDevice(file);
570 if (d->m_writer.startWriting(input: documents)) {
571 d->m_lastExportUrl = url;
572 return;
573 }
574 exportError = QDeclarativeContactModel::ExportError(d->m_writer.error());
575 } else {
576 exportError = ExportIOError;
577 }
578 }
579 emit exportCompleted(error: exportError, url);
580}
581
582void QDeclarativeContactModel::contactsExported(QVersitWriter::State state)
583{
584 if (state == QVersitWriter::FinishedState || state == QVersitWriter::CanceledState) {
585 delete d->m_writer.device();
586 d->m_writer.setDevice(0);
587 emit exportCompleted(error: QDeclarativeContactModel::ExportError(d->m_writer.error()), url: d->m_lastExportUrl);
588 }
589}
590
591void QDeclarativeContactModel::onFetchedContactDestroyed(QObject *obj)
592{
593 QContactId id = d->m_contactFetchedMap.key(avalue: static_cast<QDeclarativeContact*>(obj));
594 if (!id.isNull())
595 d->m_contactFetchedMap.remove(key: id);
596}
597
598int QDeclarativeContactModel::rowCount(const QModelIndex &parent) const
599{
600 Q_UNUSED(parent);
601 return d->m_contacts.count();
602}
603
604
605
606/*!
607 \qmlproperty Filter ContactModel::filter
608
609 This property holds the filter instance used by the contact model.
610
611 \sa Filter
612 */
613QDeclarativeContactFilter* QDeclarativeContactModel::filter() const
614{
615 return d->m_filter;
616}
617
618void QDeclarativeContactModel::setFilter(QDeclarativeContactFilter* filter)
619{
620 if (d->m_filter != filter) {
621 if (d->m_filter)
622 disconnect(sender: d->m_filter, SIGNAL(filterChanged()), receiver: this, SIGNAL(filterChanged()));
623 d->m_filter = filter;
624 if (d->m_filter)
625 connect(asender: d->m_filter, SIGNAL(filterChanged()), SIGNAL(filterChanged()), atype: Qt::UniqueConnection);
626 emit filterChanged();
627 }
628}
629
630/*!
631 \qmlproperty FetchHint ContactModel::fetchHint
632
633 This property holds the fetch hint instance used by the contact model.
634
635 \sa FetchHint
636 */
637QDeclarativeContactFetchHint* QDeclarativeContactModel::fetchHint() const
638{
639 return d->m_fetchHint;
640}
641void QDeclarativeContactModel::setFetchHint(QDeclarativeContactFetchHint* fetchHint)
642{
643 if (d->m_fetchHint != fetchHint) {
644 if (d->m_fetchHint)
645 disconnect(sender: d->m_fetchHint, SIGNAL(fetchHintChanged()), receiver: this, SIGNAL(fetchHintChanged()));
646 d->m_fetchHint = fetchHint;
647 if (d->m_fetchHint)
648 connect(asender: d->m_fetchHint, SIGNAL(fetchHintChanged()), SIGNAL(fetchHintChanged()), atype: Qt::UniqueConnection);
649 emit fetchHintChanged();
650 }
651}
652
653/*!
654 \qmlproperty list<Contact> ContactModel::contacts
655
656 This property holds the list of contacts.
657
658 \sa Contact
659 */
660QQmlListProperty<QDeclarativeContact> QDeclarativeContactModel::contacts()
661{
662 return QQmlListProperty<QDeclarativeContact>(this,
663 0,
664 contacts_append,
665 contacts_count,
666 contacts_at,
667 contacts_clear);
668}
669
670
671
672void QDeclarativeContactModel::contacts_append(QQmlListProperty<QDeclarativeContact>* prop, QDeclarativeContact* contact)
673{
674 Q_UNUSED(prop);
675 Q_UNUSED(contact);
676 qWarning() << Q_FUNC_INFO << "appending contacts is not currently supported";
677}
678
679int QDeclarativeContactModel::contacts_count(QQmlListProperty<QDeclarativeContact>* prop)
680{
681 return static_cast<QDeclarativeContactModel*>(prop->object)->d->m_contacts.count();
682}
683
684QDeclarativeContact* QDeclarativeContactModel::contacts_at(QQmlListProperty<QDeclarativeContact>* prop, int index)
685{
686 return static_cast<QDeclarativeContactModel*>(prop->object)->d->m_contacts.at(i: index);
687}
688
689void QDeclarativeContactModel::contacts_clear(QQmlListProperty<QDeclarativeContact>* prop)
690{
691 QDeclarativeContactModel* model = static_cast<QDeclarativeContactModel*>(prop->object);
692 model->clearContacts();
693 emit model->contactsChanged();
694}
695
696
697/*!
698 \qmlproperty list<SortOrder> ContactModel::sortOrders
699
700 This property holds a list of sort orders used by the contacts model.
701 \sa SortOrder
702 */
703QQmlListProperty<QDeclarativeContactSortOrder> QDeclarativeContactModel::sortOrders()
704{
705 return QQmlListProperty<QDeclarativeContactSortOrder>(this,
706 0,
707 sortOrder_append,
708 sortOrder_count,
709 sortOrder_at,
710 sortOrder_clear);
711}
712
713void QDeclarativeContactModel::startImport(QVersitReader::State state)
714{
715 if (state == QVersitReader::FinishedState || state == QVersitReader::CanceledState) {
716 QVersitContactImporter importer(d->m_importProfiles);
717 importer.setResourceHandler(&d->m_resourceHandler);
718 importer.importDocuments(documents: d->m_reader.results());
719 QList<QContact> contacts = importer.contacts();
720
721 delete d->m_reader.device();
722 d->m_reader.setDevice(0);
723
724 QStringList ids;
725
726 if (d->m_manager) {
727 if (!d->m_manager->saveContacts(contacts: &contacts)) {
728 if (d->m_error != d->m_manager->error()) {
729 d->m_error = d->m_manager->error();
730 emit errorChanged();
731 }
732 } else {
733 foreach (const QContact &c, contacts) {
734 ids << c.id().toString();
735 }
736 }
737 }
738
739 emit importCompleted(error: QDeclarativeContactModel::ImportError(d->m_reader.error()), url: d->m_lastImportUrl, ids);
740 }
741}
742
743/*!
744 \qmlsignal ContactModel::contactsFetched(int requestId, list<Contact> fetchedContacts)
745
746 This signal is emitted, when a contact fetch request is finished.
747
748 \sa ContactModel::fetchContacts
749 */
750
751/*!
752 \qmlmethod int ContactModel::fetchContacts(list<string> contactIds)
753
754 Starts a request to fetch contacts by the given \a contactIds, and returns the unique ID of this request.
755 -1 is returned if the request can't be started.
756
757 Note that the contacts fetched won't be added to the model, but can be accessed through the contactsFetched
758 signal handler.
759
760 \sa ContactModel::contactsFetched
761 */
762int QDeclarativeContactModel::fetchContacts(const QStringList &contactIds)
763{
764 if (contactIds.isEmpty())
765 return -1;
766
767 QContactFetchByIdRequest *fetchRequest = new QContactFetchByIdRequest(this);
768 connect(sender: fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)),
769 receiver: this, SLOT(onFetchContactsRequestStateChanged(QContactAbstractRequest::State)));
770 fetchRequest->setManager(d->m_manager);
771
772 QList<QContactId> ids;
773 foreach (const QString &contactId, contactIds)
774 ids.append(t: QContactId::fromString(idString: contactId));
775 fetchRequest->setIds(ids);
776 int requestId = d->m_lastRequestId.fetchAndAddOrdered(valueToAdd: 1);
777 d->m_requestIdHash.insert(key: fetchRequest, value: requestId);
778 if (fetchRequest->start()) {
779 return requestId;
780 } else {
781 d->m_requestIdHash.remove(key: fetchRequest);
782 return -1;
783 }
784}
785
786/*!
787 \qmlmethod ContactModel::removeCollection(string collectionId)
788 Removes asynchronously the contact collection with the given \a collectionId from the backend.
789 */
790void QDeclarativeContactModel::removeCollection(const QString &collectionId)
791{
792 QContactCollectionRemoveRequest* req = new QContactCollectionRemoveRequest(this);
793 req->setManager(d->m_manager);
794 req->setCollectionId(QContactCollectionId::fromString(idString: collectionId));
795
796 connect(sender: req, SIGNAL(stateChanged(QContactAbstractRequest::State)), receiver: this, SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
797
798 req->start();
799}
800
801/*!
802 \qmlmethod OContactModel::saveCollection(Collection collection)
803
804 Saves asynchronously the given \a collection into the contact backend.
805 */
806void QDeclarativeContactModel::saveCollection(QDeclarativeContactCollection *declColl)
807{
808 if (declColl) {
809 QContactCollection collection = declColl->collection();
810 QContactCollectionSaveRequest* req = new QContactCollectionSaveRequest(this);
811 req->setManager(d->m_manager);
812 req->setCollection(collection);
813
814 if (declColl->collection().id().isNull()) {
815 // if the collection id is empty this means that this is a new collection
816 // we need to keep trace of this declarative collection to update with the
817 // new Id as soon as this request finish
818 QPointer<QDeclarativeContactCollection> pCollection = declColl;
819 req->setProperty(name: "DeclarativeCollection", value: QVariant::fromValue(value: pCollection));
820 }
821
822 connect(sender: req, SIGNAL(stateChanged(QContactAbstractRequest::State)), receiver: this, SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
823 req->start();
824 }
825}
826
827/*!
828 \qmlmethod OContactModel::fetchCollections()
829 Fetch asynchronously a list of contact collections from the contact backend.
830*/
831void QDeclarativeContactModel::fetchCollections()
832{
833 // fetchCollections() is used for both direct calls and
834 // signals from model. For signal from model, check also the
835 // autoupdate-flag.
836 if (sender() == d->m_manager && !d->m_autoUpdate) {
837 return;
838 }
839
840 QContactCollectionFetchRequest* req = new QContactCollectionFetchRequest(this);
841 connect(sender: req,SIGNAL(stateChanged(QContactAbstractRequest::State)), receiver: this, SLOT(collectionsFetched()));
842 req->setManager(d->m_manager);
843 req->start();
844}
845
846/*!
847 \internal
848 */
849void QDeclarativeContactModel::onFetchContactsRequestStateChanged(QContactAbstractRequest::State state)
850{
851 if (state != QContactAbstractRequest::FinishedState)
852 return;
853
854 QContactFetchByIdRequest *request = qobject_cast<QContactFetchByIdRequest *>(object: sender());
855 Q_ASSERT(request);
856
857 checkError(request);
858
859 const int requestId = d->m_requestIdHash.value(key: request, defaultValue: -1);
860 if (requestId == -1)
861 qWarning() << Q_FUNC_INFO << "transaction not found from the request hash";
862 else
863 d->m_requestIdHash.remove(key: request);
864 QVariantList list;
865 if (request->error() == QContactManager::NoError) {
866 QList<QContact> contacts(request->contacts());
867 foreach (const QContact &contact, contacts) {
868 // if the contact was already fetched update the contact
869 QDeclarativeContact *declarativeContact = d->m_contactFetchedMap.value(akey: contact.id(), adefaultValue: 0);
870 if (!declarativeContact) {
871 declarativeContact = new QDeclarativeContact(this);
872 // Transfer the ownership to QML
873 // The model will destroy the contact if it get removed from the backend, otherwise the QML side need to destroy it.
874 QQmlEngine::setObjectOwnership(declarativeContact, QQmlEngine::JavaScriptOwnership);
875
876 // keep track of contact destruction to remove it from the list if QML destroys it
877 connect(asender: declarativeContact, SIGNAL(destroyed(QObject*)), SLOT(onFetchedContactDestroyed(QObject*)));
878
879 // we need keep track of the contact to update it if the contact get update on the backend. or destroy it
880 // if the contact get removed from the backend
881 d->m_contactFetchedMap[contact.id()] = declarativeContact;
882 }
883 declarativeContact->setContact(contact);
884 list.append(t: QVariant::fromValue(value: declarativeContact));
885 }
886 }
887 emit contactsFetched(requestId, fetchedContacts: list);
888 request->deleteLater();
889}
890
891/*!
892 \internal
893 */
894void QDeclarativeContactModel::collectionsFetched()
895{
896 QContactCollectionFetchRequest* req = qobject_cast<QContactCollectionFetchRequest*>(object: QObject::sender());
897 Q_ASSERT(req);
898 if (req->isFinished() && QContactManager::NoError == req->error()) {
899 d->m_updatePendingFlag &= ~QDeclarativeContactModelPrivate::UpdatingCollectionsPending;
900 // prepare tables
901 QHash<QString, const QContactCollection*> collections;
902 foreach (const QContactCollection& collection, req->collections()) {
903 collections.insert(key: collection.id().toString(), value: &collection);
904 }
905 QHash<QString, QDeclarativeContactCollection*> declCollections;
906 foreach (QDeclarativeContactCollection* declCollection, d->m_collections) {
907 declCollections.insert(key: declCollection->collection().id().toString(), value: declCollection);
908 }
909 // go tables through
910 QHashIterator<QString, const QContactCollection*> collIterator(collections);
911 while (collIterator.hasNext()) {
912 collIterator.next();
913 if (declCollections.contains(key: collIterator.key())) {
914 // collection on both sides, update the declarative collection
915 declCollections.value(key: collIterator.key())->setCollection(*collections.value(key: collIterator.key()));
916 } else {
917 // new collection, add it to declarative collection list
918 QDeclarativeContactCollection* declCollection = new QDeclarativeContactCollection(this);
919 declCollection->setCollection(*collections.value(key: collIterator.key()));
920 d->m_collections.append(t: declCollection);
921 }
922 }
923 QHashIterator<QString, QDeclarativeContactCollection*> declCollIterator(declCollections);
924 while (declCollIterator.hasNext()) {
925 declCollIterator.next();
926 if (!collections.contains(key: declCollIterator.key())) {
927 // collection deleted on the backend side, delete from declarative collection list
928 QDeclarativeContactCollection* toBeDeletedColl = declCollections.value(key: declCollIterator.key());
929 d->m_collections.removeOne(t: toBeDeletedColl);
930 toBeDeletedColl->deleteLater();
931 }
932 }
933 emit collectionsChanged();
934 if (d->m_updatePendingFlag & QDeclarativeContactModelPrivate::UpdatingContactsPending)
935 QMetaObject::invokeMethod(obj: this, member: "fetchAgain", type: Qt::QueuedConnection);
936 req->deleteLater();
937 }
938 checkError(request: req);
939}
940
941void QDeclarativeContactModel::clearContacts()
942{
943 qDeleteAll(c: d->m_contacts);
944 d->m_contacts.clear();
945 d->m_contactMap.clear();
946 qDeleteAll(c: d->m_contactFetchedMap.values());
947 d->m_contactFetchedMap.clear();
948}
949
950void QDeclarativeContactModel::fetchAgain()
951{
952 QList<QContactSortOrder> sortOrders;
953 foreach (QDeclarativeContactSortOrder* so, d->m_sortOrders) {
954 sortOrders.append(t: so->sortOrder());
955 }
956 QContactFetchRequest* fetchRequest = new QContactFetchRequest(this);
957
958 fetchRequest->setManager(d->m_manager);
959 fetchRequest->setSorting(sortOrders);
960
961 if (d->m_filter){
962 fetchRequest->setFilter(d->m_filter->filter());
963 } else {
964 fetchRequest->setFilter(QContactFilter());
965 }
966
967 fetchRequest->setFetchHint(d->m_fetchHint ? d->m_fetchHint->fetchHint() : QContactFetchHint());
968
969 connect(sender: fetchRequest, SIGNAL(resultsAvailable()), receiver: this, SLOT(requestUpdated()));
970 connect(sender: fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)),
971 receiver: this, SLOT(fetchRequestStateChanged(QContactAbstractRequest::State)));
972
973 // cancel all previous requests
974 foreach (QContactFetchRequest *req, d->m_pendingRequests) {
975 req->cancel();
976 req->deleteLater();
977 }
978
979 d->m_pendingContacts.clear();
980 d->m_pendingRequests.clear();
981 d->m_pendingRequests.append(t: fetchRequest);
982
983 // if we have no contacts yet, we can display results as soon as they arrive
984 // but if we are updating the model after a sort or filter change, we have to
985 // wait for all contacts before processing the update
986 d->m_progressiveLoading = d->m_contacts.isEmpty();
987
988 fetchRequest->start();
989}
990
991void QDeclarativeContactModel::requestUpdated()
992{
993
994 QContactFetchRequest* req = qobject_cast<QContactFetchRequest*>(object: QObject::sender());
995 Q_ASSERT(req);
996 if (req) {
997 QList<QContact> contacts = req->contacts();
998
999 // if we are starting from scratch, we can show contact results as they arrive
1000 if (d->m_progressiveLoading) {
1001 QList<QDeclarativeContact*> dcs;
1002 foreach (const QContact &c, contacts) {
1003 if (d->m_contactMap.contains(key: c.id())) {
1004 QDeclarativeContact* dc = d->m_contactMap.value(akey: c.id());
1005 dc->setContact(c);
1006 } else {
1007 QDeclarativeContact* dc = new QDeclarativeContact(this);
1008 if (dc) {
1009 d->m_contactMap.insert(key: c.id(), value: dc);
1010 dc->setContact(c);
1011 dcs.append(t: dc);
1012 }
1013 }
1014 }
1015
1016 if (dcs.count() > 0) {
1017 beginInsertRows(parent: QModelIndex(), first: d->m_contacts.count(), last: d->m_contacts.count() + dcs.count() - 1);
1018 // At this point we need to relay on the backend and assume that the partial results are following the fetch sorting property
1019 d->m_contacts += dcs;
1020 endInsertRows();
1021
1022 emit contactsChanged();
1023 }
1024 } else {
1025 d->m_pendingContacts << contacts;
1026 }
1027
1028 checkError(request: req);
1029 }
1030}
1031
1032void QDeclarativeContactModel::fetchRequestStateChanged(QContactAbstractRequest::State newState)
1033{
1034 if (newState != QContactAbstractRequest::FinishedState)
1035 return;
1036
1037 d->m_updatePendingFlag &= ~QDeclarativeContactModelPrivate::UpdatingContactsPending;
1038 QContactFetchRequest* req = qobject_cast<QContactFetchRequest*>(object: QObject::sender());
1039 Q_ASSERT(req);
1040 if (req) {
1041 // if we were not processing contacts as soon as they arrive, we need to process them here.
1042 if (!d->m_progressiveLoading) {
1043 // start by removing the contacts that don't belong to this result set anymore
1044 for (int i = d->m_contacts.count()-1; i >= 0; --i) {
1045 QDeclarativeContact *contact = d->m_contacts[i];
1046 if (!d->m_pendingContacts.contains(t: contact->contact())) {
1047 beginRemoveRows(parent: QModelIndex(), first: i, last: i);
1048 d->m_contacts.removeAt(i);
1049 d->m_contactMap.remove(key: contact->contact().id());
1050 endRemoveRows();
1051 }
1052 }
1053
1054 // now insert new contacts and move existing ones to their final positions
1055 int count = d->m_pendingContacts.count();
1056 for (int i = 0; i < count; ++i) {
1057 QContact c = d->m_pendingContacts[i];
1058 if (!d->m_contactMap.contains(key: c.id())) {
1059 QDeclarativeContact* dc = new QDeclarativeContact(this);
1060 dc->setContact(c);
1061 beginInsertRows(parent: QModelIndex(), first: i, last: i);
1062 d->m_contacts.insert(i, t: dc);
1063 d->m_contactMap.insert(key: c.id(),value: dc);
1064 endInsertRows();
1065 } else {
1066 QDeclarativeContact *contact = d->m_contactMap[c.id()];
1067
1068 // If there are duplicates in the pending contacts list, then the current index
1069 // can be outside this contact lists range and we need to adjust it to avoid crashing.
1070 const int oldIdx = d->m_contacts.indexOf(t: contact);
1071 const int newIdx = i < d->m_contacts.size() ? i : d->m_contacts.size() - 1;
1072 if (oldIdx != newIdx) {
1073 beginMoveRows(sourceParent: QModelIndex(), sourceFirst: oldIdx, sourceLast: oldIdx, destinationParent: QModelIndex(), destinationRow: newIdx);
1074 d->m_contacts.move(from: oldIdx, to: newIdx);
1075 endMoveRows();
1076 }
1077 }
1078 }
1079 emit contactsChanged();
1080 }
1081
1082 // and now clear the pending contact list as the model is up-to-date
1083 d->m_pendingContacts.clear();
1084 d->m_pendingRequests.removeOne(t: req);
1085 req->deleteLater();
1086 }
1087}
1088
1089/*!
1090 \internal
1091 */
1092void QDeclarativeContactModel::doUpdate()
1093{
1094 if (d->m_autoUpdate)
1095 update();
1096}
1097
1098/*!
1099 \qmlmethod ContactModel::saveContact(Contact contact)
1100
1101 Save the given \a contact into the contacts backend.
1102 Once saved successfully, the dirty flags of this contact will be reset.
1103
1104 \sa Contact::modified
1105 */
1106void QDeclarativeContactModel::saveContact(QDeclarativeContact* dc)
1107{
1108 if (dc) {
1109 QContactSaveRequest* req = new QContactSaveRequest(this);
1110 req->setManager(d->m_manager);
1111 req->setContact(dc->contact());
1112 if (dc->contact().id().isNull()) {
1113 // if the contact id is empty this means that this contact is a new contact
1114 // we need to keep trace of this declarative contact to update with the
1115 // new Id as soon as this request finish
1116 QPointer<QDeclarativeContact> pContact = dc;
1117 req->setProperty(name: "DeclarativeContact", value: QVariant::fromValue(value: pContact));
1118 }
1119
1120 connect(sender: req,SIGNAL(stateChanged(QContactAbstractRequest::State)), receiver: this, SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
1121 req->start();
1122 }
1123}
1124
1125void QDeclarativeContactModel::onRequestStateChanged(QContactAbstractRequest::State newState)
1126{
1127 if (newState != QContactAbstractRequest::FinishedState) {
1128 return;
1129 }
1130
1131 QContactAbstractRequest *request = qobject_cast<QContactAbstractRequest *>(object: sender());
1132 Q_ASSERT(request);
1133
1134 if (request->error() == QContactManager::NoError) {
1135 switch (request->type()) {
1136 case QContactAbstractRequest::ContactSaveRequest:
1137 {
1138 QVariant vContact = request->property(name: "DeclarativeContact");
1139 if (vContact.isValid()) {
1140 QPointer<QDeclarativeContact> pContact = vContact.value<QPointer<QDeclarativeContact> >();
1141 // Update contact info.
1142 // this is necessary to make sure that the declarative contact get the new contact ID otherwise
1143 // the contact Id will be empty
1144 QList<QContact> contacts = qobject_cast<QContactSaveRequest*>(object: request)->contacts();
1145 if (pContact && contacts.length() == 1) {
1146 pContact->setContact(contacts[0]);
1147 }
1148 }
1149 break;
1150 }
1151 case QContactAbstractRequest::CollectionSaveRequest:
1152 {
1153 QVariant vCollection = request->property(name: "DeclarativeCollection");
1154 if (vCollection.isValid()) {
1155 QPointer<QDeclarativeContactCollection> pCollection = vCollection.value<QPointer<QDeclarativeContactCollection> >();
1156 // Update collection info.
1157 // this is necessary to make sure that the declarative collection get the new collection ID otherwise
1158 // the collection Id will be empty
1159 QList<QContactCollection> collections = qobject_cast<QContactCollectionSaveRequest*>(object: request)->collections();
1160 if (pCollection && collections.length() == 1) {
1161 pCollection->setCollection(collections[0]);
1162 }
1163 }
1164 break;
1165 }
1166 default:
1167 break;
1168 }
1169 }
1170 checkError(request);
1171 request->deleteLater();
1172}
1173
1174void QDeclarativeContactModel::checkError(const QContactAbstractRequest *request)
1175{
1176 if (request) {
1177 updateError(error: request->error());
1178 }
1179}
1180
1181void QDeclarativeContactModel::updateError(QContactManager::Error error)
1182{
1183 if (d->m_error != error) {
1184 d->m_error = error;
1185 emit errorChanged();
1186 }
1187}
1188
1189void QDeclarativeContactModel::onContactsAdded(const QList<QContactId>& ids)
1190{
1191 if (d->m_autoUpdate && !ids.isEmpty()) {
1192 QContactFetchRequest *fetchRequest = createContactFetchRequest(ids);
1193 connect(sender: fetchRequest,SIGNAL(stateChanged(QContactAbstractRequest::State)),
1194 receiver: this, SLOT(onContactsAddedFetchRequestStateChanged(QContactAbstractRequest::State)));
1195 fetchRequest->start();
1196 }
1197}
1198
1199/*!
1200 \qmlmethod ContactModel::removeContact(string contactId)
1201 Remove the contact from the contacts store by given \a contactId.
1202 After removing a contact it is not possible to save it again.
1203 \sa Contact::contactId
1204 */
1205void QDeclarativeContactModel::removeContact(QString id)
1206{
1207 QList<QString> ids;
1208 ids << id;
1209 removeContacts(ids);
1210}
1211
1212/*!
1213 \qmlmethod ContactModel::removeContacts(list<string> contactIds)
1214 Remove the list of contacts from the contacts store by given \a contactIds.
1215 \sa Contact::contactId
1216 */
1217
1218void QDeclarativeContactModel::removeContacts(const QStringList &ids)
1219{
1220 QContactRemoveRequest* req = new QContactRemoveRequest(this);
1221 QList<QContactId> contactIdsAsList;
1222 req->setManager(d->m_manager);
1223
1224 foreach (const QString& id, ids) {
1225 QContactId contactId = QContactId::fromString(idString: id);
1226 if (!contactId.isNull())
1227 contactIdsAsList.append(t: contactId);
1228 }
1229 req->setContactIds(contactIdsAsList);
1230
1231 connect(sender: req,SIGNAL(stateChanged(QContactAbstractRequest::State)), receiver: this, SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
1232
1233 req->start();
1234}
1235
1236
1237void QDeclarativeContactModel::onContactsRemoved(const QList<QContactId> &ids)
1238{
1239 if (!d->m_autoUpdate)
1240 return;
1241
1242 bool emitSignal = false;
1243 foreach (const QContactId &id, ids) {
1244 // delete the contact from fetched map if necessary
1245 QDeclarativeContact* contact = d->m_contactFetchedMap.take(key: id);
1246 if (contact)
1247 contact->deleteLater();
1248
1249 if (d->m_contactMap.contains(key: id)) {
1250 int row = 0;
1251 //TODO:need a fast lookup
1252 for (; row < d->m_contacts.count(); row++) {
1253 if (d->m_contacts.at(i: row)->contactId() == id.toString())
1254 break;
1255 }
1256
1257 if (row < d->m_contacts.count()) {
1258 beginRemoveRows(parent: QModelIndex(), first: row, last: row);
1259 contact = d->m_contacts.takeAt(i: row);
1260 contact->deleteLater();
1261 d->m_contactMap.remove(key: id);
1262 endRemoveRows();
1263 emitSignal = true;
1264 }
1265 }
1266 }
1267 if (emitSignal)
1268 emit contactsChanged();
1269}
1270
1271void QDeclarativeContactModel::onContactsChanged(const QList<QContactId> &ids)
1272{
1273 if (d->m_autoUpdate && !ids.isEmpty()) {
1274 QContactFetchRequest *fetchRequest = createContactFetchRequest(ids);
1275 connect(sender: fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)),
1276 receiver: this, SLOT(onContactsChangedFetchRequestStateChanged(QContactAbstractRequest::State)));
1277 fetchRequest->start();
1278 }
1279
1280 // If any contact in the fetchedList has changed we need to update it.
1281 // We need a different query because feched contacts could not be part of the model.
1282 //
1283 // For example: if the model contains a filter
1284 if (!ids.isEmpty()) {
1285 QStringList pendingFetch;
1286 foreach (const QContactId &id, ids) {
1287 QDeclarativeContact* dc = d->m_contactFetchedMap.value(akey: id);
1288 if (dc)
1289 pendingFetch << dc->contactId();
1290 }
1291 if (!pendingFetch.isEmpty())
1292 fetchContacts(contactIds: pendingFetch);
1293 }
1294}
1295
1296QContactFetchRequest *QDeclarativeContactModel::createContactFetchRequest(const QList<QContactId> &ids)
1297{
1298 QContactFetchRequest *fetchRequest = new QContactFetchRequest(this);
1299 fetchRequest->setManager(d->m_manager);
1300 fetchRequest->setFetchHint(d->m_fetchHint ? d->m_fetchHint->fetchHint() : QContactFetchHint());
1301
1302 QContactIdFilter idFilter;
1303 idFilter.setIds(ids);
1304 if (d->m_filter) {
1305 QContactIntersectionFilter filter;
1306 filter.append(filter: idFilter); // result handling assumes that id filter is the first filter
1307 filter.append(filter: d->m_filter->filter());
1308 fetchRequest->setFilter(filter);
1309 } else
1310 fetchRequest->setFilter(idFilter);
1311 return fetchRequest;
1312}
1313
1314QVariant QDeclarativeContactModel::data(const QModelIndex &index, int role) const
1315{
1316 //Check if QList itme's index is valid before access it, index should be between 0 and count - 1
1317 if (index.row() < 0 || index.row() >= d->m_contacts.count()) {
1318 return QVariant();
1319 }
1320
1321 QDeclarativeContact* dc = d->m_contacts.value(i: index.row());
1322 Q_ASSERT(dc);
1323 QContact c = dc->contact();
1324
1325 switch(role) {
1326 case Qt::DisplayRole:
1327 return c.detail(type: QContactDetail::TypeDisplayLabel).value(field: QContactDisplayLabel::FieldLabel);
1328 case Qt::DecorationRole:
1329 return QPixmap();
1330 case ContactRole:
1331 return QVariant::fromValue(value: dc);
1332 }
1333 return QVariant();
1334}
1335
1336
1337void QDeclarativeContactModel::sortOrder_append(QQmlListProperty<QDeclarativeContactSortOrder> *p, QDeclarativeContactSortOrder *sortOrder)
1338{
1339 QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(object: p->object);
1340 if (model && sortOrder) {
1341 QObject::connect(sender: sortOrder, SIGNAL(sortOrderChanged()), receiver: model, SIGNAL(sortOrdersChanged()));
1342 model->d->m_sortOrders.append(t: sortOrder);
1343 emit model->sortOrdersChanged();
1344 }
1345}
1346
1347int QDeclarativeContactModel::sortOrder_count(QQmlListProperty<QDeclarativeContactSortOrder> *p)
1348{
1349 QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(object: p->object);
1350 if (model)
1351 return model->d->m_sortOrders.size();
1352 return 0;
1353}
1354QDeclarativeContactSortOrder * QDeclarativeContactModel::sortOrder_at(QQmlListProperty<QDeclarativeContactSortOrder> *p, int idx)
1355{
1356 QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(object: p->object);
1357
1358 QDeclarativeContactSortOrder* sortOrder = 0;
1359 if (model) {
1360 int i = 0;
1361 foreach (QDeclarativeContactSortOrder* s, model->d->m_sortOrders) {
1362 if (i == idx) {
1363 sortOrder = s;
1364 break;
1365 } else {
1366 i++;
1367 }
1368 }
1369 }
1370 return sortOrder;
1371}
1372void QDeclarativeContactModel::sortOrder_clear(QQmlListProperty<QDeclarativeContactSortOrder> *p)
1373{
1374 QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(object: p->object);
1375
1376 if (model) {
1377 model->d->m_sortOrders.clear();
1378 emit model->sortOrdersChanged();
1379 }
1380}
1381
1382/*!
1383 \qmlproperty list<Collection> OContactModel::collections
1384
1385 This property holds a list of collections in the contact model.
1386
1387 \sa Collection
1388 */
1389QQmlListProperty<QDeclarativeContactCollection> QDeclarativeContactModel::collections()
1390{
1391 return QQmlListProperty<QDeclarativeContactCollection>(this, 0, collection_count, collection_at);
1392}
1393
1394int QDeclarativeContactModel::collection_count(QQmlListProperty<QDeclarativeContactCollection> *p)
1395{
1396 QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(object: p->object);
1397 return model ? model->d->m_collections.count() : 0;
1398}
1399
1400QDeclarativeContactCollection *QDeclarativeContactModel::collection_at(QQmlListProperty<QDeclarativeContactCollection> *p, int idx)
1401{
1402 QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(object: p->object);
1403 QDeclarativeContactCollection* collection = 0;
1404 if (model) {
1405 if (!model->d->m_collections.isEmpty() && idx >= 0 && idx < model->d->m_collections.count())
1406 collection = model->d->m_collections.at(i: idx);
1407 }
1408 return collection;
1409}
1410/*!
1411 \internal
1412
1413 It's invoked by the fetch request from onContactsAdded().
1414 */
1415void QDeclarativeContactModel::onContactsAddedFetchRequestStateChanged(QContactAbstractRequest::State state)
1416{
1417
1418 if (state != QContactAbstractRequest::FinishedState)
1419 return;
1420 QContactFetchRequest *request = qobject_cast<QContactFetchRequest *>(object: sender());
1421 Q_ASSERT(request);
1422
1423 checkError(request);
1424
1425 if (request->error() == QContactManager::NoError) {
1426 QList<QContact> fetchedContacts(request->contacts());
1427 bool contactsAdded = false;
1428 foreach (const QContact &c,fetchedContacts) {
1429 if (d->m_contactMap.contains(key: c.id())) {
1430 qWarning() <<Q_FUNC_INFO <<"contact to be added already exists in the model";
1431 continue;
1432 }
1433 QDeclarativeContact* dc = new QDeclarativeContact(this);
1434 dc->setContact(c);
1435 int index = contactIndex(contact: dc);
1436 beginInsertRows(parent: QModelIndex(), first: index, last: index);
1437 d->m_contacts.insert(i: index, t: dc);
1438 d->m_contactMap.insert(key: c.id(), value: dc);
1439 if (!contactsAdded)
1440 contactsAdded = true;
1441 endInsertRows();
1442 }
1443 if (contactsAdded)
1444 emit contactsChanged();
1445 }
1446 request->deleteLater();
1447}
1448
1449
1450static bool contactListDoesNotContainContactWithId(const QList<QContact> &contactList, const QContactId &contactId) {
1451 foreach (const QContact &contact, contactList) {
1452 if (contact.id() == contactId)
1453 return false;
1454 }
1455 return true;
1456}
1457
1458/*!
1459 \internal
1460
1461 It's invoked by the fetch request from onContactsChanged().
1462 */
1463void QDeclarativeContactModel::onContactsChangedFetchRequestStateChanged(QContactAbstractRequest::State state)
1464{
1465 if (state != QContactAbstractRequest::FinishedState)
1466 return;
1467
1468 QContactFetchRequest *request = qobject_cast<QContactFetchRequest *>(object: sender());
1469 Q_ASSERT(request);
1470
1471 checkError(request);
1472 bool contactsUpdated = false;
1473 if (request->error() == QContactManager::NoError || request->error() == QContactManager::DoesNotExistError) {
1474 QList<QContact> fetchedContacts(request->contacts());
1475 QList<QContactId> requestedContactIds;
1476 //read requested contacts ids from the filter
1477 if (request->filter().type() == QContactFilter::IdFilter) {
1478 QContactIdFilter idFilter(request->filter());
1479 requestedContactIds = idFilter.ids();
1480 } else {
1481 QContactIntersectionFilter intersectionFilter(request->filter());
1482 QContactIdFilter idFilter(intersectionFilter.filters().at(i: 0)); // assuming that id filter is the first filter
1483 requestedContactIds = idFilter.ids();
1484 }
1485 //handle updated contacts which needs removal from model
1486 //all contacts requested but not received are removed
1487 foreach (const QContactId &id, requestedContactIds) {
1488 if (contactListDoesNotContainContactWithId(contactList: fetchedContacts, contactId: id)) {
1489 for (int i=0;i<d->m_contacts.size();++i) {
1490 if (d->m_contacts.at(i)->contactId() == id.toString()) {
1491 beginRemoveRows(parent: QModelIndex(), first: i, last: i);
1492 // Remove and delete contact object
1493 QDeclarativeContact* dc = d->m_contacts.takeAt(i);
1494 dc->deleteLater();
1495 d->m_contactMap.remove(key: id);
1496 endRemoveRows();
1497 contactsUpdated = true;
1498 }
1499 }
1500 }
1501 }
1502 foreach (const QContact &fetchedContact, fetchedContacts) {
1503 QString contactIdString(fetchedContact.id().toString());
1504 bool fetchedContactFound = false;
1505 for (int i = 0; i < d->m_contacts.size(); ++i) {
1506 //handle updated contacts which should be updated in the model
1507 if (d->m_contacts.at(i)->contactId() == contactIdString) {
1508 QDeclarativeContact* dc = d->m_contacts.at(i);
1509 dc->setContact(fetchedContact);
1510
1511 // Since the contact can change the position due the sort order we need take care of it
1512 // First we need to remove it from previous position and notify the model about that
1513 beginRemoveRows(parent: QModelIndex(), first: i, last: i);
1514 d->m_contactMap.remove(key: fetchedContact.id());
1515 d->m_contacts.removeAt(i);
1516 endRemoveRows();
1517
1518 // Calculate the new position
1519 int index = contactIndex(contact: dc);
1520 // Notify the model about the new item position
1521 beginInsertRows(parent: QModelIndex(), first: index, last: index);
1522 d->m_contacts.insert(i: index, t: dc);
1523 d->m_contactMap.insert(key: fetchedContact.id(),value: dc);
1524 if (!contactsUpdated)
1525 contactsUpdated = true;
1526 endInsertRows();
1527
1528 fetchedContactFound = true;
1529 break;
1530 }
1531 }
1532 //handle updated contacts which needs to be added in the model
1533 if (!fetchedContactFound) {
1534 QDeclarativeContact* dc = new QDeclarativeContact(this);
1535 dc->setContact(fetchedContact);
1536 int index = contactIndex(contact: dc);
1537 beginInsertRows(parent: QModelIndex(), first: index, last: index);
1538 d->m_contacts.insert(i: index, t: dc);
1539 d->m_contactMap.insert(key: fetchedContact.id(),value: dc);
1540 contactsUpdated = true;
1541 endInsertRows();
1542 }
1543 }
1544 }
1545
1546 if (contactsUpdated)
1547 emit contactsChanged();
1548
1549 request->deleteLater();
1550}
1551
1552int QDeclarativeContactModel::contactIndex(const QDeclarativeContact* contact)
1553{
1554 if (d->m_sortOrders.count() > 0) {
1555 QList<QContactSortOrder> mSortOrders;
1556 foreach (QDeclarativeContactSortOrder *sortOrder, d->m_sortOrders)
1557 mSortOrders.append(t: sortOrder->sortOrder());
1558 for (int i = 0; i < d->m_contacts.size(); i++) {
1559 // check to see if the new contact should be inserted here
1560 int comparison = QContactManagerEngine::compareContact(a: d->m_contacts.at(i)->contact(),
1561 b: contact->contact(),
1562 sortOrders: mSortOrders);
1563 //if the contacts are equal or cannot be compared
1564 //we return the current position.The default case is if the new contact
1565 //should appear before the compared contact in m_contacts
1566 if (comparison >= 0)
1567 return i;
1568 }
1569 }
1570 return d->m_contacts.size();
1571}
1572
1573#include "moc_qdeclarativecontactmodel_p.cpp"
1574
1575QT_END_NAMESPACE
1576

source code of qtpim/src/imports/contacts/qdeclarativecontactmodel.cpp