1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40
****************************************************************************/
41
#include <qcontactdetails.h>
42
#include <QtQml/qqmlinfo.h>
44
#include "qdeclarativecontactmodel_p.h"
45
#include "qcontactmanager.h"
46
#include "qcontactmanagerengine.h"
47
#include "qcontactengineid.h"
48
#include "qcontactdetailfilter.h"
49
#include "qcontactidfilter.h"
50
#include "qcontactintersectionfilter.h"
51
#include "qversitreader.h"
52
#include "qversitwriter.h"
53
#include "qversitcontactimporter.h"
54
#include "qversitcontactexporter.h"
62
#include "qcontactrequests.h"
66
\instantiates QDeclarativeContactModel
67
\brief The ContactModel element provides access to contacts from the contacts store.
68
\ingroup qml-contacts-main
69
\inqmlmodule QtContacts 5.0
71
This element is part of the \b{QtContacts} module.
73
ContactModel provides a model of contacts from the contacts store.
74
The contents of the model can be specified with \l filter, \l sortOrders and \l fetchHint properties.
75
Whether the model is automatically updated when the store or \l contacts changes, can be
76
controlled with \l ContactModel::autoUpdate property.
78
There are two ways of accessing the contact data: via model by using views and delegates,
79
or alternatively via \l contacts list property. Of the two, the model access is preferred.
80
Direct list access (i.e. non-model) is not guaranteed to be in order set by \l sortOrder.
82
At the moment the model roles provided by ContactModel are display, decoration and \c contact.
83
Through the \c contact role can access any data provided by the Contact element.
85
\sa RelationshipModel, Contact, {QContactManager}
88
QT_BEGIN_NAMESPACE_CONTACTS
90
class QDeclarativeContactModelPrivate
93
QDeclarativeContactModelPrivate()
95
m_storageLocations(QContactAbstractRequest::UserDataStorage),
98
m_error(QContactManager::NoError),
100
m_componentCompleted(false),
101
m_progressiveLoading(true)
104
~QDeclarativeContactModelPrivate()
110
QList<QDeclarativeContact*> m_contacts;
111
QMap<QContactId, QDeclarativeContact*> m_contactMap;
112
QContactManager* m_manager;
113
QContactAbstractRequest::StorageLocations m_storageLocations;
114
QDeclarativeContactFetchHint* m_fetchHint;
115
QList<QDeclarativeContactSortOrder*> m_sortOrders;
116
QDeclarativeContactFilter* m_filter;
118
QVersitReader m_reader;
119
QVersitWriter m_writer;
120
QStringList m_importProfiles;
122
QContactManager::Error m_error;
125
bool m_componentCompleted;
126
QUrl m_lastExportUrl;
127
QUrl m_lastImportUrl;
128
QAtomicInt m_lastRequestId;
129
QHash<QContactAbstractRequest *, int> m_requestIdHash;
130
QList<QContactFetchRequest*> m_pendingRequests;
131
QList<QContact> m_pendingContacts;
132
bool m_progressiveLoading;
135
QDeclarativeContactModel::QDeclarativeContactModel(QObject *parent) :
136
QAbstractListModel(parent),
137
d(new QDeclarativeContactModelPrivate)
139
QHash<int, QByteArray> roleNames;
140
roleNames = QAbstractItemModel::roleNames();
141
roleNames.insert(ContactRole, "contact");
142
setRoleNames(roleNames);
144
connect(this, SIGNAL(managerChanged()), SLOT(doUpdate()));
145
connect(this, SIGNAL(storageLocationsChanged()), SLOT(doUpdate()));
146
connect(this, SIGNAL(filterChanged()), SLOT(doUpdate()));
147
connect(this, SIGNAL(fetchHintChanged()), SLOT(doUpdate()));
148
connect(this, SIGNAL(sortOrdersChanged()), SLOT(doUpdate()));
151
connect(&d->m_reader, SIGNAL(stateChanged(QVersitReader::State)), this, SLOT(startImport(QVersitReader::State)));
152
connect(&d->m_writer, SIGNAL(stateChanged(QVersitWriter::State)), this, SLOT(contactsExported(QVersitWriter::State)));
156
\qmlproperty string ContactModel::manager
158
This property holds the manager uri of the contact backend engine.
160
QString QDeclarativeContactModel::manager() const
163
return d->m_manager->managerName();
166
void QDeclarativeContactModel::setManager(const QString& managerName)
170
d->m_manager = new QContactManager(managerName);
172
connect(d->m_manager, SIGNAL(dataChanged()), this, SLOT(update()));
173
connect(d->m_manager, SIGNAL(contactsAdded(QList<QContactId>)), this, SLOT(onContactsAdded(QList<QContactId>)));
174
connect(d->m_manager, SIGNAL(contactsRemoved(QList<QContactId>)), this, SLOT(onContactsRemoved(QList<QContactId>)));
175
connect(d->m_manager, SIGNAL(contactsChanged(QList<QContactId>)), this, SLOT(onContactsChanged(QList<QContactId>)));
177
if (d->m_error != QContactManager::NoError) {
178
d->m_error = QContactManager::NoError;
182
emit managerChanged();
186
\qmlproperty enumeration ContactModel::StorageLocation
188
Defines the different storage locations for saving contacts and model population purposes.
191
\li ContactModel::UserDataStorage A storage location where user data is stored.
192
\li ContactModel::SystemStorage A storage location where system files are stored.
195
Depending on the backend implementation, the access rights for different storage locations might vary.
197
\sa ContactModel::storageLocations
198
\sa ContactModel::saveContact
202
\qmlsignal ContactModel::storageLocationsChanged()
204
This signal is emitted, when \l ContactModel::storageLocations property changes.
206
\sa ContactModel::storageLocations
210
\qmlproperty int ContactModel::storageLocations
212
This property indicates which storage location is used to populate the model.
214
Only one storage location can be used for each model.
216
Storage location is a backend specific feature. Some backends support it and some might just ignore it. If backend
217
is having some specific requirements and they're not met, backend returns StorageLocationsNotExistingError.
219
\sa ContactModel::StorageLocation
220
\sa ContactModel::saveContact
222
int QDeclarativeContactModel::storageLocations() const
224
return d->m_storageLocations;
227
void QDeclarativeContactModel::setStorageLocations(int storageLocations)
229
QContactAbstractRequest::StorageLocations newStorageLocation = 0;
230
// only one storage location for a model is supported
231
switch (storageLocations) {
232
case UserDataStorage:
233
newStorageLocation = QContactAbstractRequest::UserDataStorage;
236
newStorageLocation = QContactAbstractRequest::SystemStorage;
238
case (UserDataStorage | SystemStorage):
239
qWarning() << Q_FUNC_INFO << "Model does not support multiple storage locations";
240
updateError(QContactManager::NotSupportedError);
243
qWarning() << Q_FUNC_INFO << "Unknown storage location";
244
updateError(QContactManager::BadArgumentError);
247
if (d->m_storageLocations != newStorageLocation) {
248
d->m_storageLocations = newStorageLocation;
249
emit storageLocationsChanged();
253
void QDeclarativeContactModel::componentComplete()
256
setManager(QString());
258
d->m_componentCompleted = true;
264
\qmlproperty bool ContactModel::autoUpdate
266
This property indicates whether or not the contact model should be updated automatically, default value is true.
268
void QDeclarativeContactModel::setAutoUpdate(bool autoUpdate)
270
if (autoUpdate == d->m_autoUpdate)
272
d->m_autoUpdate = autoUpdate;
273
emit autoUpdateChanged();
276
bool QDeclarativeContactModel::autoUpdate() const
278
return d->m_autoUpdate;
281
void QDeclarativeContactModel::update()
283
if (!d->m_componentCompleted)
285
QMetaObject::invokeMethod(this, "fetchAgain", Qt::QueuedConnection);
288
void QDeclarativeContactModel::doUpdate()
295
\qmlproperty string ContactModel::error
297
This property holds the latest error code returned by the contact manager.
299
This property is read only.
301
QString QDeclarativeContactModel::error() const
304
switch (d->m_error) {
305
case QContactManager::DoesNotExistError:
306
return QStringLiteral("DoesNotExist");
307
case QContactManager::AlreadyExistsError:
308
return QStringLiteral("AlreadyExists");
309
case QContactManager::InvalidDetailError:
310
return QStringLiteral("InvalidDetail");
311
case QContactManager::InvalidRelationshipError:
312
return QStringLiteral("InvalidRelationship");
313
case QContactManager::LockedError:
314
return QStringLiteral("LockedError");
315
case QContactManager::DetailAccessError:
316
return QStringLiteral("DetailAccessError");
317
case QContactManager::PermissionsError:
318
return QStringLiteral("PermissionsError");
319
case QContactManager::OutOfMemoryError:
320
return QStringLiteral("OutOfMemory");
321
case QContactManager::NotSupportedError:
322
return QStringLiteral("NotSupported");
323
case QContactManager::BadArgumentError:
324
return QStringLiteral("BadArgument");
325
case QContactManager::UnspecifiedError:
326
return QStringLiteral("UnspecifiedError");
327
case QContactManager::VersionMismatchError:
328
return QStringLiteral("VersionMismatch");
329
case QContactManager::LimitReachedError:
330
return QStringLiteral("LimitReached");
331
case QContactManager::InvalidContactTypeError:
332
return QStringLiteral("InvalidContactType");
337
return QStringLiteral("NoError");
342
\qmlproperty list<string> ContactModel::availableManagers
344
This property holds the list of available manager names.
345
This property is read only.
347
QStringList QDeclarativeContactModel::availableManagers() const
349
return QContactManager::availableManagers();
351
static QString urlToLocalFileName(const QUrl& url)
353
if (!url.isValid()) {
354
return url.toString();
355
} else if (url.scheme() == "qrc") {
356
return url.toString().remove(0, 5).prepend(':');
358
return url.toLocalFile();
364
\qmlmethod void ContactModel::importContacts(url url, list<string> profiles)
366
Import contacts from a vcard by the given \a url and optional \a profiles.
367
Only one import operation can be active at a time.
368
Supported profiles are:
370
\li "Sync" Imports contacts in sync mode, currently, this is the same as passing in an empty list, and is generally what you want.
371
\li "Backup" imports contacts in backup mode, use this mode if the vCard was generated by exporting in backup mode.
375
\sa QVersitContactHandlerFactory
376
\sa QVersitContactHandlerFactory::ProfileSync()
377
\sa QVersitContactHandlerFactory::ProfileBackup()
380
void QDeclarativeContactModel::importContacts(const QUrl& url, const QStringList& profiles)
382
// Reader is capable of handling only one request at the time.
383
ImportError importError = ImportNotReadyError;
384
if (d->m_reader.state() != QVersitReader::ActiveState) {
386
d->m_importProfiles = profiles;
388
//TODO: need to allow download vcard from network
389
QFile* file = new QFile(urlToLocalFileName(url));
390
bool ok = file->open(QIODevice::ReadOnly);
392
d->m_reader.setDevice(file);
393
if (d->m_reader.startReading()) {
394
d->m_lastImportUrl = url;
397
importError = QDeclarativeContactModel::ImportError(d->m_reader.error());
399
importError = ImportIOError;
402
emit importCompleted(importError, url);
406
\qmlmethod void ContactModel::exportContacts(url url, list<string> profiles, list<variant> declarativeContacts)
408
Export all contacts of this model into a vcard file to the given \a url by optional \a profiles.
409
The optional \a declarativeContacts list can be used to export an arbitrary list of QDeclarativeContact objects
410
not necessarily belonging to the data set of this model.
411
At the moment only the local file url is supported in export method.
412
Also, only one export operation can be active at a time.
413
Supported profiles are:
415
\li "Sync" exports contacts in sync mode, currently, this is the same as passing in an empty list, and is generally what you want.
416
\li "Backup" exports contacts in backup mode, this will add non-standard properties to the generated vCard
417
to try to save every detail of the contacts. Only use this if the vCard is going to be imported using the backup profile.
418
#include "moc_qdeclarativecontactmodel_p.cpp"
421
\sa QVersitContactHandlerFactory
422
\sa QVersitContactHandlerFactory::ProfileSync()
423
\sa QVersitContactHandlerFactory::ProfileBackup()
425
void QDeclarativeContactModel::exportContacts(const QUrl& url, const QStringList& profiles, const QVariantList &declarativeContacts)
427
// Writer is capable of handling only one request at the time.
428
ExportError exportError = ExportNotReadyError;
429
if (d->m_writer.state() != QVersitWriter::ActiveState) {
430
QString profile = profiles.isEmpty()? QString() : profiles.at(0);
431
//only one profile string supported now.
432
QVersitContactExporter exporter(profile);
434
QList<QContact> contacts;
435
if (declarativeContacts.isEmpty()) {
436
foreach (QDeclarativeContact* dc, d->m_contacts) {
437
contacts.append(dc->contact());
441
foreach (const QVariant &contactVariant, declarativeContacts) {
442
QObject *rawObject = contactVariant.value<QObject*>();
443
QDeclarativeContact *dc = qobject_cast<QDeclarativeContact*>(rawObject);
445
contacts.append(dc->contact());
450
exporter.exportContacts(contacts, QVersitDocument::VCard30Type);
451
QList<QVersitDocument> documents = exporter.documents();
452
QFile* file = new QFile(urlToLocalFileName(url));
453
bool ok = file->open(QIODevice::WriteOnly);
455
d->m_writer.setDevice(file);
456
if (d->m_writer.startWriting(documents)) {
457
d->m_lastExportUrl = url;
460
exportError = QDeclarativeContactModel::ExportError(d->m_writer.error());
462
exportError = ExportIOError;
465
emit exportCompleted(exportError, url);
468
void QDeclarativeContactModel::contactsExported(QVersitWriter::State state)
470
if (state == QVersitWriter::FinishedState || state == QVersitWriter::CanceledState) {
471
delete d->m_writer.device();
472
d->m_writer.setDevice(0);
473
emit exportCompleted(QDeclarativeContactModel::ExportError(d->m_writer.error()), d->m_lastExportUrl);
477
int QDeclarativeContactModel::rowCount(const QModelIndex &parent) const
480
return d->m_contacts.count();
486
\qmlproperty Filter ContactModel::filter
488
This property holds the filter instance used by the contact model.
492
QDeclarativeContactFilter* QDeclarativeContactModel::filter() const
497
void QDeclarativeContactModel::setFilter(QDeclarativeContactFilter* filter)
499
if (d->m_filter != filter) {
501
disconnect(d->m_filter, SIGNAL(filterChanged()), this, SLOT(update()));
503
d->m_filter = filter;
505
connect(d->m_filter, SIGNAL(filterChanged()), SLOT(update()), Qt::UniqueConnection);
507
emit filterChanged();
512
\qmlproperty FetchHint ContactModel::fetchHint
514
This property holds the fetch hint instance used by the contact model.
518
QDeclarativeContactFetchHint* QDeclarativeContactModel::fetchHint() const
520
return d->m_fetchHint;
522
void QDeclarativeContactModel::setFetchHint(QDeclarativeContactFetchHint* fetchHint)
524
if (fetchHint && fetchHint != d->m_fetchHint) {
526
delete d->m_fetchHint;
527
d->m_fetchHint = fetchHint;
528
emit fetchHintChanged();
533
\qmlproperty list<Contact> ContactModel::contacts
535
This property holds the list of contacts.
539
QQmlListProperty<QDeclarativeContact> QDeclarativeContactModel::contacts()
541
return QQmlListProperty<QDeclarativeContact>(this,
551
void QDeclarativeContactModel::contacts_append(QQmlListProperty<QDeclarativeContact>* prop, QDeclarativeContact* contact)
555
qWarning() << Q_FUNC_INFO << "appending contacts is not currently supported";
558
int QDeclarativeContactModel::contacts_count(QQmlListProperty<QDeclarativeContact>* prop)
560
return static_cast<QDeclarativeContactModel*>(prop->object)->d->m_contacts.count();
563
QDeclarativeContact* QDeclarativeContactModel::contacts_at(QQmlListProperty<QDeclarativeContact>* prop, int index)
565
return static_cast<QDeclarativeContactModel*>(prop->object)->d->m_contacts.at(index);
568
void QDeclarativeContactModel::contacts_clear(QQmlListProperty<QDeclarativeContact>* prop)
570
QDeclarativeContactModel* model = static_cast<QDeclarativeContactModel*>(prop->object);
571
model->clearContacts();
572
emit model->contactsChanged();
577
\qmlproperty list<SortOrder> ContactModel::sortOrders
579
This property holds a list of sort orders used by the contacts model.
582
QQmlListProperty<QDeclarativeContactSortOrder> QDeclarativeContactModel::sortOrders()
584
return QQmlListProperty<QDeclarativeContactSortOrder>(this,
592
void QDeclarativeContactModel::startImport(QVersitReader::State state)
594
if (state == QVersitReader::FinishedState || state == QVersitReader::CanceledState) {
595
QVersitContactImporter importer(d->m_importProfiles);
596
importer.importDocuments(d->m_reader.results());
597
QList<QContact> contacts = importer.contacts();
599
delete d->m_reader.device();
600
d->m_reader.setDevice(0);
603
if (!d->m_manager->saveContacts(&contacts)) {
604
if (d->m_error != d->m_manager->error()) {
605
d->m_error = d->m_manager->error();
610
emit importCompleted(QDeclarativeContactModel::ImportError(d->m_reader.error()), d->m_lastImportUrl);
615
\qmlsignal ContactModel::contactsFetched(int requestId, list<Contact> fetchedContacts)
617
This signal is emitted, when a contact fetch request is finished.
619
\sa ContactModel::fetchContacts
623
\qmlmethod int ContactModel::fetchContacts(list<string> contactIds)
625
Starts a request to fetch contacts by the given \a contactIds, and returns the unique ID of this request.
626
-1 is returned if the request can't be started.
628
Note that the contacts fetched won't be added to the model, but can be accessed through the contactsFetched
631
\sa ContactModel::contactsFetched
633
int QDeclarativeContactModel::fetchContacts(const QStringList &contactIds)
635
if (contactIds.isEmpty())
638
QContactFetchByIdRequest *fetchRequest = new QContactFetchByIdRequest(this);
639
connect(fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)),
640
this, SLOT(onFetchContactsRequestStateChanged(QContactAbstractRequest::State)));
641
fetchRequest->setManager(d->m_manager);
643
QList<QContactId> ids;
644
foreach (const QString &contactId, contactIds)
645
ids.append(QContactId::fromString(contactId));
646
fetchRequest->setIds(ids);
647
int requestId = d->m_lastRequestId.fetchAndAddOrdered(1);
648
d->m_requestIdHash.insert(fetchRequest, requestId);
649
if (fetchRequest->start()) {
652
d->m_requestIdHash.remove(fetchRequest);
660
void QDeclarativeContactModel::onFetchContactsRequestStateChanged(QContactAbstractRequest::State state)
662
if (state != QContactAbstractRequest::FinishedState || !sender())
665
QContactFetchByIdRequest *request = qobject_cast<QContactFetchByIdRequest *>(sender());
671
const int requestId = d->m_requestIdHash.value(request, -1);
673
qWarning() << Q_FUNC_INFO << "transaction not found from the request hash";
675
d->m_requestIdHash.remove(request);
677
if (request->error() == QContactManager::NoError) {
678
QList<QContact> contacts(request->contacts());
679
foreach (const QContact &contact, contacts) {
680
QDeclarativeContact *declarativeContact(0);
681
declarativeContact = new QDeclarativeContact(this);
682
declarativeContact->setContact(contact);
683
list.append(QVariant::fromValue(declarativeContact));
686
emit contactsFetched(requestId, list);
687
request->deleteLater();
690
void QDeclarativeContactModel::clearContacts()
692
qDeleteAll(d->m_contacts);
693
d->m_contacts.clear();
694
d->m_contactMap.clear();
697
void QDeclarativeContactModel::fetchAgain()
699
QList<QContactSortOrder> sortOrders;
700
foreach (QDeclarativeContactSortOrder* so, d->m_sortOrders) {
701
sortOrders.append(so->sortOrder());
703
QContactFetchRequest* fetchRequest = new QContactFetchRequest(this);
705
fetchRequest->setStorageLocations(d->m_storageLocations);
707
fetchRequest->setManager(d->m_manager);
708
fetchRequest->setSorting(sortOrders);
711
fetchRequest->setFilter(d->m_filter->filter());
713
fetchRequest->setFilter(QContactFilter());
716
fetchRequest->setFetchHint(d->m_fetchHint ? d->m_fetchHint->fetchHint() : QContactFetchHint());
718
connect(fetchRequest, SIGNAL(resultsAvailable()), this, SLOT(requestUpdated()));
719
connect(fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)),
720
this, SLOT(fetchRequestStateChanged(QContactAbstractRequest::State)));
722
// cancel all previous requests
723
foreach (QContactFetchRequest *req, d->m_pendingRequests) {
728
d->m_pendingContacts.clear();
729
d->m_pendingRequests.clear();
730
d->m_pendingRequests.append(fetchRequest);
732
// if we have no contacts yet, we can display results as soon as they arrive
733
// but if we are updating the model after a sort or filter change, we have to
734
// wait for all contacts before processing the update
735
d->m_progressiveLoading = d->m_contacts.isEmpty();
737
fetchRequest->start();
740
void QDeclarativeContactModel::requestUpdated()
743
QContactFetchRequest* req = qobject_cast<QContactFetchRequest*>(QObject::sender());
745
QList<QContact> contacts = req->contacts();
747
// if we are starting from scratch, we can show contact results as they arrive
748
if (d->m_progressiveLoading) {
749
QList<QDeclarativeContact*> dcs;
750
foreach (const QContact &c, contacts) {
751
if (d->m_contactMap.contains(c.id())) {
752
QDeclarativeContact* dc = d->m_contactMap.value(c.id());
755
QDeclarativeContact* dc = new QDeclarativeContact(this);
757
d->m_contactMap.insert(c.id(), dc);
764
if (dcs.count() > 0) {
765
beginInsertRows(QModelIndex(), d->m_contacts.count(), d->m_contacts.count() + dcs.count() - 1);
766
// At this point we need to relay on the backend and assume that the partial results are following the fetch sorting property
767
d->m_contacts += dcs;
770
emit contactsChanged();
773
d->m_pendingContacts << contacts;
780
void QDeclarativeContactModel::fetchRequestStateChanged(QContactAbstractRequest::State newState)
782
if (newState != QContactAbstractRequest::FinishedState)
785
QContactFetchRequest* req = qobject_cast<QContactFetchRequest*>(QObject::sender());
787
// if we were not processing contacts as soon as they arrive, we need to process them here.
788
if (!d->m_progressiveLoading) {
789
// start by removing the contacts that don't belong to this result set anymore
790
for (int i = d->m_contacts.count()-1; i >= 0; --i) {
791
QDeclarativeContact *contact = d->m_contacts[i];
792
if (!d->m_pendingContacts.contains(contact->contact())) {
793
beginRemoveRows(QModelIndex(), i, i);
794
d->m_contacts.removeAt(i);
795
d->m_contactMap.remove(contact->contact().id());
800
// now insert new contacts and move existing ones to their final positions
801
int count = d->m_pendingContacts.count();
802
for (int i = 0; i < count; ++i) {
803
QContact c = d->m_pendingContacts[i];
804
if (!d->m_contactMap.contains(c.id())) {
805
QDeclarativeContact* dc = new QDeclarativeContact(this);
807
beginInsertRows(QModelIndex(), i, i);
808
d->m_contacts.insert(i, dc);
809
d->m_contactMap.insert(c.id(),dc);
812
QDeclarativeContact *contact = d->m_contactMap[c.id()];
814
int pos = d->m_contacts.indexOf(contact);
816
beginMoveRows(QModelIndex(), pos, pos, QModelIndex(), i);
817
d->m_contacts.move(pos, i);
822
emit contactsChanged();
825
// and now clear the pending contact list as the model is up-to-date
826
d->m_pendingContacts.clear();
827
d->m_pendingRequests.removeOne(req);
833
\qmlmethod ContactModel::saveContact(Contact contact, StorageLocation storageLocation = UserDataStorage)
835
Save the given \a contact into the contacts backend.
837
The location for storing the contact can be defined with \a storageLocation for new contacts.
838
When the contact is updated, ie saved again, \a storageLocation is ignored and the contact
839
is saved to the same location as it were before.
841
Once saved successfully, the dirty flags of this contact will be reset.
843
\sa Contact::modified
845
void QDeclarativeContactModel::saveContact(QDeclarativeContact* dc, StorageLocation storageLocation)
848
QContactSaveRequest* req = new QContactSaveRequest(this);
849
req->setManager(d->m_manager);
850
req->setContact(dc->contact());
851
req->setStorageLocation(QContactAbstractRequest::StorageLocation(storageLocation));
852
connect(req,SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
857
void QDeclarativeContactModel::onRequestStateChanged(QContactAbstractRequest::State newState)
859
if (newState != QContactAbstractRequest::FinishedState) {
863
qWarning() << Q_FUNC_INFO << "Called without a sender.";
867
QContactAbstractRequest *request = qobject_cast<QContactAbstractRequest *>(sender());
871
request->deleteLater();
874
void QDeclarativeContactModel::checkError(const QContactAbstractRequest *request)
877
updateError(request->error());
881
void QDeclarativeContactModel::updateError(QContactManager::Error error)
883
if (d->m_error != error) {
889
void QDeclarativeContactModel::onContactsAdded(const QList<QContactId>& ids)
891
if (d->m_autoUpdate && !ids.isEmpty()) {
892
QList<QContactId> contactsIdsForThisModel = extractContactIdsInStorageLocationFromThisModel(ids);
893
if (contactsIdsForThisModel.isEmpty())
895
QContactFetchRequest *fetchRequest = createContactFetchRequest(contactsIdsForThisModel);
896
connect(fetchRequest,SIGNAL(stateChanged(QContactAbstractRequest::State)),
897
this, SLOT(onContactsAddedFetchRequestStateChanged(QContactAbstractRequest::State)));
898
fetchRequest->start();
902
QList<QContactId> QDeclarativeContactModel::extractContactIdsInStorageLocationFromThisModel(const QList<QContactId> &ids)
904
QList<QContactId> idsForThisModel;
905
foreach (const QContactId &id, ids) {
906
if (d->m_storageLocations & extractStorageLocation(id))
907
idsForThisModel.append(id);
909
return idsForThisModel;
912
QContactAbstractRequest::StorageLocations QDeclarativeContactModel::extractStorageLocation(const QContactId &id)
914
const QContactEngineId *engineId = QContactManagerEngine::engineId(id);
915
return engineId->storageLocation();
919
\qmlmethod ContactModel::removeContact(string contactId)
920
Remove the contact from the contacts store by given \a contactId.
921
After removing a contact it is not possible to save it again.
922
\sa Contact::contactId
924
void QDeclarativeContactModel::removeContact(QString id)
932
\qmlmethod ContactModel::removeContacts(list<string> contactIds)
933
Remove the list of contacts from the contacts store by given \a contactIds.
934
\sa Contact::contactId
937
void QDeclarativeContactModel::removeContacts(const QStringList &ids)
939
QContactRemoveRequest* req = new QContactRemoveRequest(this);
940
QList<QContactId> contactIdsAsList;
941
req->setManager(d->m_manager);
943
foreach (const QString& id, ids) {
944
QContactId contactId = QContactId::fromString(id);
945
if (!contactId.isNull())
946
contactIdsAsList.append(contactId);
948
req->setContactIds(contactIdsAsList);
950
connect(req,SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
956
void QDeclarativeContactModel::onContactsRemoved(const QList<QContactId> &ids)
958
if (!d->m_autoUpdate)
961
bool emitSignal = false;
962
foreach (const QContactId &id, ids) {
963
if (d->m_contactMap.contains(id)) {
965
//TODO:need a fast lookup
966
for (; row < d->m_contacts.count(); row++) {
967
if (d->m_contacts.at(row)->contactId() == id.toString())
971
if (row < d->m_contacts.count()) {
972
beginRemoveRows(QModelIndex(), row, row);
973
d->m_contacts.removeAt(row);
974
d->m_contactMap.remove(id);
981
emit contactsChanged();
984
void QDeclarativeContactModel::onContactsChanged(const QList<QContactId> &ids)
986
if (d->m_autoUpdate && !ids.isEmpty()) {
987
QList<QContactId> contactsIdsForThisModel = extractContactIdsInStorageLocationFromThisModel(ids);
988
if (contactsIdsForThisModel.isEmpty())
990
QContactFetchRequest *fetchRequest = createContactFetchRequest(contactsIdsForThisModel);
991
connect(fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)),
992
this, SLOT(onContactsChangedFetchRequestStateChanged(QContactAbstractRequest::State)));
993
fetchRequest->start();
997
QContactFetchRequest *QDeclarativeContactModel::createContactFetchRequest(const QList<QContactId> &ids)
999
QContactFetchRequest *fetchRequest = new QContactFetchRequest(this);
1000
fetchRequest->setManager(d->m_manager);
1001
fetchRequest->setFetchHint(d->m_fetchHint ? d->m_fetchHint->fetchHint() : QContactFetchHint());
1002
// model supports only one storage location at the moment
1003
fetchRequest->setStorageLocations(QFlags<QContactAbstractRequest::StorageLocation>(d->m_storageLocations));
1005
QContactIdFilter idFilter;
1006
idFilter.setIds(ids);
1008
QContactIntersectionFilter filter;
1009
filter.append(idFilter); // result handling assumes that id filter is the first filter
1010
filter.append(d->m_filter->filter());
1011
fetchRequest->setFilter(filter);
1013
fetchRequest->setFilter(idFilter);
1014
return fetchRequest;
1017
QVariant QDeclarativeContactModel::data(const QModelIndex &index, int role) const
1019
//Check if QList itme's index is valid before access it, index should be between 0 and count - 1
1020
if (index.row() < 0 || index.row() >= d->m_contacts.count()) {
1024
QDeclarativeContact* dc = d->m_contacts.value(index.row());
1026
QContact c = dc->contact();
1029
case Qt::DisplayRole:
1030
return c.detail(QContactDetail::TypeDisplayLabel).value(QContactDisplayLabel::FieldLabel);
1031
case Qt::DecorationRole:
1034
return QVariant::fromValue(dc);
1040
void QDeclarativeContactModel::sortOrder_append(QQmlListProperty<QDeclarativeContactSortOrder> *p, QDeclarativeContactSortOrder *sortOrder)
1042
QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(p->object);
1043
if (model && sortOrder) {
1044
QObject::connect(sortOrder, SIGNAL(sortOrderChanged()), model, SIGNAL(sortOrdersChanged()));
1045
model->d->m_sortOrders.append(sortOrder);
1046
emit model->sortOrdersChanged();
1050
int QDeclarativeContactModel::sortOrder_count(QQmlListProperty<QDeclarativeContactSortOrder> *p)
1052
QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(p->object);
1054
return model->d->m_sortOrders.size();
1057
QDeclarativeContactSortOrder * QDeclarativeContactModel::sortOrder_at(QQmlListProperty<QDeclarativeContactSortOrder> *p, int idx)
1059
QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(p->object);
1061
QDeclarativeContactSortOrder* sortOrder = 0;
1064
foreach (QDeclarativeContactSortOrder* s, model->d->m_sortOrders) {
1075
void QDeclarativeContactModel::sortOrder_clear(QQmlListProperty<QDeclarativeContactSortOrder> *p)
1077
QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(p->object);
1080
model->d->m_sortOrders.clear();
1081
emit model->sortOrdersChanged();
1087
It's invoked by the fetch request from onContactsAdded().
1089
void QDeclarativeContactModel::onContactsAddedFetchRequestStateChanged(QContactAbstractRequest::State state)
1092
if (state != QContactAbstractRequest::FinishedState || !sender())
1094
QContactFetchRequest *request = qobject_cast<QContactFetchRequest *>(sender());
1098
checkError(request);
1100
if (request->error() == QContactManager::NoError) {
1101
QList<QContact> fetchedContacts(request->contacts());
1102
bool contactsAdded = false;
1103
foreach (const QContact &c,fetchedContacts) {
1104
if (d->m_contactMap.contains(c.id())) {
1105
qWarning() <<Q_FUNC_INFO <<"contact to be added already exists in the model";
1108
QDeclarativeContact* dc = new QDeclarativeContact(this);
1110
int index = contactIndex(dc);
1111
beginInsertRows(QModelIndex(), index, index);
1112
d->m_contacts.insert(index, dc);
1113
d->m_contactMap.insert(c.id(), dc);
1115
contactsAdded = true;
1119
emit contactsChanged();
1121
request->deleteLater();
1125
static bool contactListDoesNotContainContactWithId(const QList<QContact> &contactList, const QContactId &contactId) {
1126
foreach (const QContact &contact, contactList) {
1127
if (contact.id() == contactId)
1136
It's invoked by the fetch request from onContactsChanged().
1138
void QDeclarativeContactModel::onContactsChangedFetchRequestStateChanged(QContactAbstractRequest::State state)
1140
if (state != QContactAbstractRequest::FinishedState || !sender())
1143
QContactFetchRequest *request = qobject_cast<QContactFetchRequest *>(sender());
1147
checkError(request);
1148
bool contactsUpdated = false;
1149
if (request->error() == QContactManager::NoError || request->error() == QContactManager::DoesNotExistError) {
1150
QList<QContact> fetchedContacts(request->contacts());
1151
QList<QContactId> requestedContactIds;
1152
//read requested contacts ids from the filter
1153
if (request->filter().type() == QContactFilter::IdFilter) {
1154
QContactIdFilter idFilter(request->filter());
1155
requestedContactIds = idFilter.ids();
1157
QContactIntersectionFilter intersectionFilter(request->filter());
1158
QContactIdFilter idFilter(intersectionFilter.filters().at(0)); // assuming that id filter is the first filter
1159
requestedContactIds = idFilter.ids();
1161
//handle updated contacts which needs removal from model
1162
//all contacts requested but not received are removed
1163
foreach (const QContactId &id, requestedContactIds) {
1164
if (contactListDoesNotContainContactWithId(fetchedContacts, id)) {
1165
for (int i=0;i<d->m_contacts.size();++i) {
1166
if (d->m_contacts.at(i)->contactId() == id.toString()) {
1167
beginRemoveRows(QModelIndex(), i, i);
1168
d->m_contacts.removeAt(i);
1169
d->m_contactMap.remove(id);
1171
contactsUpdated = true;
1176
foreach (const QContact &fetchedContact, fetchedContacts) {
1177
QString contactIdString(fetchedContact.id().toString());
1178
bool fetchedContactFound = false;
1179
for (int i = 0; i < d->m_contacts.size(); ++i) {
1180
//handle updated contacts which should be updated in the model
1181
if (d->m_contacts.at(i)->contactId() == contactIdString) {
1182
QDeclarativeContact* dc = d->m_contacts.at(i);
1183
dc->setContact(fetchedContact);
1185
// Since the contact can change the position due the sort order we need take care of it
1186
// First we need to remove it from previous position and notify the model about that
1187
beginRemoveRows(QModelIndex(), i, i);
1188
d->m_contactMap.remove(fetchedContact.id());
1189
d->m_contacts.removeAt(i);
1192
// Calculate the new position
1193
int index = contactIndex(dc);
1194
// Notify the model about the new item position
1195
beginInsertRows(QModelIndex(), index, index);
1196
d->m_contacts.insert(index, dc);
1197
d->m_contactMap.insert(fetchedContact.id(),dc);
1198
if (!contactsUpdated)
1199
contactsUpdated = true;
1202
fetchedContactFound = true;
1206
//handle updated contacts which needs to be added in the model
1207
if (!fetchedContactFound) {
1208
QDeclarativeContact* dc = new QDeclarativeContact(this);
1209
dc->setContact(fetchedContact);
1210
int index = contactIndex(dc);
1211
beginInsertRows(QModelIndex(), index, index);
1212
d->m_contacts.insert(index, dc);
1213
d->m_contactMap.insert(fetchedContact.id(),dc);
1214
contactsUpdated = true;
1220
if (contactsUpdated)
1221
emit contactsChanged();
1223
request->deleteLater();
1226
int QDeclarativeContactModel::contactIndex(const QDeclarativeContact* contact)
1228
if (d->m_sortOrders.count() > 0) {
1229
QList<QContactSortOrder> mSortOrders;
1230
foreach (QDeclarativeContactSortOrder *sortOrder, d->m_sortOrders)
1231
mSortOrders.append(sortOrder->sortOrder());
1232
for (int i = 0; i < d->m_contacts.size(); i++) {
1233
// check to see if the new contact should be inserted here
1234
int comparison = QContactManagerEngine::compareContact(d->m_contacts.at(i)->contact(),
1237
//if the contacts are equal or cannot be compared
1238
//we return the current position.The default case is if the new contact
1239
//should appear before the compared contact in m_contacts
1240
if (comparison >= 0)
1244
return d->m_contacts.size();
1247
#include "moc_qdeclarativecontactmodel_p.cpp"
1249
QT_END_NAMESPACE_CONTACTS