| 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 | |
| 65 | QTCONTACTS_USE_NAMESPACE |
| 66 | QTVERSIT_USE_NAMESPACE |
| 67 | |
| 68 | QT_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. |
| 102 | class ContactExporterResourceHandler : public QVersitResourceHandler |
| 103 | { |
| 104 | public: |
| 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 | |
| 164 | class QDeclarativeContactModelPrivate |
| 165 | { |
| 166 | public: |
| 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 | |
| 218 | QDeclarativeContactModel::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 | |
| 237 | QDeclarativeContactModel::~QDeclarativeContactModel() |
| 238 | { |
| 239 | } |
| 240 | |
| 241 | /*! |
| 242 | \qmlproperty string ContactModel::manager |
| 243 | |
| 244 | This property holds the manager uri of the contact backend engine. |
| 245 | */ |
| 246 | QString QDeclarativeContactModel::manager() const |
| 247 | { |
| 248 | if (d->m_manager) |
| 249 | return d->m_manager->managerName(); |
| 250 | return QString(); |
| 251 | } |
| 252 | void 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 | |
| 281 | void 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 | */ |
| 296 | void QDeclarativeContactModel::setAutoUpdate(bool autoUpdate) |
| 297 | { |
| 298 | if (autoUpdate == d->m_autoUpdate) |
| 299 | return; |
| 300 | d->m_autoUpdate = autoUpdate; |
| 301 | emit autoUpdateChanged(); |
| 302 | } |
| 303 | |
| 304 | bool QDeclarativeContactModel::autoUpdate() const |
| 305 | { |
| 306 | return d->m_autoUpdate; |
| 307 | } |
| 308 | |
| 309 | void 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 | */ |
| 327 | void 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 | */ |
| 345 | void 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 | */ |
| 362 | void 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 | |
| 372 | void 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 | */ |
| 385 | QString 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 | */ |
| 431 | QStringList QDeclarativeContactModel::availableManagers() const |
| 432 | { |
| 433 | return QContactManager::availableManagers(); |
| 434 | } |
| 435 | static 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 | */ |
| 493 | void 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 | */ |
| 538 | void 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 | |
| 582 | void 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 | |
| 591 | void 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 | |
| 598 | int 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 | */ |
| 613 | QDeclarativeContactFilter* QDeclarativeContactModel::filter() const |
| 614 | { |
| 615 | return d->m_filter; |
| 616 | } |
| 617 | |
| 618 | void 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 | */ |
| 637 | QDeclarativeContactFetchHint* QDeclarativeContactModel::fetchHint() const |
| 638 | { |
| 639 | return d->m_fetchHint; |
| 640 | } |
| 641 | void 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 | */ |
| 660 | QQmlListProperty<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 | |
| 672 | void 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 | |
| 679 | int QDeclarativeContactModel::contacts_count(QQmlListProperty<QDeclarativeContact>* prop) |
| 680 | { |
| 681 | return static_cast<QDeclarativeContactModel*>(prop->object)->d->m_contacts.count(); |
| 682 | } |
| 683 | |
| 684 | QDeclarativeContact* QDeclarativeContactModel::contacts_at(QQmlListProperty<QDeclarativeContact>* prop, int index) |
| 685 | { |
| 686 | return static_cast<QDeclarativeContactModel*>(prop->object)->d->m_contacts.at(i: index); |
| 687 | } |
| 688 | |
| 689 | void 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 | */ |
| 703 | QQmlListProperty<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 | |
| 713 | void 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 | */ |
| 762 | int 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 | */ |
| 790 | void 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 | */ |
| 806 | void 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 | */ |
| 831 | void 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 | */ |
| 849 | void 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 | */ |
| 894 | void 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 | |
| 941 | void 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 | |
| 950 | void 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 | |
| 991 | void 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 | |
| 1032 | void 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 | */ |
| 1092 | void 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 | */ |
| 1106 | void 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 | |
| 1125 | void 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 | |
| 1174 | void QDeclarativeContactModel::checkError(const QContactAbstractRequest *request) |
| 1175 | { |
| 1176 | if (request) { |
| 1177 | updateError(error: request->error()); |
| 1178 | } |
| 1179 | } |
| 1180 | |
| 1181 | void QDeclarativeContactModel::updateError(QContactManager::Error error) |
| 1182 | { |
| 1183 | if (d->m_error != error) { |
| 1184 | d->m_error = error; |
| 1185 | emit errorChanged(); |
| 1186 | } |
| 1187 | } |
| 1188 | |
| 1189 | void 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 | */ |
| 1205 | void 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 | |
| 1218 | void 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 | |
| 1237 | void 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 | |
| 1271 | void 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 | |
| 1296 | QContactFetchRequest *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 | |
| 1314 | QVariant 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 | |
| 1337 | void 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 | |
| 1347 | int 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 | } |
| 1354 | QDeclarativeContactSortOrder * 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 | } |
| 1372 | void 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 | */ |
| 1389 | QQmlListProperty<QDeclarativeContactCollection> QDeclarativeContactModel::collections() |
| 1390 | { |
| 1391 | return QQmlListProperty<QDeclarativeContactCollection>(this, 0, collection_count, collection_at); |
| 1392 | } |
| 1393 | |
| 1394 | int 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 | |
| 1400 | QDeclarativeContactCollection *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 | */ |
| 1415 | void 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 | |
| 1450 | static 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 | */ |
| 1463 | void 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 | |
| 1552 | int 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 | |
| 1575 | QT_END_NAMESPACE |
| 1576 | |