diff --git a/src/contacts/contacts.pro b/src/contacts/contacts.pro index d4100f8a449f49bfb665188e28ebe948d2563982..55226af73893c9c7d32b792bca6d4183a1b29e00 100644 --- a/src/contacts/contacts.pro +++ b/src/contacts/contacts.pro @@ -19,6 +19,9 @@ PUBLIC_HEADERS += \ qcontactactionfactory.h \ qcontactactiontarget.h \ qcontactchangeset.h \ + qcontactcollection.h \ + qcontactcollectionchangeset.h \ + qcontactcollectionid.h \ qcontactdetail.h \ qcontactfetchhint.h \ qcontactfilter.h \ @@ -39,6 +42,8 @@ PRIVATE_HEADERS += \ qcontactactionmanager_p.h \ qcontactactiontarget_p.h \ qcontactchangeset_p.h \ + qcontactcollection_p.h \ + qcontactcollectionchangeset_p.h \ qcontactdetail_p.h \ qcontactfetchhint_p.h \ qcontactfilter_p.h \ @@ -56,6 +61,9 @@ SOURCES += \ qcontactactionmanager_p.cpp \ qcontactactiontarget.cpp \ qcontactchangeset.cpp \ + qcontactcollection.cpp \ + qcontactcollectionchangeset.cpp \ + qcontactcollectionid.cpp \ qcontactdetail.cpp \ qcontactfetchhint.cpp \ qcontactfilter.cpp \ diff --git a/src/contacts/filters/filters.pri b/src/contacts/filters/filters.pri index 0c017cab0713f73d9902020683a2f1684c05a312..19a8e8d75a1afccb3861e8a158b91d64234e38cf 100644 --- a/src/contacts/filters/filters.pri +++ b/src/contacts/filters/filters.pri @@ -3,6 +3,7 @@ INCLUDEPATH += filters PUBLIC_HEADERS += \ filters/qcontactactionfilter.h \ filters/qcontactchangelogfilter.h \ + filters/qcontactcollectionfilter.h \ filters/qcontactdetailfilter.h \ filters/qcontactdetailrangefilter.h \ filters/qcontactfilters.h \ @@ -15,6 +16,7 @@ PUBLIC_HEADERS += \ PRIVATE_HEADERS += \ filters/qcontactactionfilter_p.h \ filters/qcontactchangelogfilter_p.h \ + filters/qcontactcollectionfilter_p.h \ filters/qcontactdetailfilter_p.h \ filters/qcontactdetailrangefilter_p.h \ filters/qcontactidfilter_p.h \ @@ -25,6 +27,7 @@ PRIVATE_HEADERS += \ SOURCES += \ filters/qcontactactionfilter.cpp \ filters/qcontactchangelogfilter.cpp \ + filters/qcontactcollectionfilter.cpp \ filters/qcontactdetailfilter.cpp \ filters/qcontactdetailrangefilter.cpp \ filters/qcontactidfilter.cpp \ diff --git a/src/contacts/filters/qcontactcollectionfilter.cpp b/src/contacts/filters/qcontactcollectionfilter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56bb9de215a3dec8f1da7ca4ae70b880203f12b7 --- /dev/null +++ b/src/contacts/filters/qcontactcollectionfilter.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtContacts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcontactcollectionfilter.h" +#include "qcontactcollectionfilter_p.h" + +QT_BEGIN_NAMESPACE_CONTACTS + +/*! + \class QContactCollectionFilter + \brief The QContactCollectionFilter class provides a filter based around the collection one + contact belongs to. + \inmodule QtContacts + \ingroup contacts-filters + + It may be used to select contacts belonging to certain collections. + */ + +Q_IMPLEMENT_CONTACTFILTER_PRIVATE(QContactCollectionFilter) + +/*! + \fn QContactCollectionFilter::QContactCollectionFilter(const QContactCollectionFilter &other) + + Constructs a copy of \a other if possible, otherwise constructs a new contact collection filter. + */ + +/*! + Constructs a new contact collection filter. + */ +QContactCollectionFilter::QContactCollectionFilter() + : QContactFilter(new QContactCollectionFilterPrivate) +{ +} + +/*! + Sets the \a id of the collection, which the contacts should belong to. + */ +void QContactCollectionFilter::setCollectionId(const QContactCollectionId &id) +{ + Q_D(QContactCollectionFilter); + d->m_ids.clear(); + d->m_ids.insert(id); +} + +/*! + Sets the list of collection \a ids, which the contacts should belong to. + */ +void QContactCollectionFilter::setCollectionIds(const QSet<QContactCollectionId> &ids) +{ + Q_D(QContactCollectionFilter); + d->m_ids = ids; +} + +/*! + Returns the list of collection IDs of contacts should belong to. + */ +QSet<QContactCollectionId> QContactCollectionFilter::collectionIds() const +{ + Q_D(const QContactCollectionFilter); + return d->m_ids; +} + +QT_END_NAMESPACE_CONTACTS diff --git a/src/contacts/filters/qcontactcollectionfilter.h b/src/contacts/filters/qcontactcollectionfilter.h new file mode 100644 index 0000000000000000000000000000000000000000..da6cdede49d417964feaa969337eec3f47a48190 --- /dev/null +++ b/src/contacts/filters/qcontactcollectionfilter.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTACTCOLLECTIONFILTER_H +#define QCONTACTCOLLECTIONFILTER_H + +#include <QtCore/qset.h> + +#include <QtContacts/qcontactcollectionid.h> +#include <QtContacts/qcontactfilter.h> + +QT_BEGIN_NAMESPACE_CONTACTS + +class QContactCollectionFilterPrivate; + +/* Leaf class */ + +class Q_CONTACTS_EXPORT QContactCollectionFilter : public QContactFilter +{ +public: + QContactCollectionFilter(); + QContactCollectionFilter(const QContactFilter &other); + + void setCollectionId(const QContactCollectionId &id); + void setCollectionIds(const QSet<QContactCollectionId> &ids); + QSet<QContactCollectionId> collectionIds() const; + +private: + Q_DECLARE_CONTACTFILTER_PRIVATE(QContactCollectionFilter) +}; + +QT_END_NAMESPACE_CONTACTS + +#endif // QCONTACTCOLLECTIONFILTER_H diff --git a/src/contacts/filters/qcontactcollectionfilter_p.h b/src/contacts/filters/qcontactcollectionfilter_p.h new file mode 100644 index 0000000000000000000000000000000000000000..db06be82696771d156ddc6638bd5270a42fde619 --- /dev/null +++ b/src/contacts/filters/qcontactcollectionfilter_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTACTCOLLECTIONFILTER_P_H +#define QCONTACTCOLLECTIONFILTER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtContacts/qcontactcollectionfilter.h> +#include <QtContacts/private/qcontactfilter_p.h> + +#include <algorithm> + +QT_BEGIN_NAMESPACE_CONTACTS + +class QContactCollectionFilterPrivate : public QContactFilterPrivate +{ +public: + QContactCollectionFilterPrivate() + : QContactFilterPrivate() + { + } + + QContactCollectionFilterPrivate(const QContactCollectionFilterPrivate &other) + : QContactFilterPrivate(other), m_ids(other.m_ids) + { + } + + virtual bool compare(const QContactFilterPrivate *other) const + { + const QContactCollectionFilterPrivate *od = static_cast<const QContactCollectionFilterPrivate *>(other); + if (od) + return m_ids == od->m_ids; + return false; + } + +#ifndef QT_NO_DATASTREAM + QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const + { + if (formatVersion == 1) + stream << m_ids; + return stream; + } + + QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion) + { + if (formatVersion == 1) + stream >> m_ids; + return stream; + } +#endif // QT_NO_DATASTREAM + +#ifndef QT_NO_DEBUG_STREAM + QDebug &debugStreamOut(QDebug &dbg) const + { + dbg.nospace() << "QContactCollectionFilter(collectionIds="; + QList<QContactCollectionId> ids(m_ids.toList()); + std::sort(ids.begin(), ids.end()); + dbg.nospace() << ids; + dbg.nospace() << ")"; + return dbg.maybeSpace(); + } +#endif // QT_NO_DEBUG_STREAM + + Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(QContactCollectionFilter, QContactFilter::CollectionFilter) + + QSet<QContactCollectionId> m_ids; +}; + +QT_END_NAMESPACE_CONTACTS + +#endif // QCONTACTCOLLECTIONFILTER_P_H diff --git a/src/contacts/filters/qcontactfilters.h b/src/contacts/filters/qcontactfilters.h index ccae2927614258f953c188b425fe282d1ebafd44..8d8618a7b70e81dbb7e7ddaba5fe26d9a5ba2282 100644 --- a/src/contacts/filters/qcontactfilters.h +++ b/src/contacts/filters/qcontactfilters.h @@ -47,6 +47,7 @@ #include <QtContacts/qcontactactionfilter.h> #include <QtContacts/qcontactchangelogfilter.h> +#include <QtContacts/qcontactcollectionfilter.h> #include <QtContacts/qcontactdetailfilter.h> #include <QtContacts/qcontactdetailrangefilter.h> #include <QtContacts/qcontactidfilter.h> diff --git a/src/contacts/qcontact.cpp b/src/contacts/qcontact.cpp index 931fbd6ae37a9e1437869a6de63f35edf6184f87..d2ebadf33e4fe2c7902143cec6b48062cc50b241 100644 --- a/src/contacts/qcontact.cpp +++ b/src/contacts/qcontact.cpp @@ -670,6 +670,16 @@ QList<QContactId> QContact::relatedContacts(const QString& relationshipType, QCo return retn; } +QContactCollectionId QContact::collectionId() const +{ + return d->m_collectionId; +} + +void QContact::setCollectionId(const QContactCollectionId &collectionId) +{ + d->m_collectionId = collectionId; +} + /*! * Return a list of descriptors for the actions available to be performed on this contact. * diff --git a/src/contacts/qcontact.h b/src/contacts/qcontact.h index ef895dd3943af899585f95cbc80721a15fa22607..05da340eddbc137aaaf270636aec392996ced62a 100644 --- a/src/contacts/qcontact.h +++ b/src/contacts/qcontact.h @@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE_CONTACTS class QContactActionDescriptor; class QContactId; class QContactManager; +class QContactCollectionId; class QContactData; class Q_CONTACTS_EXPORT QContact @@ -115,6 +116,9 @@ public: QList<QContactRelationship> relationships(const QString& relationshipType = QString()) const; QList<QContactId> relatedContacts(const QString& relationshipType = QString(), QContactRelationship::Role role = QContactRelationship::Either) const; + QContactCollectionId collectionId() const; + void setCollectionId(const QContactCollectionId &collectionId); + /* Actions available to be performed on this contact */ QList<QContactActionDescriptor> availableActions(const QString& serviceName = QString()) const; diff --git a/src/contacts/qcontact_p.h b/src/contacts/qcontact_p.h index eba3dfe931b291894328d40442e5f9792efa65ec..460c0bcef54ccb155a834c5e4263b657346493c0 100644 --- a/src/contacts/qcontact_p.h +++ b/src/contacts/qcontact_p.h @@ -61,6 +61,7 @@ #include <QtContacts/qcontactdetail.h> #include <QtContacts/qcontactid.h> #include <QtContacts/qcontactrelationship.h> +#include <QtContacts/qcontactcollectionid.h> QT_BEGIN_NAMESPACE_CONTACTS @@ -75,6 +76,7 @@ public: QContactData(const QContactData& other) : QSharedData(other), m_id(other.m_id), + m_collectionId(other.m_collectionId), m_details(other.m_details), m_relationshipsCache(other.m_relationshipsCache), m_preferences(other.m_preferences) @@ -84,6 +86,7 @@ public: ~QContactData() {} QContactId m_id; + QContactCollectionId m_collectionId; QList<QContactDetail> m_details; QList<QContactRelationship> m_relationshipsCache; QMap<QString, int> m_preferences; diff --git a/src/contacts/qcontactabstractrequest.h b/src/contacts/qcontactabstractrequest.h index e8dec93168bdbd12bfc824e9d6ac1c99928cf2d2..0e2ea3b95a8ab9744474ec93702aa8a3f9f2714a 100644 --- a/src/contacts/qcontactabstractrequest.h +++ b/src/contacts/qcontactabstractrequest.h @@ -84,7 +84,10 @@ public: RelationshipFetchRequest, RelationshipRemoveRequest, RelationshipSaveRequest, - ContactFetchByIdRequest + ContactFetchByIdRequest, + CollectionFetchRequest, + CollectionRemoveRequest, + CollectionSaveRequest, }; RequestType type() const; diff --git a/src/contacts/qcontactcollection.cpp b/src/contacts/qcontactcollection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d7affe5614c19291af9c4d014a8e59fe9963e17 --- /dev/null +++ b/src/contacts/qcontactcollection.cpp @@ -0,0 +1,307 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcontactcollection.h" +#include "qcontactcollection_p.h" + +#ifndef QT_NO_DATASTREAM +#include <QtCore/qdatastream.h> +#endif +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> +#endif + +QT_BEGIN_NAMESPACE_CONTACTS + +/*! + \class QContactCollection + \brief The QContactCollection class represents a collection of contacts in a manager. + \inmodule QtContacts + \ingroup contacts-main + + A collection has an ID and optionally some metadata, and contains zero or more contacts. + Each different manager will have different requirements before a collection may be saved + in it. Some managers do not allow collections to be saved at all, while others may require + a collection to have some minimal amount of metadata defined in it prior to save. + For example, most managers require a valid value for the QContactCollection::KeyName + meta data key to be set prior to save. + + Every QContact is contained within a collection when stored in a manager. + To save an contact in a collection, the client should call QContact::setCollectionId() + on the contact, passing in the ID of the destination collection as the argument, and then + save the contact in the manager. To move an contact from one collection to another, the client + must fetch the contact from the manager, set the collection ID in the contact to the ID of the + collection to which the client wishes the contact to be moved, and then resave the contact in the + manager. That is, the collection which a contact is part of is treated as a property of the + contact. + */ + +/*! + \enum QContactCollection::MetaDataKey + + This enumeration describes the key of the contact collection metadata. + + \value KeyName This metadata describes the name of the collection. + \value KeyDescription This metadata gives a description of the collection. + \value KeyColor This metadata describes the color of the collection. + \value KeySecondaryColor This metadata describes the secondary color of the collection. + \value KeyImage This metadata describes the image of the collection. + \value KeyExtended This is an extened metadata, which is stored as a QVariantMap. + */ + +/*! + Constructs a new collection. + */ +QContactCollection::QContactCollection() + : d(new QContactCollectionData) +{ +} + +/*! + Cleans up any memory in use by the collection. + */ +QContactCollection::~QContactCollection() +{ +} + +/*! + Constructs a new copy of the \a other collection. + */ +QContactCollection::QContactCollection(const QContactCollection &other) + : d(other.d) +{ +} + +/*! + Assigns this collection to be equal to the \a other collection. + */ +QContactCollection &QContactCollection::operator=(const QContactCollection &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if the collection is the same as that of the \a other collection, false if either + the ID or any of the stored metadata are not the same. + */ +bool QContactCollection::operator==(const QContactCollection &other) const +{ + if (d == other.d) + return true; + + if (d->m_id != other.d->m_id + || d->m_metaData.size() != other.d->m_metaData.size()) { + return false; + } + + QMap<QContactCollection::MetaDataKey, QVariant>::const_iterator i = d->m_metaData.constBegin(); + while (i != d->m_metaData.constEnd()) { + if (i.value() != other.d->m_metaData.value(i.key())) + return false; + ++i; + } + + return true; +} + +/*! + \fn QContactCollection::operator!=(const QContactCollection &other) const + + Returns true if the collection is not the same as the \a other collection. + */ + +/*! + Returns the ID of the collection. + */ +QContactCollectionId QContactCollection::id() const +{ + return d->m_id; +} + +/*! + Sets the ID of the collection to \a id. + + If the ID is set to a null (default-constructed) ID, saving the collection will cause the manager + to save the collection as a new collection. + */ +void QContactCollection::setId(const QContactCollectionId &id) +{ + d->m_id = id; +} + +/*! + Sets the metadata of the collection to be \a metaData. + */ +void QContactCollection::setMetaData(const QMap<QContactCollection::MetaDataKey, QVariant> &metaData) +{ + d->m_metaData = metaData; +} + +/*! + Returns the meta data of the collection. + */ +QMap<QContactCollection::MetaDataKey, QVariant> QContactCollection::metaData() const +{ + return d->m_metaData; +} + +/*! + Sets the meta data of the collection for the given \a key to the given \a value. + */ +void QContactCollection::setMetaData(MetaDataKey key, const QVariant &value) +{ + d->m_metaData.insert(key, value); +} + +/*! + Sets the value of the extended metadata with the given \a key to \a value. + */ +void QContactCollection::setExtendedMetaData(const QString &key, const QVariant &value) +{ + QVariantMap variantMap = d->m_metaData.value(QContactCollection::KeyExtended).toMap(); + variantMap.insert(key, value); + d->m_metaData.insert(QContactCollection::KeyExtended, variantMap); +} + +/*! + Returns the value of extended metadata with the given \a key. + */ +QVariant QContactCollection::extendedMetaData(const QString &key) const +{ + return d->m_metaData.value(QContactCollection::KeyExtended).toMap().value(key); +} + +/*! + Returns the meta data of the collection for the given \a key. + */ +QVariant QContactCollection::metaData(MetaDataKey key) const +{ + return d->m_metaData.value(key); +} + +/*! + \relates QContactCollection + Returns the hash value for \a key. + */ +Q_CONTACTS_EXPORT uint qHash(const QContactCollection &key) +{ + uint hash = qHash(key.id()); + QMap<QContactCollection::MetaDataKey, QVariant>::const_iterator i = key.d->m_metaData.constBegin(); + while (i != key.d->m_metaData.constEnd()) { + if (i.key() == QContactCollection::KeyExtended) { + QVariantMap variantMap = i.value().toMap(); + QVariantMap::const_iterator j = variantMap.constBegin(); + while (j != variantMap.constEnd()) { + hash += QT_PREPEND_NAMESPACE(qHash)(j.key()) + QT_PREPEND_NAMESPACE(qHash)(j.value().toString()); + ++j; + } + } else { + hash += QT_PREPEND_NAMESPACE(qHash)(i.key()) + QT_PREPEND_NAMESPACE(qHash)(i.value().toString()); + } + ++i; + } + return hash; +} + +#ifndef QT_NO_DEBUG_STREAM +/*! + \relates QContactCollection + Streams the \a collection to the given debug stream \a dbg, and returns the stream. + */ +QDebug operator<<(QDebug dbg, const QContactCollection& collection) +{ + dbg.nospace() << "QContactCollection(id=" << collection.id(); + + QMap<QContactCollection::MetaDataKey, QVariant> metaData = collection.metaData(); + QMap<QContactCollection::MetaDataKey, QVariant>::const_iterator i = metaData.constBegin(); + while (i != metaData.constEnd()) { + dbg.nospace() << ", " << i.key() << '=' << i.value(); + ++i; + } + dbg.nospace() << ')'; + return dbg.maybeSpace(); +} +#endif // QT_NO_DEBUG_STREAM + +#ifndef QT_NO_DATASTREAM +/*! + \relates QContactCollection + Writes \a collection to the stream \a out. + */ +QDataStream &operator<<(QDataStream &out, const QContactCollection &collection) +{ + quint8 formatVersion = 1; + return out << formatVersion + << collection.id().toString() + << collection.metaData(); +} + +/*! + \relates QContactCollection + Reads an organizer collection from stream \a in into \a collection. + */ +QDataStream &operator>>(QDataStream &in, QContactCollection &collection) +{ + quint8 formatVersion; + in >> formatVersion; + if (formatVersion == 1) { + QString idString; + QMap<int, QVariant> values; + in >> idString >> values; + + collection = QContactCollection(); + collection.setId(QContactCollectionId::fromString(idString)); + + QMap<int, QVariant>::const_iterator i = values.constBegin(); + while (i != values.constEnd()) { + collection.setMetaData(static_cast<QContactCollection::MetaDataKey>(i.key()), i.value()); + ++i; + } + } else { + in.setStatus(QDataStream::ReadCorruptData); + } + return in; +} +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE_CONTACTS diff --git a/src/contacts/qcontactcollection.h b/src/contacts/qcontactcollection.h new file mode 100644 index 0000000000000000000000000000000000000000..2278db565d3812c93cf9b865a90cc17fb05b8265 --- /dev/null +++ b/src/contacts/qcontactcollection.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTACTCOLLECTION_H +#define QCONTACTCOLLECTION_H + +#include <QtCore/qmap.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qvariant.h> + +#include <QtContacts/qcontactcollectionid.h> +#include <QtContacts/qcontactid.h> +#include <QtContacts/qcontactdetail.h> +#include <QtContacts/qcontacttype.h> + +QT_BEGIN_NAMESPACE_CONTACTS + +class QContactManagerEngine; + +class QContactCollectionData; +class Q_CONTACTS_EXPORT QContactCollection +{ +public: + enum MetaDataKey { + KeyName = 0, + KeyDescription, + KeyColor, + KeySecondaryColor, + KeyImage, + KeyExtended + }; + + QContactCollection(); + ~QContactCollection(); + + QContactCollection(const QContactCollection &other); + QContactCollection &operator=(const QContactCollection &other); + + bool operator==(const QContactCollection &other) const; + bool operator!=(const QContactCollection &other) const {return !(other == *this);} + + QContactCollectionId id() const; + void setId(const QContactCollectionId &id); + + void setMetaData(MetaDataKey key, const QVariant &value); + QVariant metaData(MetaDataKey key) const; + + void setMetaData(const QMap<QContactCollection::MetaDataKey, QVariant> &metaData); + QMap<QContactCollection::MetaDataKey, QVariant> metaData() const; + + void setExtendedMetaData(const QString &key, const QVariant &value); + QVariant extendedMetaData(const QString &key) const; + +private: + friend Q_CONTACTS_EXPORT uint qHash(const QContactCollection &key); + friend class QContactManagerEngine; + QSharedDataPointer<QContactCollectionData> d; +}; + +Q_CONTACTS_EXPORT uint qHash(const QContactCollection &key); + +#ifndef QT_NO_DEBUG_STREAM +Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactCollection &collection); +#endif // QT_NO_DEBUG_STREAM + +#ifndef QT_NO_DATASTREAM +Q_CONTACTS_EXPORT QDataStream &operator<<(QDataStream &out, const QContactCollection &collection); +Q_CONTACTS_EXPORT QDataStream &operator>>(QDataStream &in, QContactCollection &collection); +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE_CONTACTS + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContactCollection), Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +#endif // QCONTACTCOLLECTION_H diff --git a/src/contacts/qcontactcollection_p.h b/src/contacts/qcontactcollection_p.h new file mode 100644 index 0000000000000000000000000000000000000000..eaf5da74852272d1e8ccb9b7574b49c2296d97dc --- /dev/null +++ b/src/contacts/qcontactcollection_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTACTCOLLECTION_P_H +#define QCONTACTCOLLECTION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qmap.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qvariant.h> + +#include <QtContacts/qcontactcollection.h> +#include <QtContacts/qcontactcollectionid.h> + +QT_BEGIN_NAMESPACE_CONTACTS + +class QContactCollectionData : public QSharedData +{ +public: + QContactCollectionData() + : QSharedData() + { + } + + QContactCollectionData(const QContactCollectionData &other) + : QSharedData(other), m_metaData(other.m_metaData), m_id(other.m_id) + { + } + + ~QContactCollectionData() + { + } + + QMap<QContactCollection::MetaDataKey, QVariant> m_metaData; + QContactCollectionId m_id; +}; + +QT_END_NAMESPACE_CONTACTS + +#endif // QCONTACTCOLLECTION_P_H diff --git a/src/contacts/qcontactcollectionchangeset.cpp b/src/contacts/qcontactcollectionchangeset.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f9af68052740da1a9ca55d44d61143b582f6d37 --- /dev/null +++ b/src/contacts/qcontactcollectionchangeset.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcontactcollectionchangeset.h" +#include "qcontactcollectionchangeset_p.h" + +#include "qcontactmanagerengine.h" + +QT_BEGIN_NAMESPACE_CONTACTS + +/*! + \class QContactCollectionChangeSet + \brief The QContactCollectionChangeSet class provides a simple API to simplify the emission + of state-change signals for collections from QContactManagerEngine implementations. + \inmodule QtContacts + \ingroup contacts-main + + This class should only be used by backend developers. + */ + +/*! + Constructs a new change set. + */ +QContactCollectionChangeSet::QContactCollectionChangeSet() + : d(new QContactCollectionChangeSetData) +{ +} + +/*! + Constructs a copy of the \a other change set. + */ +QContactCollectionChangeSet::QContactCollectionChangeSet(const QContactCollectionChangeSet &other) + : d(other.d) +{ +} + +/*! + Frees the memory used by this change set. + */ +QContactCollectionChangeSet::~QContactCollectionChangeSet() +{ +} + +/*! + Assigns this change set to be equal to \a other. + */ +QContactCollectionChangeSet &QContactCollectionChangeSet::operator=(const QContactCollectionChangeSet &other) +{ + d = other.d; + return *this; +} + +/*! + Sets the data changed flag to \a dataChanged. + + If this is set to true prior to calling emitSignals(), only the QContactManagerEngine::dataChanged() + signal will be emitted; otherwise, the appropriate finer-grained signals will be emitted. + */ +void QContactCollectionChangeSet::setDataChanged(bool dataChanged) +{ + d->m_dataChanged = dataChanged; +} + +/*! + Returns the value of the data changed flag. + */ +bool QContactCollectionChangeSet::dataChanged() const +{ + return d->m_dataChanged; +} + +/*! + Returns the set of IDs of collections which have been added to the database. + */ +QSet<QContactCollectionId> QContactCollectionChangeSet::addedCollections() const +{ + return d->m_addedCollections; +} + +/*! + Inserts the given \a collectionId into the set of IDs of collections which have been added to + the database. + */ +void QContactCollectionChangeSet::insertAddedCollection(const QContactCollectionId &collectionId) +{ + d->m_addedCollections.insert(collectionId); + d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(collectionId, QContactManager::Add)); +} + +/*! + Inserts each of the given \a collectionIds into the set of IDs of collections which have been + added to the database. + */ +void QContactCollectionChangeSet::insertAddedCollections(const QList<QContactCollectionId> &collectionIds) +{ + foreach (const QContactCollectionId &id, collectionIds) { + d->m_addedCollections.insert(id); + d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(id, QContactManager::Add)); + } +} + +/*! + Clears the set of IDs of collections which have been added to the database. + */ +void QContactCollectionChangeSet::clearAddedCollections() +{ + d->m_addedCollections.clear(); +} + +/*! + Returns the set of IDs of collections which have been changed in the database. + */ +QSet<QContactCollectionId> QContactCollectionChangeSet::changedCollections() const +{ + return d->m_changedCollections; +} + +/*! + Inserts the given \a collectionId into the set of IDs of collections which have been changed in + the database. + */ +void QContactCollectionChangeSet::insertChangedCollection(const QContactCollectionId &collectionId) +{ + d->m_changedCollections.insert(collectionId); + d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(collectionId, QContactManager::Change)); +} + +/*! + Inserts each of the given \a collectionIds into the set of IDs of collections which have been + changed in the database. + */ +void QContactCollectionChangeSet::insertChangedCollections(const QList<QContactCollectionId> &collectionIds) +{ + foreach (const QContactCollectionId &id, collectionIds) { + d->m_changedCollections.insert(id); + d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(id, QContactManager::Change)); + } +} + +/*! + Clears the set of IDs of collections which have been changed in the database. + */ +void QContactCollectionChangeSet::clearChangedCollections() +{ + d->m_changedCollections.clear(); +} + +/*! + Returns the set of IDs of collections which have been removed from the database. + */ +QSet<QContactCollectionId> QContactCollectionChangeSet::removedCollections() const +{ + return d->m_removedCollections; +} + +/*! + Inserts the given \a collectionId into the set of IDs of collections which have been removed from + the database. + */ +void QContactCollectionChangeSet::insertRemovedCollection(const QContactCollectionId &collectionId) +{ + d->m_removedCollections.insert(collectionId); + d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(collectionId, QContactManager::Remove)); +} + +/*! + Inserts each of the given \a collectionIds into the set of IDs of collections which have been + removed from the database. + */ +void QContactCollectionChangeSet::insertRemovedCollections(const QList<QContactCollectionId> &collectionIds) +{ + foreach (const QContactCollectionId &id, collectionIds) { + d->m_removedCollections.insert(id); + d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(id, QContactManager::Remove)); + } +} + +/*! + Clears the set of ids of collections which have been removed from the database. + */ +void QContactCollectionChangeSet::clearRemovedCollections() +{ + d->m_removedCollections.clear(); +} + +/*! + Returns the list of ids of organizer collections which have been added, changed or removed from + the database. The list includes information about which database operation was done. The ids and + operations are ordered so that the first operation is first in the list. + */ +QList<QPair<QContactCollectionId, QContactManager::Operation> > QContactCollectionChangeSet::modifiedCollections() const +{ + return d->m_modifiedCollections; +} + +/*! + Clears the list of ids of organizer collections which have been added, changed or removed from the database + */ +void QContactCollectionChangeSet::clearModifiedCollections() +{ + d->m_modifiedCollections.clear(); +} + +/*! + Clears all flags and sets of IDs in this change set. + */ +void QContactCollectionChangeSet::clearAll() +{ + d->m_dataChanged = false; + d->m_addedCollections.clear(); + d->m_changedCollections.clear(); + d->m_removedCollections.clear(); + d->m_modifiedCollections.clear(); +} + +/*! + Emits the appropriate signals from the given \a engine given the state of the change set. Note + that the flags and sets of IDs are not cleared after signals are emitted. + */ +void QContactCollectionChangeSet::emitSignals(QContactManagerEngine *engine) const +{ + if (!engine) + return; + + if (d->m_dataChanged) { + emit engine->dataChanged(); + } else { + if (!d->m_addedCollections.isEmpty()) + emit engine->collectionsAdded(d->m_addedCollections.toList()); + if (!d->m_changedCollections.isEmpty()) + emit engine->collectionsChanged(d->m_changedCollections.toList()); + if (!d->m_removedCollections.isEmpty()) + emit engine->collectionsRemoved(d->m_removedCollections.toList()); + if (!d->m_modifiedCollections.isEmpty()) + emit engine->collectionsModified(d->m_modifiedCollections); + + } +} + +QT_END_NAMESPACE_CONTACTS diff --git a/src/contacts/qcontactcollectionchangeset.h b/src/contacts/qcontactcollectionchangeset.h new file mode 100644 index 0000000000000000000000000000000000000000..8cb392cd8f3231bb4d2f89f1eed7fbeb88791ad3 --- /dev/null +++ b/src/contacts/qcontactcollectionchangeset.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTACTCOLLECTIONCHANGESET_H +#define QCONTACTCOLLECTIONCHANGESET_H + +#include <QtCore/qlist.h> +#include <QtCore/qset.h> +#include <QtCore/qshareddata.h> + +#include <QtContacts/qcontactcollectionid.h> +#include <QtContacts/qcontactmanager.h> + +QT_BEGIN_NAMESPACE_CONTACTS + +class QContactManagerEngine; + +class QContactCollectionChangeSetData; +class Q_CONTACTS_EXPORT QContactCollectionChangeSet +{ +public: + QContactCollectionChangeSet(); + QContactCollectionChangeSet(const QContactCollectionChangeSet &other); + ~QContactCollectionChangeSet(); + + QContactCollectionChangeSet &operator=(const QContactCollectionChangeSet &other); + + void setDataChanged(bool dataChanged); + bool dataChanged() const; + + QSet<QContactCollectionId> addedCollections() const; + void insertAddedCollection(const QContactCollectionId &collectionId); + void insertAddedCollections(const QList<QContactCollectionId> &collectionIds); + void clearAddedCollections(); + + QSet<QContactCollectionId> changedCollections() const; + void insertChangedCollection(const QContactCollectionId &collectionId); + void insertChangedCollections(const QList<QContactCollectionId> &collectionIds); + void clearChangedCollections(); + + QSet<QContactCollectionId> removedCollections() const; + void insertRemovedCollection(const QContactCollectionId &collectionId); + void insertRemovedCollections(const QList<QContactCollectionId> &collectionIds); + void clearRemovedCollections(); + + QList<QPair<QContactCollectionId, QContactManager::Operation> > modifiedCollections() const; + void clearModifiedCollections(); + + void clearAll(); + + void emitSignals(QContactManagerEngine *engine) const; + +private: + QSharedDataPointer<QContactCollectionChangeSetData> d; +}; + +QT_END_NAMESPACE_CONTACTS + +#endif // QCONTACTCOLLECTIONCHANGESET_H diff --git a/src/contacts/qcontactcollectionchangeset_p.h b/src/contacts/qcontactcollectionchangeset_p.h new file mode 100644 index 0000000000000000000000000000000000000000..b8e372f372394874b742bc8b091667135adf671a --- /dev/null +++ b/src/contacts/qcontactcollectionchangeset_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTACTCOLLECTIONCHANGESET_P_H +#define QCONTACTCOLLECTIONCHANGESET_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qlist.h> +#include <QtCore/qset.h> +#include <QtCore/qshareddata.h> + +#include <QtContacts/qcontactcollectionid.h> + +QT_BEGIN_NAMESPACE_CONTACTS + +class QContactCollectionChangeSetData : public QSharedData +{ +public: + QContactCollectionChangeSetData() + : QSharedData(), m_dataChanged(false) + { + } + + QContactCollectionChangeSetData(const QContactCollectionChangeSetData& other) + : QSharedData(other), + m_dataChanged(other.m_dataChanged), + m_addedCollections(other.m_addedCollections), + m_changedCollections(other.m_changedCollections), + m_removedCollections(other.m_removedCollections), + m_modifiedCollections(other.m_modifiedCollections) + { + } + + ~QContactCollectionChangeSetData() + { + } + + bool m_dataChanged; + QSet<QContactCollectionId> m_addedCollections; + QSet<QContactCollectionId> m_changedCollections; + QSet<QContactCollectionId> m_removedCollections; + QList<QPair<QContactCollectionId, QContactManager::Operation> > m_modifiedCollections; +}; + +QT_END_NAMESPACE_CONTACTS + +#endif // QCONTACTCOLLECTIONCHANGESET_P_H diff --git a/src/contacts/qcontactcollectionid.cpp b/src/contacts/qcontactcollectionid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..82b4f92437ab5b09d816e546a039b5f0d27e011b --- /dev/null +++ b/src/contacts/qcontactcollectionid.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcontactcollectionid.h" + +#ifndef QT_NO_DATASTREAM +#include <QtCore/qdatastream.h> +#endif +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> +#endif + +#include "qcontactmanager_p.h" + +QT_BEGIN_NAMESPACE_CONTACTS + +/*! + \class QContactCollectionId + \brief The QContactCollectionId class provides information that uniquely identifies a collection + in a particular manager. + \inmodule QtContact + \ingroup contact-main + + It consists of a manager URI which identifies the manager which contains the collection, + and the engine specific ID of the collection in that manager. + + An invalid QContactCollectionId has an empty manager URI. +*/ + +/*! + \fn QContactCollectionId::QContactCollectionId() + + Constructs a new, invalid collection ID. +*/ + +// TODO: Document and remove internal once the correct signature has been determined +/*! + \fn QContactCollectionId::QContactCollectionId(const QString &managerUri, const QByteArray &localId) + \internal + + Constructs an ID from the supplied manager URI \a managerUri and the engine + specific \a localId string. +*/ + +/*! + \fn bool QContactCollectionId::operator==(const QContactCollectionId &other) const + + Returns true if this collection ID has the same manager URI and + engine specific ID as \a other. Returns true also, if both IDs are null. +*/ + +/*! + \fn bool QContactCollectionId::operator!=(const QContactCollectionId &other) const + + Returns true if either the manager URI or engine specific ID of this + collection ID is different to that of \a other. +*/ + +/*! + \fn bool operator<(const QContactCollectionId &id1, const QContactCollectionId &id2) + \relates QContactCollectionId + + Returns true if the collection ID \a id1 will be considered less than + the collection ID \a id2 if the manager URI of ID \a id1 is alphabetically + less than the manager URI of the \a id2 ID. If both IDs have the same + manager URI, ID \a id1 will be considered less than the ID \a id2 + if the the engine specific ID of \a id1 is less than the engine specific ID of \a id2. + + The invalid, null collection ID consists of an empty manager URI and a null engine ID, + and hence will be less than any valid, non-null collection ID. + + This operator is provided primarily to allow use of a QContactCollectionId as a key in a QMap. +*/ + +/*! + \fn uint qHash(const QContactCollectionId &id) + \relates QContactCollectionId + + Returns the hash value for \a id. +*/ + +/*! + \fn bool QContactCollectionId::isValid() const + + Returns true if the manager URI part is not null; returns false otherwise. + + Note that valid ID may be null at the same time, which means new collection. + + \sa isNull() +*/ + +/*! + \fn bool QContactCollectionId::isNull() const + + Returns true if the engine specific ID part is a null (default constructed); + returns false otherwise. + + \sa isValid() +*/ + +/*! + \fn QString QContactCollectionId::managerUri() const + + Returns the URI of the manager which contains the collection identified by this ID. + + \sa localId() +*/ + +/*! + \fn QByteArray QContactCollectionId::localId() const + + Returns the collection's engine specific ID part. + + \sa managerUri() +*/ + +/*! + Serializes the collection ID to a string. The format of the string will be: + "qtorganizer:managerName:params:localId", where localId is encoded binary data + formatted as hexadecimal to ensure it is in a printable form. + + \sa fromString(), toByteArray() +*/ +QString QContactCollectionId::toString() const +{ + if (!isNull()) { + // Ensure the localId component has a valid string representation by hex encoding + const QByteArray encodedLocalId(m_localId.toHex()); + return QString::fromUtf8(QContactManagerData::buildIdData(m_managerUri, encodedLocalId)); + } + + return QString(); +} + +/*! + Deserializes the given \a idString. Returns a default-constructed (null) + collection ID if the given \a idString is not a valid, serialized collection ID. + + \sa toString(), fromByteArray() +*/ +QContactCollectionId QContactCollectionId::fromString(const QString &idString) +{ + QString managerUri; + QByteArray localId; + + if (QContactManagerData::parseIdData(idString.toUtf8(), 0, 0, &managerUri, &localId)) { + // The localId component must be decoded from hex + return QContactCollectionId(managerUri, QByteArray::fromHex(localId)); + } + + return QContactCollectionId(); +} + +/*! + Serializes the collection ID to a byte array. + + \sa fromByteArray(), toString() +*/ +QByteArray QContactCollectionId::toByteArray() const +{ + if (!isNull()) + return QContactManagerData::buildIdData(m_managerUri, m_localId); + + return QByteArray(); +} + +/*! + Deserializes the given \a idData. Returns a default-constructed (null) + collection ID if the given \a idData does not contain a valid, serialized collection ID. + + \sa toByteArray(), fromString() +*/ +QContactCollectionId QContactCollectionId::fromByteArray(const QByteArray &idData) +{ + QString managerUri; + QByteArray localId; + + if (QContactManagerData::parseIdData(idData, 0, 0, &managerUri, &localId)) + return QContactCollectionId(managerUri, localId); + + return QContactCollectionId(); +} + +#ifndef QT_NO_DEBUG_STREAM +/*! + \relates QContactCollectionId + Outputs \a id to the debug stream \a dbg. +*/ +QDebug operator<<(QDebug dbg, const QContactCollectionId &id) +{ + dbg.nospace() << "QContactCollectionId(" << id.toString().toUtf8().constData() << ")"; + return dbg.maybeSpace(); +} +#endif // QT_NO_DEBUG_STREAM + +#ifndef QT_NO_DATASTREAM +/*! + \relates QContactCollectionId + Streams \a id to the data stream \a out. +*/ +QDataStream &operator<<(QDataStream &out, const QContactCollectionId &id) +{ + out << id.toByteArray(); + return out; +} + +/*! + \relates QContactCollectionId + Streams \a id in from the data stream \a in. +*/ +QDataStream &operator>>(QDataStream &in, QContactCollectionId &id) +{ + QByteArray idData; + in >> idData; + id = QContactCollectionId::fromByteArray(idData); + return in; +} +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE_CONTACTS diff --git a/src/contacts/qcontactcollectionid.h b/src/contacts/qcontactcollectionid.h new file mode 100644 index 0000000000000000000000000000000000000000..8df82109137271904c5a19dd10e3dd7f3f32fc14 --- /dev/null +++ b/src/contacts/qcontactcollectionid.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTACTCOLLECTIONID_H +#define QCONTACTCOLLECTIONID_H + +#include <QtCore/qvariant.h> + +#include <QtContacts/qcontactsglobal.h> + +QT_BEGIN_NAMESPACE_CONTACTS + +class QContactManagerEngine; + +class Q_CONTACTS_EXPORT QContactCollectionId +{ +public: + inline QContactCollectionId() {} + inline QContactCollectionId(const QString &managerUri, const QByteArray &localId) + : m_managerUri(localId.isEmpty() ? QString() : managerUri), + m_localId(m_managerUri.isEmpty() ? QByteArray() : localId) + {} + // compiler-generated dtor and copy/move ctors/assignment operators are fine! + + inline bool operator==(const QContactCollectionId &other) const + { return localId() == other.localId() && managerUri() == other.managerUri(); } + inline bool operator!=(const QContactCollectionId &other) const + { return !operator==(other); } + + inline bool isNull() const { return m_localId.isEmpty(); } + + inline QString managerUri() const { return m_managerUri; } + inline QByteArray localId() const { return m_localId; } + + QString toString() const; + static QContactCollectionId fromString(const QString &idString); + + QByteArray toByteArray() const; + static QContactCollectionId fromByteArray(const QByteArray &idData); + +private: + QString m_managerUri; + QByteArray m_localId; +}; + +inline bool operator<(const QContactCollectionId &id1, const QContactCollectionId &id2) +{ return id1.managerUri() != id2.managerUri() ? id1.managerUri() < id2.managerUri() : id1.localId() < id2.localId(); } + +inline uint qHash(const QContactCollectionId &id) +{ return qHash(id.localId()); } + +#ifndef QT_NO_DATASTREAM +Q_CONTACTS_EXPORT QDataStream &operator<<(QDataStream &out, const QContactCollectionId &id); +Q_CONTACTS_EXPORT QDataStream &operator>>(QDataStream &in, QContactCollectionId &id); +#endif + +#ifndef QT_NO_DEBUG_STREAM +Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactCollectionId &id); +#endif + +QT_END_NAMESPACE_CONTACTS + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContactCollectionId), Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QTCONTACTS_PREPEND_NAMESPACE(QContactCollectionId)) + +#endif // QCONTACTCOLLECTIONID_H diff --git a/src/contacts/qcontactfilter.h b/src/contacts/qcontactfilter.h index fdcddf77f55817a23d95035eeb8eab619bff7271..50fb81c9c0d283336936f45bd2b84ff09ebc4d7b 100644 --- a/src/contacts/qcontactfilter.h +++ b/src/contacts/qcontactfilter.h @@ -74,7 +74,8 @@ public: IntersectionFilter, UnionFilter, IdFilter, - DefaultFilter + DefaultFilter, + CollectionFilter }; FilterType type() const; diff --git a/src/contacts/qcontactmanager.cpp b/src/contacts/qcontactmanager.cpp index 805d4203f7cd97e2b25c0e64d27f00cd16197156..2730d29e309b0677aa8546679142b15cf7568605 100644 --- a/src/contacts/qcontactmanager.cpp +++ b/src/contacts/qcontactmanager.cpp @@ -811,6 +811,74 @@ QList<QContactDetail::DetailType> QContactManager::supportedContactDetailTypes() return d->m_engine->supportedContactDetailTypes(); } +/*! + Returns the default collection managed by this manager. There is always only one default collection + for each backend. + */ +QContactCollection QContactManager::defaultCollection() +{ + QContactManagerSyncOpErrorHolder h(this); + return d->m_engine->defaultCollection(&h.error); +} + +/*! + Returns the collection identified by the given \a collectionId which is managed by this manager. + */ +QContactCollection QContactManager::collection(const QContactCollectionId& collectionId) +{ + QContactManagerSyncOpErrorHolder h(this); + return d->m_engine->collection(collectionId, &h.error); +} + +/*! + Returns a list of all of the collections managed by this manager. + */ +QList<QContactCollection> QContactManager::collections() +{ + QContactManagerSyncOpErrorHolder h(this); + return d->m_engine->collections(&h.error); +} + +/*! + Saves the given \a collection to the backend, and returns true on success or false otherwise. + + Note that certain backends may not allow modification nor adding new collections, in such cases + the operation will fail and result a QContactManager::PermissionsError error. + + A new collection will be created in the backend store if the collection ID of it is null. + Otherwise, an existing collection with the same ID will be updated. If the given collection ID + does not exist in the backend, it will result a QContactManager::DoesNotExistError error. + + Note that upon successful saving, the backend may update the collection, e.g. collection ID for + newly saved collections. + */ +bool QContactManager::saveCollection(QContactCollection* collection) +{ + QContactManagerSyncOpErrorHolder h(this); + if (collection) { + return d->m_engine->saveCollection(collection, &h.error); + } else { + h.error = QContactManager::BadArgumentError; + return false; + } +} + +/*! + Removes the collection identified by the given \a collectionId (and all items in the collection) + from the manager. Returns true on success, false on failure. + + If the given \a collectionId does not exist, the operation will fail and QContactManager::DoesNotExistError + will be returned when calling error(). + + If the given \a collectionId refers to the default collection, the operation will fail and + QContactManager::PermissionsError will be returned when calling error(). + */ +bool QContactManager::removeCollection(const QContactCollectionId &collectionId) +{ + QContactManagerSyncOpErrorHolder h(this); + return d->m_engine->removeCollection(collectionId, &h.error); +} + /*! Returns the engine backend implementation version number */ diff --git a/src/contacts/qcontactmanager.h b/src/contacts/qcontactmanager.h index 9ccf3e6caa32a9b5cbbb7866dedc81e8301de6bd..c50288ed1a64a30b3ebc0d4f8f634f6768204170 100644 --- a/src/contacts/qcontactmanager.h +++ b/src/contacts/qcontactmanager.h @@ -47,6 +47,7 @@ #include <QtCore/qstringlist.h> #include <QtContacts/qcontact.h> +#include <QtContacts/qcontactcollection.h> #include <QtContacts/qcontactid.h> #include <QtContacts/qcontactfetchhint.h> #include <QtContacts/qcontactrelationship.h> @@ -110,6 +111,12 @@ public: MissingPlatformRequirementsError }; + enum Operation { + Add, + Change, + Remove + }; + /* Error reporting */ QContactManager::Error error() const; QMap<int, QContactManager::Error> errorMap() const; @@ -150,6 +157,13 @@ public: QList<QContactType::TypeValues> supportedContactTypes() const; QList<QContactDetail::DetailType> supportedContactDetailTypes() const; + // collections + QContactCollection defaultCollection(); + QContactCollection collection(const QContactCollectionId& collectionId); + QList<QContactCollection> collections(); + bool saveCollection(QContactCollection* collection); + bool removeCollection(const QContactCollectionId& collectionId); + /* return a list of available backends for which a QContactManager can be constructed. */ static QStringList availableManagers(); @@ -161,6 +175,10 @@ Q_SIGNALS: void relationshipsAdded(const QList<QContactId>& affectedContactIds); void relationshipsRemoved(const QList<QContactId>& affectedContactIds); void selfContactIdChanged(const QContactId& oldId, const QContactId& newId); // need both? or just new? + void collectionsAdded(const QList<QContactCollectionId> &collectionIds); + void collectionsChanged(const QList<QContactCollectionId> &collectionIds); + void collectionsRemoved(const QList<QContactCollectionId> &collectionIds); + void collectionsModified(const QList<QPair<QContactCollectionId, QContactManager::Operation> > &collectionIds); protected: void connectNotify(const QMetaMethod &signal); diff --git a/src/contacts/qcontactmanagerengine.cpp b/src/contacts/qcontactmanagerengine.cpp index 5c41a499ce604bc4294060ab8f66bc12b2488546..ba2919d5fcfe8c02d9b98fc5cc2b2807ed5b02ee 100644 --- a/src/contacts/qcontactmanagerengine.cpp +++ b/src/contacts/qcontactmanagerengine.cpp @@ -451,6 +451,79 @@ bool QContactManagerEngine::removeRelationships(const QList<QContactRelationship return false; } +/*! + This function should be reimplemented to support synchronous calls to fetch the default collection. + Any errors encountered during this operation should be stored to \a error. +*/ +QContactCollection QContactManagerEngine::defaultCollection(QContactManager::Error* error) +{ + *error = QContactManager::NotSupportedError; + return QContactCollection(); +} + +/*! + This function should be reimplemented to support synchronous calls to fetch a collection based + on its ID. Any errors encountered during this operation should be stored to \a error. If the + given \a collectionId does not specify a valid collection, \a error will be set to + \c QContactManager::DoesNotExistError. + +*/ +QContactCollection QContactManagerEngine::collection(const QContactCollectionId& collectionId, QContactManager::Error* error) +{ + Q_UNUSED(collectionId); + *error = QContactManager::NotSupportedError; + return QContactCollection(); +} + +/*! + This function should be reimplemented to support synchronous calls to fetch all the collections + managed by this backend. Any errors encountered during this operation should be stored to \a error. + */ +QList<QContactCollection> QContactManagerEngine::collections(QContactManager::Error* error) +{ + *error = QContactManager::NotSupportedError; + return QList<QContactCollection>(); +} + +/*! + This function should be reimplemented to support synchronous calls to save a collection. + + This function is supposed to save the given \a collection to the backend, and returns true on + success or false otherwise. Any errors encountered during this operation should be stored to + \a error. + + A new collection will be created in the backend store if the collection ID of it is null. + Otherwise, an existing collection with the same ID will be updated. If the given collection ID + does not exist in the backend, it will result a QContactManager::DoesNotExistError error. + + Note that upon successful saving, the backend may update the collection, e.g. collection ID for + newly saved collections. +*/ +bool QContactManagerEngine::saveCollection(QContactCollection* collection, QContactManager::Error* error) +{ + Q_UNUSED(collection); + + *error = QContactManager::NotSupportedError; + return false; +} + +/*! + This function should be reimplemented to support synchronous calls to remove a collection. + + This function is supposed to remove the collection identified by the given \a collectionId, and + all items in the collection. Returns true on success, or false otherwise. Any errors encountered + during this operation should be stored to \a error. + + Note that removing the default collection should not be allowed and should result a + QContactManager::PermissionsError error. +*/ +bool QContactManagerEngine::removeCollection(const QContactCollectionId& collectionId, QContactManager::Error* error) +{ + Q_UNUSED(collectionId); + + *error = QContactManager::NotSupportedError; + return false; +} /*! Given an input \a filter, returns the canonical version of the filter. @@ -1416,6 +1489,15 @@ bool QContactManagerEngine::testFilter(const QContactFilter &filter, const QCont // Fall through to end } break; + + case QContactFilter::CollectionFilter: + { + const QContactCollectionFilter cf(filter); + const QSet<QContactCollectionId>& ids = cf.collectionIds(); + if (ids.contains(contact.collectionId())) + return true; + return false; + } } return false; } @@ -1921,6 +2003,116 @@ void QContactManagerEngine::updateRelationshipFetchRequest(QContactRelationshipF #endif } +/*! + Updates the given QContactCollectionFetchRequest \a req with the latest results \a result and an operation error \a error. + In addition, the state of the request will be changed to \a newState. + + It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. + If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. + */ +void QContactManagerEngine::updateCollectionFetchRequest(QContactCollectionFetchRequest* req, const QList<QContactCollection>& result, QContactManager::Error error, QContactAbstractRequest::State newState) +{ + Q_ASSERT(req); + QContactCollectionFetchRequestPrivate* rd = static_cast<QContactCollectionFetchRequestPrivate*>(req->d_ptr); + QMutexLocker ml(&rd->m_mutex); + bool emitState = rd->m_state != newState; + rd->m_collections = result; + rd->m_error = error; + rd->m_state = newState; + ml.unlock(); +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + QPointer<QContactAbstractRequest> guard(req); +#endif + Qt::ConnectionType connectionType = Qt::DirectConnection; +#ifdef QT_NO_THREAD + if (req->thread() != QThread::currentThread()) + connectionType = Qt::BlockingQueuedConnection; +#endif + QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(guard); +#endif + if (emitState) + QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(guard); +#endif +} + +/*! + Updates the given QContactCollectionRemoveRequest \a req with the operation error \a error, and map of input index to individual error \a errorMap. + In addition, the state of the request will be changed to \a newState. + + It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. + If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. + */ +void QContactManagerEngine::updateCollectionRemoveRequest(QContactCollectionRemoveRequest* req, QContactManager::Error error, const QMap<int, QContactManager::Error>& errorMap, QContactAbstractRequest::State newState) +{ + Q_ASSERT(req); + QContactCollectionRemoveRequestPrivate* rd = static_cast<QContactCollectionRemoveRequestPrivate*>(req->d_ptr); + QMutexLocker ml(&rd->m_mutex); + bool emitState = rd->m_state != newState; + rd->m_errors = errorMap; + rd->m_error = error; + rd->m_state = newState; + ml.unlock(); +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + QPointer<QContactAbstractRequest> guard(req); +#endif + Qt::ConnectionType connectionType = Qt::DirectConnection; +#ifdef QT_NO_THREAD + if (req->thread() != QThread::currentThread()) + connectionType = Qt::BlockingQueuedConnection; +#endif + QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(guard); +#endif + if (emitState) + QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(guard); +#endif +} + +/*! + Updates the given QContactCollectionSaveRequest \a req with the latest results \a result, operation error \a error, and map of input index to individual error \a errorMap. + In addition, the state of the request will be changed to \a newState. + + It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. + If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. + */ +void QContactManagerEngine::updateCollectionSaveRequest(QContactCollectionSaveRequest* req, const QList<QContactCollection>& result, QContactManager::Error error, const QMap<int, QContactManager::Error>& errorMap, QContactAbstractRequest::State newState) +{ + Q_ASSERT(req); + QContactCollectionSaveRequestPrivate* rd = static_cast<QContactCollectionSaveRequestPrivate*>(req->d_ptr); + QMutexLocker ml(&rd->m_mutex); + bool emitState = rd->m_state != newState; + rd->m_collections = result; + rd->m_errors = errorMap; + rd->m_error = error; + rd->m_state = newState; + ml.unlock(); +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + QPointer<QContactAbstractRequest> guard(req); +#endif + Qt::ConnectionType connectionType = Qt::DirectConnection; +#ifdef QT_NO_THREAD + if (req->thread() != QThread::currentThread()) + connectionType = Qt::BlockingQueuedConnection; +#endif + QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(guard); +#endif + if (emitState) + QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + Q_ASSERT(guard); +#endif +} + + /*! For each contact in \a contacts, either add it to the database or update an existing one. diff --git a/src/contacts/qcontactmanagerengine.h b/src/contacts/qcontactmanagerengine.h index c438772d7c255843001c6c946088651853ef486b..146b4a48d1cec29983d5d890a9c77ebdea38b1f3 100644 --- a/src/contacts/qcontactmanagerengine.h +++ b/src/contacts/qcontactmanagerengine.h @@ -56,6 +56,9 @@ #include <QtContacts/qcontactmanager.h> #include <QtContacts/qcontactrequests.h> #include <QtContacts/qcontactsortorder.h> +#include <QtContacts/qcontactcollectionfetchrequest.h> +#include <QtContacts/qcontactcollectionsaverequest.h> +#include <QtContacts/qcontactcollectionremoverequest.h> QT_BEGIN_NAMESPACE_CONTACTS @@ -77,6 +80,8 @@ public: inline QContactId contactId(const QByteArray &localId) const { return QContactId(managerUri(), localId); } + inline QContactCollectionId collectionId(const QByteArray &localId) const + { return QContactCollectionId(managerUri(), localId); } /* Filtering */ virtual QList<QContactId> contactIds(const QContactFilter &filter, const QList<QContactSortOrder> &sortOrders, QContactManager::Error *error) const; @@ -102,6 +107,13 @@ public: virtual bool saveRelationships(QList<QContactRelationship> *relationships, QMap<int, QContactManager::Error>* errorMap, QContactManager::Error *error); virtual bool removeRelationships(const QList<QContactRelationship> &relationships, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error *error); + // collections + virtual QContactCollection defaultCollection(QContactManager::Error *error); + virtual QContactCollection collection(const QContactCollectionId &collectionId, QContactManager::Error *error); + virtual QList<QContactCollection> collections(QContactManager::Error *error); + virtual bool saveCollection(QContactCollection *collection, QContactManager::Error *error); + virtual bool removeCollection(const QContactCollectionId &collectionId, QContactManager::Error *error); + /* Validation for saving */ virtual bool validateContact(const QContact &contact, QContactManager::Error *error) const; @@ -126,6 +138,10 @@ Q_SIGNALS: void relationshipsAdded(const QList<QContactId> &affectedContactIds); void relationshipsRemoved(const QList<QContactId> &affectedContactIds); void selfContactIdChanged(const QContactId &oldId, const QContactId &newId); + void collectionsAdded(const QList<QContactCollectionId> &collectionIds); + void collectionsChanged(const QList<QContactCollectionId> &collectionIds); + void collectionsRemoved(const QList<QContactCollectionId> &collectionIds); + void collectionsModified(const QList<QPair<QContactCollectionId, QContactManager::Operation> > &collectionIds); public: // Async update functions @@ -140,6 +156,11 @@ public: static void updateRelationshipRemoveRequest(QContactRelationshipRemoveRequest *req, QContactManager::Error error, const QMap<int, QContactManager::Error> &errorMap, QContactAbstractRequest::State); static void updateRelationshipFetchRequest(QContactRelationshipFetchRequest *req, const QList<QContactRelationship> &result, QContactManager::Error error, QContactAbstractRequest::State); + // collections + static void updateCollectionFetchRequest(QContactCollectionFetchRequest *request, const QList<QContactCollection> &result, QContactManager::Error error, QContactAbstractRequest::State newState); + static void updateCollectionRemoveRequest(QContactCollectionRemoveRequest *request, QContactManager::Error error, const QMap<int, QContactManager::Error> &errorMap, QContactAbstractRequest::State newState); + static void updateCollectionSaveRequest(QContactCollectionSaveRequest *request, const QList<QContactCollection> &result, QContactManager::Error error, const QMap<int, QContactManager::Error> &errorMap, QContactAbstractRequest::State newState); + // Other protected area update functions static void setDetailAccessConstraints(QContactDetail *detail, QContactDetail::AccessConstraints constraints); static void setContactRelationships(QContact *contact, const QList<QContactRelationship> &relationships); diff --git a/src/contacts/requests/qcontactcollectionfetchrequest.cpp b/src/contacts/requests/qcontactcollectionfetchrequest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..adf09a6a340d4ed7ddf627f0eed31128b16bca47 --- /dev/null +++ b/src/contacts/requests/qcontactcollectionfetchrequest.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcontactcollectionfetchrequest.h" + +#include "qcontactrequests_p.h" + +QT_BEGIN_NAMESPACE_CONTACTS + +/*! + \class QContactCollectionFetchRequest + \brief The QContactCollectionFetchRequest class allows a client to asynchronously fetch collections + from a backend. + \inmodule QtContacts + \ingroup contacts-requests + + This request will fetch all the collections stored in the given backend. + */ + +/*! + Constructs a new contact fetch request whose parent is the specified \a parent. +*/ +QContactCollectionFetchRequest::QContactCollectionFetchRequest(QObject *parent) + : QContactAbstractRequest(new QContactCollectionFetchRequestPrivate, parent) +{ +} + +/*! + Frees memory in use by this request. +*/ +QContactCollectionFetchRequest::~QContactCollectionFetchRequest() +{ +} + +/*! + Returns the collections retrieved by this request. +*/ +QList<QContactCollection> QContactCollectionFetchRequest::collections() const +{ + Q_D(const QContactCollectionFetchRequest); + QMutexLocker ml(&d->m_mutex); + return d->m_collections; +} + +#include "moc_qcontactcollectionfetchrequest.cpp" + +QT_END_NAMESPACE_CONTACTS diff --git a/src/contacts/requests/qcontactcollectionfetchrequest.h b/src/contacts/requests/qcontactcollectionfetchrequest.h new file mode 100644 index 0000000000000000000000000000000000000000..db08499f513dcfc5fc8349541361438d5ab415ea --- /dev/null +++ b/src/contacts/requests/qcontactcollectionfetchrequest.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTACTCOLLECTIONFETCHREQUEST_H +#define QCONTACTCOLLECTIONFETCHREQUEST_H + +#include <QtCore/qlist.h> + +#include <QtContacts/qcontactabstractrequest.h> +#include <QtContacts/qcontactcollection.h> + +QT_BEGIN_NAMESPACE_CONTACTS + +class QContactCollectionFetchRequestPrivate; + +/* Leaf class */ + +class Q_CONTACTS_EXPORT QContactCollectionFetchRequest : public QContactAbstractRequest +{ + Q_OBJECT + +public: + QContactCollectionFetchRequest(QObject *parent = 0); + ~QContactCollectionFetchRequest(); + + QList<QContactCollection> collections() const; + +private: + Q_DISABLE_COPY(QContactCollectionFetchRequest) + friend class QContactManagerEngine; + Q_DECLARE_PRIVATE_D(d_ptr, QContactCollectionFetchRequest) +}; + +QT_END_NAMESPACE_CONTACTS + +#endif // QCONTACTCOLLECTIONFETCHREQUEST_H diff --git a/src/contacts/requests/qcontactcollectionremoverequest.cpp b/src/contacts/requests/qcontactcollectionremoverequest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9bd3a03ade2e678b398fdfe240d8d92720dacb0a --- /dev/null +++ b/src/contacts/requests/qcontactcollectionremoverequest.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcontactcollectionremoverequest.h" + +#include "qcontactrequests_p.h" + +QT_BEGIN_NAMESPACE_CONTACTS + +/*! + \class QContactCollectionRemoveRequest + \brief The QContactCollectionRemoveRequest class allows a client to asynchronously remove + collections from a backend. + \inmodule QtContacts + \ingroup contact-requests + */ + +/*! + Constructs a new collection remove request whose parent is the specified \a parent. +*/ +QContactCollectionRemoveRequest::QContactCollectionRemoveRequest(QObject *parent) + : QContactAbstractRequest(new QContactCollectionRemoveRequestPrivate, parent) +{ +} + +/*! + Frees memory in use by this request. +*/ +QContactCollectionRemoveRequest::~QContactCollectionRemoveRequest() +{ +} + +/*! + Sets the ID of collection which will be removed by this request to \a collectionId. +*/ +void QContactCollectionRemoveRequest::setCollectionId(const QContactCollectionId &collectionId) +{ + Q_D(QContactCollectionRemoveRequest); + QMutexLocker ml(&d->m_mutex); + d->m_collectionIds.clear(); + d->m_collectionIds.append(collectionId); +} + +/*! + Sets the list of IDs of collections which will be removed by this request to \a collectionIds. +*/ +void QContactCollectionRemoveRequest::setCollectionIds(const QList<QContactCollectionId> &collectionIds) +{ + Q_D(QContactCollectionRemoveRequest); + QMutexLocker ml(&d->m_mutex); + d->m_collectionIds = collectionIds; +} + +/*! + Returns the list of IDs of collections which will be removed by this request. +*/ +QList<QContactCollectionId> QContactCollectionRemoveRequest::collectionIds() const +{ + Q_D(const QContactCollectionRemoveRequest); + QMutexLocker ml(&d->m_mutex); + return d->m_collectionIds; +} + +/*! + Returns any errors which occurred during the request. +*/ +QMap<int, QContactManager::Error> QContactCollectionRemoveRequest::errorMap() const +{ + Q_D(const QContactCollectionRemoveRequest); + QMutexLocker ml(&d->m_mutex); + return d->m_errors; +} + +#include "moc_qcontactcollectionremoverequest.cpp" + +QT_END_NAMESPACE_CONTACTS diff --git a/src/contacts/requests/qcontactcollectionremoverequest.h b/src/contacts/requests/qcontactcollectionremoverequest.h new file mode 100644 index 0000000000000000000000000000000000000000..54b2e75380dc31a7aaad4809a52ce7b66cbcf997 --- /dev/null +++ b/src/contacts/requests/qcontactcollectionremoverequest.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTACTCOLLECTIONREMOVEREQUEST_H +#define QCONTACTCOLLECTIONREMOVEREQUEST_H + +#include <QtCore/qlist.h> +#include <QtCore/qmap.h> + +#include <QtContacts/qcontactabstractrequest.h> +#include <QtContacts/qcontactcollectionid.h> + +QT_BEGIN_NAMESPACE_CONTACTS + +class QContactCollectionRemoveRequestPrivate; + +/* Leaf class */ + +class Q_CONTACTS_EXPORT QContactCollectionRemoveRequest : public QContactAbstractRequest +{ + Q_OBJECT + +public: + QContactCollectionRemoveRequest(QObject *parent = 0); + ~QContactCollectionRemoveRequest(); + + void setCollectionId(const QContactCollectionId &collectionId); + void setCollectionIds(const QList<QContactCollectionId> &collectionIds); + QList<QContactCollectionId> collectionIds() const; + + QMap<int, QContactManager::Error> errorMap() const; + +private: + Q_DISABLE_COPY(QContactCollectionRemoveRequest) + friend class QOrganizerManagerEngine; + Q_DECLARE_PRIVATE_D(d_ptr, QContactCollectionRemoveRequest) +}; + +QT_END_NAMESPACE_CONTACTS + +#endif // QCONTACTCOLLECTIONREMOVEREQUEST_H diff --git a/src/contacts/requests/qcontactcollectionsaverequest.cpp b/src/contacts/requests/qcontactcollectionsaverequest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..39877f7c9c628b9ec64a2c13b8e3bcf02885dcec --- /dev/null +++ b/src/contacts/requests/qcontactcollectionsaverequest.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcontactcollectionsaverequest.h" + +#include "qcontactrequests_p.h" + +QT_BEGIN_NAMESPACE_CONTACTS + +/*! + \class QContactCollectionSaveRequest + \brief The QContactCollectionSaveRequest class allows a client to asynchronously save collections + to a backend. + \inmodule QtContacts + \ingroup contact-requests + */ + +/*! + Constructs a new collection save request whose parent is the specified \a parent. +*/ +QContactCollectionSaveRequest::QContactCollectionSaveRequest(QObject *parent) + : QContactAbstractRequest(new QContactCollectionSaveRequestPrivate, parent) +{ +} + +/*! + Frees memory in use by this request. +*/ +QContactCollectionSaveRequest::~QContactCollectionSaveRequest() +{ +} + +/*! + Sets the collection which will be saved to \a collection. +*/ +void QContactCollectionSaveRequest::setCollection(const QContactCollection &collection) +{ + Q_D(QContactCollectionSaveRequest); + QMutexLocker ml(&d->m_mutex); + d->m_collections.clear(); + d->m_collections.append(collection); +} + +/*! + Sets the list of collections which will be saved to \a collections. +*/ +void QContactCollectionSaveRequest::setCollections(const QList<QContactCollection> &collections) +{ + Q_D(QContactCollectionSaveRequest); + QMutexLocker ml(&d->m_mutex); + d->m_collections = collections; +} + +/*! + Returns the collections which will be saved by this request if called prior to calling start(), + otherwise returns the (possibly updated) collections which have been saved. +*/ +QList<QContactCollection> QContactCollectionSaveRequest::collections() const +{ + Q_D(const QContactCollectionSaveRequest); + QMutexLocker ml(&d->m_mutex); + return d->m_collections; +} + +/*! + Returns the map of input definition list indices to errors which occurred. +*/ +QMap<int, QContactManager::Error> QContactCollectionSaveRequest::errorMap() const +{ + Q_D(const QContactCollectionSaveRequest); + QMutexLocker ml(&d->m_mutex); + return d->m_errors; +} + +#include "moc_qcontactcollectionsaverequest.cpp" + +QT_END_NAMESPACE_CONTACTS diff --git a/src/contacts/requests/qcontactcollectionsaverequest.h b/src/contacts/requests/qcontactcollectionsaverequest.h new file mode 100644 index 0000000000000000000000000000000000000000..287157c5ea499294e86605f335ae2e266d95ae9c --- /dev/null +++ b/src/contacts/requests/qcontactcollectionsaverequest.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtOrganizer module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTACTCOLLECTIONSAVEREQUEST_H +#define QCONTACTCOLLECTIONSAVEREQUEST_H + +#include <QtCore/qlist.h> +#include <QtCore/qmap.h> + +#include <QtContacts/qcontactabstractrequest.h> +#include <QtContacts/qcontactcollection.h> + +QT_BEGIN_NAMESPACE_CONTACTS + +class QContactCollectionSaveRequestPrivate; + +/* Leaf class */ + +class Q_CONTACTS_EXPORT QContactCollectionSaveRequest : public QContactAbstractRequest +{ + Q_OBJECT + +public: + QContactCollectionSaveRequest(QObject *parent = 0); + ~QContactCollectionSaveRequest(); + + void setCollection(const QContactCollection &collection); + void setCollections(const QList<QContactCollection> &collections); + QList<QContactCollection> collections() const; + + QMap<int, QContactManager::Error> errorMap() const; + +private: + Q_DISABLE_COPY(QContactCollectionSaveRequest) + friend class QContactManagerEngine; + Q_DECLARE_PRIVATE_D(d_ptr, QContactCollectionSaveRequest) +}; + +QT_END_NAMESPACE_CONTACTS + +#endif // QCONTACTCOLLECTIONSAVEREQUEST_H diff --git a/src/contacts/requests/qcontactrequests.h b/src/contacts/requests/qcontactrequests.h index d976eecfdcfab39d29c6f0afc57e02817c5754de..778942c1ef54091537ad5e4da17caa5c24525b7e 100644 --- a/src/contacts/requests/qcontactrequests.h +++ b/src/contacts/requests/qcontactrequests.h @@ -55,6 +55,11 @@ #include <QtContacts/qcontactremoverequest.h> #include <QtContacts/qcontactsaverequest.h> +#include <QtContacts/qcontactcollectionchangeset.h> +#include <QtContacts/qcontactcollectionfetchrequest.h> +#include <QtContacts/qcontactcollectionremoverequest.h> +#include <QtContacts/qcontactcollectionsaverequest.h> + QT_BEGIN_NAMESPACE_CONTACTS QT_END_NAMESPACE_CONTACTS diff --git a/src/contacts/requests/qcontactrequests_p.h b/src/contacts/requests/qcontactrequests_p.h index f2aae7c5fa9d0d7e8c01c81c99571abcc2152c3f..6ea90fdd57f34ba1db4c87125917f994c32750b1 100644 --- a/src/contacts/requests/qcontactrequests_p.h +++ b/src/contacts/requests/qcontactrequests_p.h @@ -306,6 +306,92 @@ public: QMap<int, QContactManager::Error> m_errors; }; +class QContactCollectionFetchRequestPrivate : public QContactAbstractRequestPrivate +{ +public: + QContactCollectionFetchRequestPrivate() + : QContactAbstractRequestPrivate(QContactAbstractRequest::CollectionFetchRequest) + { + } + + ~QContactCollectionFetchRequestPrivate() + { + } + +#ifndef QT_NO_DEBUG_STREAM + QDebug& debugStreamOut(QDebug &dbg) const + { + dbg.nospace() << "QContactCollectionFetchRequestPrivate("; + dbg.nospace() << "collections="; + dbg.nospace() << m_collections; + dbg.nospace() << ")"; + return dbg.maybeSpace(); + } +#endif + + QList<QContactCollection> m_collections; +}; + +class QContactCollectionRemoveRequestPrivate : public QContactAbstractRequestPrivate +{ +public: + QContactCollectionRemoveRequestPrivate() + : QContactAbstractRequestPrivate(QContactAbstractRequest::CollectionRemoveRequest) + { + } + + ~QContactCollectionRemoveRequestPrivate() + { + } + +#ifndef QT_NO_DEBUG_STREAM + QDebug& debugStreamOut(QDebug &dbg) const + { + dbg.nospace() << "QContactCollectionRemoveRequestPrivate("; + dbg.nospace() << "collectionIds="; + dbg.nospace() << m_collectionIds; + dbg.nospace() << ","; + dbg.nospace() << "errorMap="; + dbg.nospace() << m_errors; + dbg.nospace() << ")"; + return dbg.maybeSpace(); + } +#endif + + QList<QContactCollectionId> m_collectionIds; + QMap<int, QContactManager::Error> m_errors; +}; + +class QContactCollectionSaveRequestPrivate : public QContactAbstractRequestPrivate +{ +public: + QContactCollectionSaveRequestPrivate() + : QContactAbstractRequestPrivate(QContactAbstractRequest::CollectionSaveRequest) + { + } + + ~QContactCollectionSaveRequestPrivate() + { + } + +#ifndef QT_NO_DEBUG_STREAM + QDebug& debugStreamOut(QDebug &dbg) const + { + dbg.nospace() << "QContactCollectionSaveRequestPrivate("; + dbg.nospace() << "collections="; + dbg.nospace() << m_collections; + dbg.nospace() << ","; + dbg.nospace() << "errorMap="; + dbg.nospace() << m_errors; + dbg.nospace() << ")"; + return dbg.maybeSpace(); + } +#endif + + QList<QContactCollection> m_collections; + QMap<int, QContactManager::Error> m_errors; +}; + QT_END_NAMESPACE_CONTACTS #endif // QCONTACTREQUESTS_P_H diff --git a/src/contacts/requests/requests.pri b/src/contacts/requests/requests.pri index a8efb65b19abe1f4794ab421d55d81cc0cd3001c..d5c9dff03b7f14724a0e3e191bc05cacbbd09e58 100644 --- a/src/contacts/requests/requests.pri +++ b/src/contacts/requests/requests.pri @@ -1,6 +1,9 @@ INCLUDEPATH += requests PUBLIC_HEADERS += \ + requests/qcontactcollectionfetchrequest.h \ + requests/qcontactcollectionremoverequest.h \ + requests/qcontactcollectionsaverequest.h \ requests/qcontactfetchrequest.h \ requests/qcontactfetchbyidrequest.h \ requests/qcontactidfetchrequest.h \ @@ -15,6 +18,9 @@ PRIVATE_HEADERS += \ requests/qcontactrequests_p.h SOURCES += \ + requests/qcontactcollectionfetchrequest.cpp \ + requests/qcontactcollectionremoverequest.cpp \ + requests/qcontactcollectionsaverequest.cpp \ requests/qcontactfetchrequest.cpp \ requests/qcontactfetchbyidrequest.cpp \ requests/qcontactidfetchrequest.cpp \ diff --git a/src/imports/contacts/contacts.pro b/src/imports/contacts/contacts.pro index b9b3c6a29fa00f0f62b5c530c2cce45c3c0d85c6..a26bb82653afc7e3dbecaa685dab4bd76dda3aa6 100644 --- a/src/imports/contacts/contacts.pro +++ b/src/imports/contacts/contacts.pro @@ -5,6 +5,7 @@ include(filters/filters.pri) HEADERS += qdeclarativecontactmodel_p.h \ qdeclarativecontact_p.h \ + qdeclarativecontactcollection_p.h \ qdeclarativecontactdetail_p.h \ qdeclarativecontactfilter_p.h \ qdeclarativecontactsortorder_p.h \ @@ -15,6 +16,7 @@ HEADERS += qdeclarativecontactmodel_p.h \ SOURCES += plugin.cpp \ qdeclarativecontactmodel.cpp \ qdeclarativecontact.cpp \ + qdeclarativecontactcollection.cpp \ qdeclarativecontactdetail.cpp \ qdeclarativecontactfilter.cpp \ qdeclarativecontactsortorder.cpp \ diff --git a/src/imports/contacts/filters/filters.pri b/src/imports/contacts/filters/filters.pri index 63bf1e468b7e048784859d01ca561fb4191131ef..2bb4738e9b609b4084dcb6030ab657b7817e0715 100644 --- a/src/imports/contacts/filters/filters.pri +++ b/src/imports/contacts/filters/filters.pri @@ -3,6 +3,7 @@ INCLUDEPATH += filters HEADERS += \ filters/qdeclarativecontactactionfilter_p.h \ filters/qdeclarativecontactchangelogfilter_p.h \ + filters/qdeclarativecontactcollectionfilter_p.h \ filters/qdeclarativecontactdetailfilter_p.h \ filters/qdeclarativecontactdetailrangefilter_p.h \ filters/qdeclarativecontactidfilter_p.h \ diff --git a/src/imports/contacts/filters/qdeclarativecontactcollectionfilter_p.h b/src/imports/contacts/filters/qdeclarativecontactcollectionfilter_p.h new file mode 100644 index 0000000000000000000000000000000000000000..3f523db16a20d0189c238e24c856bca651b16289 --- /dev/null +++ b/src/imports/contacts/filters/qdeclarativecontactcollectionfilter_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtContacts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONTACTCOLLECTIONFILTER_H +#define QDECLARATIVECONTACTCOLLECTIONFILTER_H + +#include <QtContacts/qcontactcollectionfilter.h> + +#include "qdeclarativecontactfilter_p.h" + +QTCONTACTS_USE_NAMESPACE + +QT_BEGIN_NAMESPACE + +class QDeclarativeContactCollectionFilter : public QDeclarativeContactFilter +{ + Q_OBJECT + Q_PROPERTY(QStringList ids READ ids WRITE setIds NOTIFY valueChanged) + +public: + QDeclarativeContactCollectionFilter(QObject *parent = 0) + : QDeclarativeContactFilter(parent) + { + connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); + } + + QStringList ids() const + { + return m_ids; + } + + void setIds(const QStringList &ids) + { + foreach (const QString &id, ids) { + if (!m_ids.contains(id)) { + m_ids = ids; + emit valueChanged(); + return; + } + } + + foreach (const QString &id, m_ids) { + if (!ids.contains(id)) { + m_ids = ids; + emit valueChanged(); + return; + } + } + } + + // used by model + QContactFilter filter() const + { + QContactCollectionFilter f; + QSet<QContactCollectionId> ids; + + foreach (const QVariant &id, m_ids) { + QContactCollectionId cId = QContactCollectionId::fromString(id.toString()); + if (!cId.isNull()) + ids << cId; + } + + f.setCollectionIds(ids); + return f; + } + +Q_SIGNALS: + void valueChanged(); + +private: + QStringList m_ids; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeContactCollectionFilter) + +#endif // QDECLARATIVECONTACTCOLLECTIONFILTER_H diff --git a/src/imports/contacts/filters/qdeclarativecontactfiltermoc.cpp b/src/imports/contacts/filters/qdeclarativecontactfiltermoc.cpp index 9a83d1d03bdfb917cbe6c6f21493c9a7f3b6dbf5..c7db6f83c6e9b9411d601f4e6a8386089225ee78 100644 --- a/src/imports/contacts/filters/qdeclarativecontactfiltermoc.cpp +++ b/src/imports/contacts/filters/qdeclarativecontactfiltermoc.cpp @@ -45,6 +45,7 @@ QT_BEGIN_NAMESPACE #include "moc_qdeclarativecontactactionfilter_p.cpp" #include "moc_qdeclarativecontactchangelogfilter_p.cpp" +#include "moc_qdeclarativecontactcollectionfilter_p.cpp" #include "moc_qdeclarativecontactdetailfilter_p.cpp" #include "moc_qdeclarativecontactdetailrangefilter_p.cpp" #include "moc_qdeclarativecontactintersectionfilter_p.cpp" diff --git a/src/imports/contacts/filters/qdeclarativecontactfilters_p.h b/src/imports/contacts/filters/qdeclarativecontactfilters_p.h index c8039396504a6608385f69755084b3c065bf72eb..a073715b92a2625479e2d4b347210a4f9f7037ef 100644 --- a/src/imports/contacts/filters/qdeclarativecontactfilters_p.h +++ b/src/imports/contacts/filters/qdeclarativecontactfilters_p.h @@ -47,6 +47,7 @@ #include "qdeclarativecontactactionfilter_p.h" #include "qdeclarativecontactchangelogfilter_p.h" +#include "qdeclarativecontactcollectionfilter_p.h" #include "qdeclarativecontactdetailfilter_p.h" #include "qdeclarativecontactdetailrangefilter_p.h" #include "qdeclarativecontactidfilter_p.h" diff --git a/src/imports/contacts/plugin.cpp b/src/imports/contacts/plugin.cpp index 0b3902e8039436c68b3405f0e95cbd43b6e56e80..1060eae341b4ba75268f6817276491fd249c3db6 100644 --- a/src/imports/contacts/plugin.cpp +++ b/src/imports/contacts/plugin.cpp @@ -46,6 +46,7 @@ #include <QtContacts/qcontactabstractrequest.h> #include "qdeclarativecontact_p.h" +#include "qdeclarativecontactcollection_p.h" #include "qdeclarativecontactdetail_p.h" #include "qdeclarativecontactfetchhint_p.h" #include "qdeclarativecontactfilter_p.h" @@ -73,8 +74,10 @@ public: qRegisterMetaType<QContactAbstractRequest::State>("QContactAbstractRequest::State"); qRegisterMetaType<QContactId>("QContactId"); qRegisterMetaType<QList<QContactId> >("QList<QContactId>"); + qRegisterMetaType<QList<QContactCollectionId> >("QList<QContactCollectionId>"); qmlRegisterType<QDeclarativeContactModel>(uri, major, minor, "ContactModel"); qmlRegisterType<QDeclarativeContact>(uri, major, minor, "Contact"); + qmlRegisterType<QDeclarativeContactCollection>(uri, major, minor, "Collection"); qmlRegisterType<QDeclarativeContactFetchHint>(uri, major, minor, "FetchHint"); qmlRegisterType<QDeclarativeContactRelationshipModel>(uri, major, minor, "RelationshipModel"); qmlRegisterType<QDeclarativeContactRelationship>(uri, major, minor, "Relationship"); @@ -115,6 +118,7 @@ public: qmlRegisterType<QDeclarativeContactFilter>(uri, major, minor, "Filter"); qmlRegisterType<QDeclarativeContactActionFilter>(uri, major, minor, "ActionFilter"); qmlRegisterType<QDeclarativeContactChangeLogFilter>(uri, major, minor, "ChangeLogFilter"); + qmlRegisterType<QDeclarativeContactCollectionFilter>(uri, major, minor, "CollectionFilter"); qmlRegisterType<QDeclarativeContactDetailFilter>(uri, major, minor, "DetailFilter"); qmlRegisterType<QDeclarativeContactDetailRangeFilter>(uri, major, minor, "DetailRangeFilter"); qmlRegisterType<QDeclarativeContactIdFilter>(uri, major, minor, "IdFilter"); diff --git a/src/imports/contacts/qdeclarativecontact.cpp b/src/imports/contacts/qdeclarativecontact.cpp index 19b5abedd2298e1e818e1e5fb35440f8a7f03c9c..dc05ae6906a9e95c383cfa0e53392bdfbb3a0318 100644 --- a/src/imports/contacts/qdeclarativecontact.cpp +++ b/src/imports/contacts/qdeclarativecontact.cpp @@ -125,6 +125,7 @@ QDeclarativeContact::~QDeclarativeContact() void QDeclarativeContact::setContact(const QContact& contact) { m_id = contact.id(); + m_collectionId = contact.collectionId(); foreach (QDeclarativeContactDetail *detail, m_details) delete detail; m_details.clear(); @@ -154,6 +155,7 @@ QContact QDeclarativeContact::contact() const { QContact contact; contact.setId(m_id); + contact.setCollectionId(m_collectionId); foreach (QDeclarativeContactDetail *detail, m_details) contact.saveDetail(&detail->detail()); @@ -353,6 +355,29 @@ QVariantMap QDeclarativeContact::preferredDetails() const return result; } +/*! + \qmlproperty string OContact::collectionId + + This property holds the id of collection where the contact belongs to. +*/ +QString QDeclarativeContact::collectionId() const +{ + return m_collectionId.toString(); +} + +void QDeclarativeContact::setCollectionId(const QString &collectionId) +{ + QContactCollectionId newCollectionId(QContactCollectionId::fromString(collectionId)); + + // in case invalid collectionId-string, fromString() will return default collectionId-string + // instead of the intended collectionId-string + if (newCollectionId.toString() == collectionId && m_collectionId.toString() != collectionId) { + m_collectionId = newCollectionId; + m_modified = true; + emit contactChanged(); + } +} + /*! \qmlproperty list<ContactDetail> Contact::contactDetails diff --git a/src/imports/contacts/qdeclarativecontact_p.h b/src/imports/contacts/qdeclarativecontact_p.h index 35937fdbb7024e33e59c82696b056a37664893ee..f788e4842838f20a5aad6dbbe0187dda397080fb 100644 --- a/src/imports/contacts/qdeclarativecontact_p.h +++ b/src/imports/contacts/qdeclarativecontact_p.h @@ -46,6 +46,7 @@ #include <QtContacts/qcontact.h> #include <QtContacts/qcontactid.h> +#include <QtContacts/qcontactcollectionid.h> #include "qdeclarativecontactdetails_p.h" @@ -98,6 +99,7 @@ class QDeclarativeContact : public QObject Q_PROPERTY (QQmlListProperty<QDeclarativeContactUrl> urls READ urls NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactVersion* version READ version NOTIFY contactChanged) Q_PROPERTY (QVariantMap preferredDetails READ preferredDetails NOTIFY contactChanged) + Q_PROPERTY (QString collectionId READ collectionId WRITE setCollectionId NOTIFY contactChanged) Q_CLASSINFO("DefaultProperty", "contactDetails") public: @@ -127,6 +129,9 @@ public: Q_INVOKABLE QDeclarativeContactDetail* preferredDetail(const QString& actionName) const; QVariantMap preferredDetails() const; + QString collectionId() const; + void setCollectionId(const QString& collectionId); + QDeclarativeContactAddress* address(); QQmlListProperty<QDeclarativeContactAddress> addresses(); QDeclarativeContactAnniversary* anniversary(); @@ -164,6 +169,7 @@ public: protected: bool m_modified; QContactId m_id; + QContactCollectionId m_collectionId; // always create a copy of the detail for QML // however, seems the garbage collection can't delete all of them (QTBUG-20377) diff --git a/src/imports/contacts/qdeclarativecontactcollection.cpp b/src/imports/contacts/qdeclarativecontactcollection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6505f3820733c5d2374da8fcb966fd88ad286d3b --- /dev/null +++ b/src/imports/contacts/qdeclarativecontactcollection.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtContacts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativecontactcollection_p.h" + +QTCONTACTS_USE_NAMESPACE + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Collection + \instantiates QDeclarativeContactCollection + \brief The Collection element represents a collection of items in an organizer manager. + \ingroup qml-contacts-main + \inqmlmodule QtContacts + */ + +/*! + \internal + */ +QDeclarativeContactCollection::QDeclarativeContactCollection(QObject *parent) + : QObject(parent) +{ +} + +/*! + \qmlproperty string Collection::collectionId + + This property holds the ID of the collection. + */ +QString QDeclarativeContactCollection::id() const +{ + return d.id().toString(); +} + +void QDeclarativeContactCollection::setId(const QString &id) +{ + if (d.id().toString() != id) { + d.setId(QContactCollectionId::fromString(id)); + emit valueChanged(); + } +} + +/*! + \qmlproperty string Collection::name + + This property holds the name meta data of a collection. + */ +QString QDeclarativeContactCollection::name() const +{ + return metaData(QContactCollection::KeyName).toString(); +} + +void QDeclarativeContactCollection::setName(const QString &name) +{ + setMetaData(QContactCollection::KeyName, name); +} + +/*! + \qmlproperty string Collection::description + + This property holds the description meta data of a collection. + */ +QString QDeclarativeContactCollection::description() const +{ + return metaData(QContactCollection::KeyDescription).toString(); +} + +void QDeclarativeContactCollection::setDescription(const QString &description) +{ + setMetaData(QContactCollection::KeyDescription, description); +} + +/*! + \qmlproperty color Collection::secondaryColor + + This property holds the secondary color meta data of a collection. + */ +QColor QDeclarativeContactCollection::secondaryColor() const +{ + return metaData(QContactCollection::KeySecondaryColor).value<QColor>(); +} + +void QDeclarativeContactCollection::setSecondaryColor(const QColor &secondaryColor) +{ + setMetaData(QContactCollection::KeySecondaryColor, secondaryColor); +} + +/*! + \qmlproperty color Collection::color + + This property holds the color meta data of a collection. + */ +QColor QDeclarativeContactCollection::color() const +{ + return metaData(QContactCollection::KeyColor).value<QColor>(); +} + +void QDeclarativeContactCollection::setColor(const QColor &color) +{ + setMetaData(QContactCollection::KeyColor, color); +} + +/*! + \qmlproperty url Collection::image + + This property holds the image url meta data of a collection. + */ +QUrl QDeclarativeContactCollection::image() const +{ + return QUrl(metaData(QContactCollection::KeyImage).toString()); +} + +void QDeclarativeContactCollection::setImage(const QUrl &url) +{ + setMetaData(QContactCollection::KeyImage, url); +} + +/*! + \qmlmethod Collection::setMetaData(key, value) + + Sets the meta data of the collection for the given \a key to the given \a value. Possible keys + include: + \list + \li Collection.KeyName + \li Collection.KeyDescription + \li Collection.KeyColor + \li Collection.KeySecondaryColor + \li Collection.KeyImage + \li Collection.KeyExtended + \endlist + */ +void QDeclarativeContactCollection::setMetaData(QContactCollection::MetaDataKey key, const QVariant &value) +{ + if (metaData(key) != value) { + d.setMetaData(key, value); + emit valueChanged(); + } +} + +/*! + \qmlmethod var Collection::metaData(key) + + Returns the meta data stored in this collection for the given \a key. Possible keys include: + \list + \li Collection.KeyName + \li Collection.KeyDescription + \li Collection.KeyColor + \li Collection.KeyImage + \li Collection.KeyExtended + \endlist + */ +QVariant QDeclarativeContactCollection::metaData(QContactCollection::MetaDataKey key) const +{ + return d.metaData(key); +} + +/*! + \qmlmethod Collection::setExtendedMetaData(key, value) + + Sets the value of the extended metadata with the given \a key to \a value. + */ +void QDeclarativeContactCollection::setExtendedMetaData(const QString &key, const QVariant &value) +{ + if (extendedMetaData(key) != value) { + d.setExtendedMetaData(key, value); + emit valueChanged(); + } +} + +/*! + \qmlmethod var Collection::extendedMetaData(key) + + Returns the value of extended metadata with the given \a key. + */ +QVariant QDeclarativeContactCollection::extendedMetaData(const QString &key) const +{ + return d.extendedMetaData(key); +} + +/*! + \internal + */ +QContactCollection QDeclarativeContactCollection::collection() const +{ + return d; +} + +/*! + \internal + */ +void QDeclarativeContactCollection::setCollection(const QContactCollection &collection) +{ + d = collection; +} + +#include "moc_qdeclarativecontactcollection_p.cpp" + +QT_END_NAMESPACE diff --git a/src/imports/contacts/qdeclarativecontactcollection_p.h b/src/imports/contacts/qdeclarativecontactcollection_p.h new file mode 100644 index 0000000000000000000000000000000000000000..1fe9a7ff71434b50ae4fedbfd135f59e0163af90 --- /dev/null +++ b/src/imports/contacts/qdeclarativecontactcollection_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtContacts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONTACTCOLLECTION_H +#define QDECLARATIVECONTACTCOLLECTION_H + +#include <QtCore/qurl.h> + +#include <QtGui/qcolor.h> + +#include <QtQml/qqml.h> + +#include <QtContacts/qcontactcollection.h> + +QTCONTACTS_USE_NAMESPACE + +QT_BEGIN_NAMESPACE + +class QDeclarativeContactCollection : public QObject +{ + Q_OBJECT + + Q_ENUMS(MetaDataKey) + Q_PROPERTY(QString collectionId READ id WRITE setId NOTIFY valueChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY valueChanged) + Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY valueChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY valueChanged) + Q_PROPERTY(QColor secondaryColor READ secondaryColor WRITE setSecondaryColor NOTIFY valueChanged) + Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY valueChanged) + +public: + enum MetaDataKey { + KeyName = QContactCollection::KeyName, + KeyDescription = QContactCollection::KeyDescription, + KeyColor = QContactCollection::KeyColor, + KeySecondaryColor = QContactCollection::KeySecondaryColor, + KeyImage = QContactCollection::KeyImage, + KeyExtended = QContactCollection::KeyExtended + }; + + QDeclarativeContactCollection(QObject *parent = 0); + + QString id() const; + void setId(const QString &id); + + QString name() const; + void setName(const QString &name); + + QString description() const; + void setDescription(const QString &description); + + QColor color() const; + void setColor(const QColor &color); + + QColor secondaryColor() const; + void setSecondaryColor(const QColor &secondaryColor); + + QUrl image() const; + void setImage(const QUrl &url); + + Q_INVOKABLE void setMetaData(QContactCollection::MetaDataKey key, const QVariant &value); + Q_INVOKABLE QVariant metaData(QContactCollection::MetaDataKey key) const; + + Q_INVOKABLE void setExtendedMetaData(const QString &key, const QVariant &value); + Q_INVOKABLE QVariant extendedMetaData(const QString &key) const; + + // used by model + QContactCollection collection() const; + void setCollection(const QContactCollection & collection); + +Q_SIGNALS: + void valueChanged(); + +private: + QContactCollection d; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeContactCollection) + +#endif // QDECLARATIVECONTACTCOLLECTION_H diff --git a/src/imports/contacts/qdeclarativecontactmodel.cpp b/src/imports/contacts/qdeclarativecontactmodel.cpp index d640116b00f09f41f9f1bcc3e6aebb23f436a5e2..9bf21367326773653711519ddb950f7c26fbc63a 100644 --- a/src/imports/contacts/qdeclarativecontactmodel.cpp +++ b/src/imports/contacts/qdeclarativecontactmodel.cpp @@ -179,7 +179,8 @@ public: m_error(QContactManager::NoError), m_autoUpdate(true), m_componentCompleted(false), - m_progressiveLoading(true) + m_progressiveLoading(true), + m_updatePendingFlag(QDeclarativeContactModelPrivate::NonePending) { } ~QDeclarativeContactModelPrivate() @@ -188,6 +189,12 @@ public: delete m_manager; } + enum UpdateTypePending { + NonePending = 0x0, + UpdatingContactsPending = 0x1, + UpdatingCollectionsPending = 0x2 + }; + QList<QDeclarativeContact*> m_contacts; QMap<QContactId, QDeclarativeContact*> m_contactMap; QMap<QContactId, QDeclarativeContact*> m_contactFetchedMap; @@ -211,7 +218,9 @@ public: QHash<QContactAbstractRequest *, int> m_requestIdHash; QList<QContactFetchRequest*> m_pendingRequests; QList<QContact> m_pendingContacts; + QList<QDeclarativeContactCollection*> m_collections; bool m_progressiveLoading; + int m_updatePendingFlag; }; QDeclarativeContactModel::QDeclarativeContactModel(QObject *parent) : @@ -224,9 +233,9 @@ QDeclarativeContactModel::QDeclarativeContactModel(QObject *parent) : setRoleNames(roleNames); connect(this, SIGNAL(managerChanged()), SLOT(doUpdate())); - connect(this, SIGNAL(filterChanged()), SLOT(doUpdate())); - connect(this, SIGNAL(fetchHintChanged()), SLOT(doUpdate())); - connect(this, SIGNAL(sortOrdersChanged()), SLOT(doUpdate())); + connect(this, SIGNAL(filterChanged()), SLOT(doContactUpdate())); + connect(this, SIGNAL(fetchHintChanged()), SLOT(doContactUpdate())); + connect(this, SIGNAL(sortOrdersChanged()), SLOT(doContactUpdate())); //import vcard connect(&d->m_reader, SIGNAL(stateChanged(QVersitReader::State)), this, SLOT(startImport(QVersitReader::State))); @@ -253,14 +262,21 @@ void QDeclarativeContactModel::setManager(const QString& managerName) if (d->m_manager && (managerName == d->m_manager->managerName() || managerName == d->m_manager->managerUri())) return; - if (d->m_manager) + if (d->m_manager) { + cancelUpdate(); delete d->m_manager; + } + d->m_manager = new QContactManager(managerName); connect(d->m_manager, SIGNAL(dataChanged()), this, SLOT(doUpdate())); connect(d->m_manager, SIGNAL(contactsAdded(QList<QContactId>)), this, SLOT(onContactsAdded(QList<QContactId>))); connect(d->m_manager, SIGNAL(contactsRemoved(QList<QContactId>)), this, SLOT(onContactsRemoved(QList<QContactId>))); connect(d->m_manager, SIGNAL(contactsChanged(QList<QContactId>,QList<QContactDetail::DetailType>)), this, SLOT(onContactsChanged(QList<QContactId>))); + connect(d->m_manager, SIGNAL(collectionsAdded(QList<QContactCollectionId>)), this, SLOT(fetchCollections())); + connect(d->m_manager, SIGNAL(collectionsChanged(QList<QContactCollectionId>)), this, SLOT(fetchCollections())); + connect(d->m_manager, SIGNAL(collectionsRemoved(QList<QContactCollectionId>)), this, SLOT(fetchCollections())); + if (d->m_error != QContactManager::NoError) { d->m_error = QContactManager::NoError; @@ -300,15 +316,71 @@ bool QDeclarativeContactModel::autoUpdate() const void QDeclarativeContactModel::update() { - if (!d->m_componentCompleted) + if (!d->m_componentCompleted || d->m_updatePendingFlag) return; + // Disallow possible duplicate request triggering + d->m_updatePendingFlag = (QDeclarativeContactModelPrivate::UpdatingContactsPending | QDeclarativeContactModelPrivate::UpdatingCollectionsPending); + QMetaObject::invokeMethod(this, "fetchCollections", Qt::QueuedConnection); +} + +/*! + \qmlmethod ContactModel::updateContacts() + + Manually update the contact model contacts. + + \sa ContactModel::update + \sa ContactModel::updateCollections + \sa ContactModel::autoUpdate + */ +void QDeclarativeContactModel::updateContacts() +{ + if (!d->m_componentCompleted || d->m_updatePendingFlag) + return; + // Disallow possible duplicate request triggering + d->m_updatePendingFlag = QDeclarativeContactModelPrivate::UpdatingContactsPending; QMetaObject::invokeMethod(this, "fetchAgain", Qt::QueuedConnection); } -void QDeclarativeContactModel::doUpdate() +/*! + \qmlmethod ContactModel::updateCollections() + + Manually update the contact model collections. + + \sa ContactModel::update + \sa ContactModel::updateContacts + \sa ContactModel::autoUpdate + */ +void QDeclarativeContactModel::updateCollections() +{ + if (!d->m_componentCompleted || d->m_updatePendingFlag) + return; + // Disallow possible duplicate request triggering + d->m_updatePendingFlag = QDeclarativeContactModelPrivate::UpdatingCollectionsPending; + QMetaObject::invokeMethod(this, "fetchCollections", Qt::QueuedConnection); +} + +/*! + \qmlmethod ContactModel::cancelUpdate() + + Cancel the running contact model content update request. + + \sa ContactModel::autoUpdate + \sa ContactModel::update + */ +void QDeclarativeContactModel::cancelUpdate() +{ + foreach (QContactFetchRequest *req, d->m_pendingRequests) { + req->cancel(); + req->deleteLater(); + } + d->m_pendingRequests.clear();; + d->m_updatePendingFlag = QDeclarativeContactModelPrivate::NonePending; +} + +void QDeclarativeContactModel::doContactUpdate() { if (d->m_autoUpdate) - update(); + updateContacts(); } /*! @@ -719,6 +791,66 @@ int QDeclarativeContactModel::fetchContacts(const QStringList &contactIds) } } +/*! + \qmlmethod ContactModel::removeCollection(string collectionId) + Removes asynchronously the contact collection with the given \a collectionId from the backend. + */ +void QDeclarativeContactModel::removeCollection(const QString &collectionId) +{ + QContactCollectionRemoveRequest* req = new QContactCollectionRemoveRequest(this); + req->setManager(d->m_manager); + req->setCollectionId(QContactCollectionId::fromString(collectionId)); + + connect(req, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onRequestStateChanged(QContactAbstractRequest::State))); + + req->start(); +} + +/*! + \qmlmethod OContactModel::saveCollection(Collection collection) + + Saves asynchronously the given \a collection into the contact backend. + */ +void QDeclarativeContactModel::saveCollection(QDeclarativeContactCollection *declColl) +{ + if (declColl) { + QContactCollection collection = declColl->collection(); + QContactCollectionSaveRequest* req = new QContactCollectionSaveRequest(this); + req->setManager(d->m_manager); + req->setCollection(collection); + + if (declColl->collection().id().isNull()) { + // if the collection id is empty this means that this is a new collection + // we need to keep trace of this declarative collection to update with the + // new Id as soon as this request finish + QPointer<QDeclarativeContactCollection> pCollection = declColl; + req->setProperty("DeclarativeCollection", QVariant::fromValue(pCollection)); + } + + connect(req, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onRequestStateChanged(QContactAbstractRequest::State))); + req->start(); + } +} + +/*! + \qmlmethod OContactModel::fetchCollections() + Fetch asynchronously a list of contact collections from the contact backend. +*/ +void QDeclarativeContactModel::fetchCollections() +{ + // fetchCollections() is used for both direct calls and + // signals from model. For signal from model, check also the + // autoupdate-flag. + if (sender() == d->m_manager && !d->m_autoUpdate) { + return; + } + + QContactCollectionFetchRequest* req = new QContactCollectionFetchRequest(this); + connect(req,SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(collectionsFetched())); + req->setManager(d->m_manager); + req->start(); +} + /*! \internal */ @@ -764,6 +896,56 @@ void QDeclarativeContactModel::onFetchContactsRequestStateChanged(QContactAbstra request->deleteLater(); } +/*! + \internal + */ +void QDeclarativeContactModel::collectionsFetched() +{ + QContactCollectionFetchRequest* req = qobject_cast<QContactCollectionFetchRequest*>(QObject::sender()); + Q_ASSERT(req); + if (req->isFinished() && QContactManager::NoError == req->error()) { + d->m_updatePendingFlag &= ~QDeclarativeContactModelPrivate::UpdatingCollectionsPending; + // prepare tables + QHash<QString, const QContactCollection*> collections; + foreach (const QContactCollection& collection, req->collections()) { + collections.insert(collection.id().toString(), &collection); + } + QHash<QString, QDeclarativeContactCollection*> declCollections; + foreach (QDeclarativeContactCollection* declCollection, d->m_collections) { + declCollections.insert(declCollection->collection().id().toString(), declCollection); + } + // go tables through + QHashIterator<QString, const QContactCollection*> collIterator(collections); + while (collIterator.hasNext()) { + collIterator.next(); + if (declCollections.contains(collIterator.key())) { + // collection on both sides, update the declarative collection + declCollections.value(collIterator.key())->setCollection(*collections.value(collIterator.key())); + } else { + // new collection, add it to declarative collection list + QDeclarativeContactCollection* declCollection = new QDeclarativeContactCollection(this); + declCollection->setCollection(*collections.value(collIterator.key())); + d->m_collections.append(declCollection); + } + } + QHashIterator<QString, QDeclarativeContactCollection*> declCollIterator(declCollections); + while (declCollIterator.hasNext()) { + declCollIterator.next(); + if (!collections.contains(declCollIterator.key())) { + // collection deleted on the backend side, delete from declarative collection list + QDeclarativeContactCollection* toBeDeletedColl = declCollections.value(declCollIterator.key()); + d->m_collections.removeOne(toBeDeletedColl); + toBeDeletedColl->deleteLater(); + } + } + emit collectionsChanged(); + if (d->m_updatePendingFlag & QDeclarativeContactModelPrivate::UpdatingContactsPending) + QMetaObject::invokeMethod(this, "fetchAgain", Qt::QueuedConnection); + req->deleteLater(); + } + checkError(req); +} + void QDeclarativeContactModel::clearContacts() { qDeleteAll(d->m_contacts); @@ -860,6 +1042,7 @@ void QDeclarativeContactModel::fetchRequestStateChanged(QContactAbstractRequest: if (newState != QContactAbstractRequest::FinishedState) return; + d->m_updatePendingFlag &= ~QDeclarativeContactModelPrivate::UpdatingContactsPending; QContactFetchRequest* req = qobject_cast<QContactFetchRequest*>(QObject::sender()); Q_ASSERT(req); if (req) { @@ -908,6 +1091,15 @@ void QDeclarativeContactModel::fetchRequestStateChanged(QContactAbstractRequest: } } +/*! + \internal + */ +void QDeclarativeContactModel::doUpdate() +{ + if (d->m_autoUpdate) + update(); +} + /*! \qmlmethod ContactModel::saveContact(Contact contact) @@ -944,18 +1136,40 @@ void QDeclarativeContactModel::onRequestStateChanged(QContactAbstractRequest::St QContactAbstractRequest *request = qobject_cast<QContactAbstractRequest *>(sender()); Q_ASSERT(request); - if ((request->type() == QContactSaveRequest::ContactSaveRequest) && - (request->error() == QContactManager::NoError)) { - QVariant vContact = request->property("DeclarativeContact"); - if (vContact.isValid()) { - QPointer<QDeclarativeContact> pContact = vContact.value<QPointer<QDeclarativeContact> >(); - // Update contact info. - // this is necessary to make sure that the declarative contact get the new contact ID otherwise - // the contact Id will be empty - QList<QContact> contacts = qobject_cast<QContactSaveRequest*>(request)->contacts(); - if (pContact && contacts.length() == 1) { - pContact->setContact(contacts[0]); + if (request->error() == QContactManager::NoError) { + switch (request->type()) { + case QContactAbstractRequest::ContactSaveRequest: + { + QVariant vContact = request->property("DeclarativeContact"); + if (vContact.isValid()) { + QPointer<QDeclarativeContact> pContact = vContact.value<QPointer<QDeclarativeContact> >(); + // Update contact info. + // this is necessary to make sure that the declarative contact get the new contact ID otherwise + // the contact Id will be empty + QList<QContact> contacts = qobject_cast<QContactSaveRequest*>(request)->contacts(); + if (pContact && contacts.length() == 1) { + pContact->setContact(contacts[0]); + } } + break; + } + case QContactAbstractRequest::CollectionSaveRequest: + { + QVariant vCollection = request->property("DeclarativeCollection"); + if (vCollection.isValid()) { + QPointer<QDeclarativeContactCollection> pCollection = vCollection.value<QPointer<QDeclarativeContactCollection> >(); + // Update collection info. + // this is necessary to make sure that the declarative collection get the new collection ID otherwise + // the collection Id will be empty + QList<QContactCollection> collections = qobject_cast<QContactCollectionSaveRequest*>(request)->collections(); + if (pCollection && collections.length() == 1) { + pCollection->setCollection(collections[0]); + } + } + break; + } + default: + break; } } checkError(request); @@ -1169,6 +1383,35 @@ void QDeclarativeContactModel::sortOrder_clear(QQmlListProperty<QDeclarativeCon emit model->sortOrdersChanged(); } } + +/*! + \qmlproperty list<Collection> OContactModel::collections + + This property holds a list of collections in the contact model. + + \sa Collection + */ +QQmlListProperty<QDeclarativeContactCollection> QDeclarativeContactModel::collections() +{ + return QQmlListProperty<QDeclarativeContactCollection>(this, 0, collection_count, collection_at); +} + +int QDeclarativeContactModel::collection_count(QQmlListProperty<QDeclarativeContactCollection> *p) +{ + QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(p->object); + return model ? model->d->m_collections.count() : 0; +} + +QDeclarativeContactCollection *QDeclarativeContactModel::collection_at(QQmlListProperty<QDeclarativeContactCollection> *p, int idx) +{ + QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(p->object); + QDeclarativeContactCollection* collection = 0; + if (model) { + if (!model->d->m_collections.isEmpty() && idx >= 0 && idx < model->d->m_collections.count()) + collection = model->d->m_collections.at(idx); + } + return collection; +} /*! \internal diff --git a/src/imports/contacts/qdeclarativecontactmodel_p.h b/src/imports/contacts/qdeclarativecontactmodel_p.h index a3acdf88440ea15c01337d2113ed03646307b12b..bfdf27ce74bb164e3e4f9f494c31e7c3c4f61886 100644 --- a/src/imports/contacts/qdeclarativecontactmodel_p.h +++ b/src/imports/contacts/qdeclarativecontactmodel_p.h @@ -53,6 +53,7 @@ #include <QtVersit/qversitwriter.h> #include "qdeclarativecontact_p.h" +#include "qdeclarativecontactcollection_p.h" #include "qdeclarativecontactfetchhint_p.h" #include "qdeclarativecontactfilter_p.h" #include "qdeclarativecontactsortorder_p.h" @@ -73,6 +74,7 @@ class QDeclarativeContactModel : public QAbstractListModel, public QQmlParserSta Q_PROPERTY(QDeclarativeContactFilter* filter READ filter WRITE setFilter NOTIFY filterChanged) Q_PROPERTY(QDeclarativeContactFetchHint* fetchHint READ fetchHint WRITE setFetchHint NOTIFY fetchHintChanged) Q_PROPERTY(QQmlListProperty<QDeclarativeContact> contacts READ contacts NOTIFY contactsChanged) + Q_PROPERTY(QQmlListProperty<QDeclarativeContactCollection> collections READ collections NOTIFY collectionsChanged) Q_PROPERTY(QQmlListProperty<QDeclarativeContactSortOrder> sortOrders READ sortOrders NOTIFY sortOrdersChanged) Q_ENUMS(ExportError) Q_ENUMS(ImportError) @@ -133,17 +135,24 @@ public: static QDeclarativeContact* contacts_at(QQmlListProperty<QDeclarativeContact>* prop, int index); static void contacts_clear(QQmlListProperty<QDeclarativeContact>* prop); + QQmlListProperty<QDeclarativeContactSortOrder> sortOrders(); static void sortOrder_append(QQmlListProperty<QDeclarativeContactSortOrder> *p, QDeclarativeContactSortOrder *sortOrder); static int sortOrder_count(QQmlListProperty<QDeclarativeContactSortOrder> *p); static QDeclarativeContactSortOrder * sortOrder_at(QQmlListProperty<QDeclarativeContactSortOrder> *p, int idx); static void sortOrder_clear(QQmlListProperty<QDeclarativeContactSortOrder> *p); - QQmlListProperty<QDeclarativeContactSortOrder> sortOrders() ; + QQmlListProperty<QDeclarativeContactCollection> collections(); + static int collection_count(QQmlListProperty<QDeclarativeContactCollection> *p); + static QDeclarativeContactCollection* collection_at(QQmlListProperty<QDeclarativeContactCollection> *p, int idx); Q_INVOKABLE void removeContact(QString id); Q_INVOKABLE void removeContacts(const QStringList& ids); Q_INVOKABLE void saveContact(QDeclarativeContact* dc); Q_INVOKABLE int fetchContacts(const QStringList& contactIds); + Q_INVOKABLE void removeCollection(const QString& collectionId); + Q_INVOKABLE void saveCollection(QDeclarativeContactCollection* collection); + // FIXME : Naming indicates fetch from database + Q_INVOKABLE void fetchCollections(); Q_INVOKABLE void importContacts(const QUrl& url, const QStringList& profiles = QStringList()); Q_INVOKABLE void exportContacts(const QUrl& url, const QStringList& profiles = QStringList(), const QVariantList &declarativeContacts = QVariantList()); @@ -153,6 +162,7 @@ signals: void errorChanged(); void fetchHintChanged(); void contactsChanged(); + void collectionsChanged(); void sortOrdersChanged(); void autoUpdateChanged(); void exportCompleted(ExportError error, QUrl url); @@ -161,6 +171,9 @@ signals: public slots: void update(); + void updateContacts(); + void updateCollections(); + void cancelUpdate(); private slots: void clearContacts(); @@ -168,6 +181,7 @@ private slots: void requestUpdated(); void fetchRequestStateChanged(QContactAbstractRequest::State newState); void doUpdate(); + void doContactUpdate(); void onRequestStateChanged(QContactAbstractRequest::State newState); void onContactsAdded(const QList<QContactId>& ids); void onContactsRemoved(const QList<QContactId>& ids); @@ -185,6 +199,8 @@ private slots: // handle fetch request from fetchContacts() void onFetchContactsRequestStateChanged(QContactAbstractRequest::State state); + void collectionsFetched(); + private: QContactFetchRequest *createContactFetchRequest(const QList<QContactId> &ids); void checkError(const QContactAbstractRequest *request); diff --git a/src/plugins/contacts/memory/qcontactmemorybackend.cpp b/src/plugins/contacts/memory/qcontactmemorybackend.cpp index d8e698c1dfbcc2057fc903c5e087715487ed0b6a..6476ae4850e0767041be75e82b369d6bd642aafa 100644 --- a/src/plugins/contacts/memory/qcontactmemorybackend.cpp +++ b/src/plugins/contacts/memory/qcontactmemorybackend.cpp @@ -134,6 +134,16 @@ QContactMemoryEngine::QContactMemoryEngine(QContactMemoryEngineData *data) qRegisterMetaType<QContactId>("QContactId"); d->m_managerUri = managerUri(); d->m_sharedEngines.append(this); + + // the default collection always exists. + if (d->m_idToCollectionHash.isEmpty()) { + d->m_managerUri = managerUri(); + const QContactCollectionId defaultId = defaultCollectionId(); + QContactCollection defaultCollection; + defaultCollection.setId(defaultId); + defaultCollection.setMetaData(QContactCollection::KeyName, QString(QStringLiteral("Default Collection"))); + d->m_idToCollectionHash.insert(defaultId, defaultCollection); + } } /*! Frees any memory used by this engine */ @@ -511,6 +521,102 @@ bool QContactMemoryEngine::removeRelationships(const QList<QContactRelationship> return (*error == QContactManager::NoError); } +QContactCollection QContactMemoryEngine::defaultCollection(QContactManager::Error *error) +{ + const QContactCollectionId defaultCollectionId = this->defaultCollectionId(); + Q_ASSERT(d->m_idToCollectionHash.contains(defaultCollectionId)); + *error = QContactManager::NoError; + return d->m_idToCollectionHash.value(defaultCollectionId); +} + +QContactCollection QContactMemoryEngine::collection(const QContactCollectionId &collectionId, QContactManager::Error *error) +{ + if (d->m_idToCollectionHash.contains(collectionId)) { + *error = QContactManager::NoError; + return d->m_idToCollectionHash.value(collectionId); + } + + *error = QContactManager::DoesNotExistError; + return QContactCollection(); +} + +QList<QContactCollection> QContactMemoryEngine::collections(QContactManager::Error *error) +{ + Q_ASSERT(!d->m_idToCollectionHash.isEmpty()); + *error = QContactManager::NoError; + return d->m_idToCollectionHash.values(); +} + +bool QContactMemoryEngine::saveCollection(QContactCollection *collection, QContactManager::Error *error) +{ + QContactCollectionId collectionId = collection->id(); + + QContactCollectionChangeSet cs; + if (d->m_idToCollectionHash.contains(collectionId)) { + // this collection already exists. update our internal list + // if the collection has been modified. + if (d->m_idToCollectionHash.value(collectionId) == *collection) { + *error = QContactManager::NoError; + return true; + } + + cs.insertChangedCollection(collectionId); + } else { + // this must be a new collection. check that the id is null. + if (!collectionId.isNull() && collectionId.managerUri() != d->m_managerUri) { + // nope, this collection belongs in another manager, or has been deleted. + *error = QContactManager::DoesNotExistError; + return false; + } + + // this is a new collection with a null id; create a new id, add it to our list. + QUuid id = QUuid::createUuid(); + collectionId = this->collectionId(id.toByteArray()); + collection->setId(collectionId); + cs.insertAddedCollection(collectionId); + } + + d->m_idToCollectionHash.insert(collectionId, *collection); + d->emitSharedSignals(&cs); + *error = QContactManager::NoError; + return true; +} + +bool QContactMemoryEngine::removeCollection(const QContactCollectionId &collectionId, QContactManager::Error *error) +{ + if (collectionId == defaultCollectionId()) { + // attempting to remove the default collection. this is not allowed in the memory engine. + *error = QContactManager::PermissionsError; + return false; + } + + // try to find the collection to remove it (and the items it contains) + if (d->m_idToCollectionHash.contains(collectionId)) { + // found the collection to remove. remove the items in the collection. + const QList<QContactId> contactsToRemove = d->m_contactsInCollections.values(collectionId); + if (!contactsToRemove.isEmpty()) { + QMap<int, QContactManager::Error> errorMap; + if (!removeContacts(contactsToRemove, &errorMap, error)) { + // without transaction support, we can't back out. but the operation should fail. + return false; + } + } + + // now remove the collection from our lists. + d->m_idToCollectionHash.remove(collectionId); + d->m_contactsInCollections.remove(collectionId); + QContactCollectionChangeSet cs; + cs.insertRemovedCollection(collectionId); + d->emitSharedSignals(&cs); + *error = QContactManager::NoError; + return true; + } + + // the collection doesn't exist... + *error = QContactManager::DoesNotExistError; + return false; +} + /*! \reimp */ void QContactMemoryEngine::requestDestroyed(QContactAbstractRequest *req) { @@ -728,6 +834,65 @@ void QContactMemoryEngine::performAsynchronousOperation(QContactAbstractRequest } break; + case QContactAbstractRequest::CollectionFetchRequest: + { + QContactCollectionFetchRequest* r = static_cast<QContactCollectionFetchRequest*>(currentRequest); + QContactManager::Error operationError = QContactManager::NoError; + QList<QContactCollection> requestedContactCollections = collections(&operationError); + + // update the request with the results. + updateCollectionFetchRequest(r, requestedContactCollections, operationError, QContactAbstractRequest::FinishedState); + } + break; + + case QContactAbstractRequest::CollectionSaveRequest: + { + QContactCollectionSaveRequest* r = static_cast<QContactCollectionSaveRequest*>(currentRequest); + QList<QContactCollection> collections = r->collections(); + QList<QContactCollection> retn; + + QContactManager::Error operationError = QContactManager::NoError; + QMap<int, QContactManager::Error> errorMap; + for (int i = 0; i < collections.size(); ++i) { + QContactManager::Error tempError = QContactManager::NoError; + QContactCollection curr = collections.at(i); + if (!saveCollection(&curr, &tempError)) { + errorMap.insert(i, tempError); + operationError = tempError; + } + retn.append(curr); + } + + updateCollectionSaveRequest(r, retn, operationError, errorMap, QContactAbstractRequest::FinishedState); + } + break; + + case QContactAbstractRequest::CollectionRemoveRequest: + { + // removes the collections identified in the list of ids. + QContactCollectionRemoveRequest* r = static_cast<QContactCollectionRemoveRequest*>(currentRequest); + QContactManager::Error operationError = QContactManager::NoError; + QList<QContactCollectionId> collectionsToRemove = r->collectionIds(); + QMap<int, QContactManager::Error> errorMap; + + for (int i = 0; i < collectionsToRemove.size(); i++) { + QContactManager::Error tempError = QContactManager::NoError; + removeCollection(collectionsToRemove.at(i), &tempError); + + if (tempError != QContactManager::NoError) { + errorMap.insert(i, tempError); + operationError = tempError; + } + } + + if (!errorMap.isEmpty() || operationError != QContactManager::NoError) + updateCollectionRemoveRequest(r, operationError, errorMap, QContactAbstractRequest::FinishedState); + else + updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); + } + break; + + default: // unknown request type. break; } @@ -736,6 +901,12 @@ void QContactMemoryEngine::performAsynchronousOperation(QContactAbstractRequest d->emitSharedSignals(&changeSet); } +QContactCollectionId QContactMemoryEngine::defaultCollectionId() const +{ + static const QByteArray id("Personal"); + return collectionId(id); +} + void QContactMemoryEngine::partiallySyncDetails(QContact *to, const QContact &from, const QList<QContactDetail::DetailType> &mask) { // these details in old contact @@ -893,6 +1064,20 @@ bool QContactMemoryEngine::saveContact(QContact *theContact, QContactChangeSet & return false; } + // check the contact collection + QContactCollectionId collectionId = theContact->collectionId(); + // if is null use default collection + if (collectionId.isNull()) { + collectionId = this->defaultCollectionId(); + theContact->setCollectionId(collectionId); + } else { + // check if the collection exists + QContactCollection collection = this->collection(collectionId, error); + if (collection.id().isNull()) { + return false; + } + } + // check if this is partial save if (!mask.isEmpty()) { QContact tempContact; @@ -915,6 +1100,7 @@ bool QContactMemoryEngine::saveContact(QContact *theContact, QContactChangeSet & // finally, add the contact to our internal lists and return d->m_contacts.append(*theContact); // add contact to list d->m_contactIds.append(theContact->id()); // track the contact id. + d->m_contactsInCollections.insert(collectionId, newContactId); // link contact to collection changeSet.insertAddedContact(theContact->id()); } diff --git a/src/plugins/contacts/memory/qcontactmemorybackend_p.h b/src/plugins/contacts/memory/qcontactmemorybackend_p.h index 7c44d7d3fc88bfdaf6f6b4060d0e6df4f869768d..adeff03a74f34c65e5b37a55b33bb5884bac93a1 100644 --- a/src/plugins/contacts/memory/qcontactmemorybackend_p.h +++ b/src/plugins/contacts/memory/qcontactmemorybackend_p.h @@ -104,7 +104,9 @@ public: QString m_id; // the id parameter value QContactId m_selfContactId; // the "MyCard" contact id - QList<QContact> m_contacts; // list of contacts + QList<QContact> m_contacts; // list of contacts + QHash<QContactCollectionId, QContactId> m_contactsInCollections; // hash of contacts for each collection + QHash<QContactCollectionId, QContactCollection> m_idToCollectionHash; // hash of id to the collection identified by that id QList<QContactId> m_contactIds; // list of contact Id's QList<QContactRelationship> m_relationships; // list of contact relationships QMap<QContactId, QList<QContactRelationship> > m_orderedRelationships; // map of ordered lists of contact relationships @@ -120,6 +122,12 @@ public: cs->emitSignals(engine); } + void emitSharedSignals(QContactCollectionChangeSet *cs) + { + foreach (QContactManagerEngine *engine, m_sharedEngines) + cs->emitSignals(engine); + } + QList<QContactManagerEngine*> m_sharedEngines; // The list of engines that share this data }; @@ -157,6 +165,13 @@ public: virtual bool saveRelationships(QList<QContactRelationship> *relationships, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error *error); virtual bool removeRelationships(const QList<QContactRelationship> &relationships, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error *error); + // collections + QContactCollection defaultCollection(QContactManager::Error* error); + QContactCollection collection(const QContactCollectionId &collectionId, QContactManager::Error *error); + QList<QContactCollection> collections(QContactManager::Error* error); + bool saveCollection(QContactCollection* collection, QContactManager::Error* error); + bool removeCollection(const QContactCollectionId& collectionId, QContactManager::Error* error); + /*! \reimp */ virtual bool validateContact(const QContact &contact, QContactManager::Error *error) const { @@ -202,6 +217,8 @@ private: void performAsynchronousOperation(QContactAbstractRequest *request); + QContactCollectionId defaultCollectionId() const; + QContactMemoryEngineData *d; static QMap<QString, QContactMemoryEngineData*> engineDatas; diff --git a/tests/auto/contacts/contacts.pro b/tests/auto/contacts/contacts.pro index fe4cb48651e2c0ebb5c02b346bf03b5cbc49f506..408b57e445e470ff4c362c431f9f043ebc74153f 100644 --- a/tests/auto/contacts/contacts.pro +++ b/tests/auto/contacts/contacts.pro @@ -4,6 +4,7 @@ TEMPLATE = subdirs SUBDIRS += \ qcontact \ qcontactasync \ + qcontactcollection \ qcontactdetail \ qcontactdetails \ qcontactfilter \ diff --git a/tests/auto/contacts/qcontactasync/unittest/tst_qcontactasync.cpp b/tests/auto/contacts/qcontactasync/unittest/tst_qcontactasync.cpp index 59a584f8387ac442cf5c1d74d2944b3d65311e47..885606b27cefd13123fc46b577df848219ed56dd 100644 --- a/tests/auto/contacts/qcontactasync/unittest/tst_qcontactasync.cpp +++ b/tests/auto/contacts/qcontactasync/unittest/tst_qcontactasync.cpp @@ -56,6 +56,13 @@ QTCONTACTS_USE_NAMESPACE fqcfr2.start(); \ fqcfr3.start(); +/* Define an innocuous request (fetch ie doesn't mutate) to "fill up" any queues */ +#define FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(manager) QContactFetchRequest fqifr1, fqifr2, fqifr3; \ + fqifr1.setManager(manager); fqifr1.start(); \ + fqifr2.setManager(manager); fqifr2.start(); \ + fqifr3.setManager(manager); fqifr3.start(); + + //TESTED_COMPONENT=src/contacts // Unfortunately the plumbing isn't in place to allow cancelling requests at arbitrary points @@ -230,6 +237,13 @@ private slots: void relationshipSave(); void relationshipSave_data() { addManagers(); } + void collectionFetch(); + void collectionFetch_data() { addManagers(); } + void collectionRemove(); + void collectionRemove_data() { addManagers(); } + void collectionSave(); + void collectionSave_data() { addManagers(); } + void maliciousManager(); // uses it's own custom data (manager) void testQuickDestruction(); @@ -245,6 +259,7 @@ private: bool compareContacts(QContact ca, QContact cb); bool containsIgnoringTimestamps(const QList<QContact>& list, const QContact& c); bool compareIgnoringTimestamps(const QContact& ca, const QContact& cb); + bool containsAllCollectionIds(const QList<QContactCollectionId>& target, const QList<QContactCollectionId>& ids); QContactManager* prepareModel(const QString& uri); Qt::HANDLE m_mainThreadId; @@ -376,6 +391,18 @@ bool tst_QContactAsync::compareIgnoringTimestamps(const QContact& ca, const QCon return false; } +bool tst_QContactAsync::containsAllCollectionIds(const QList<QContactCollectionId> &target, const QList<QContactCollectionId> &ids) +{ + bool containsAllIds = true; + foreach (QContactCollectionId id, ids) { + if (!target.contains(id)) { + containsAllIds = false; + break; + } + } + return containsAllIds; +} + void tst_QContactAsync::testDestructor() { QFETCH(QString, uri); @@ -1316,6 +1343,9 @@ void tst_QContactAsync::contactSave() //QCOMPARE(result, expected); // XXX: really, we should use isSuperset() from tst_QContactManager, but this will do for now: QVERIFY(result.first().detail<QContactName>() == nameDetail); + + // check if the contact was saved on default collection + QCOMPARE(result.first().collectionId().toString(), cm->defaultCollection().id().toString()); QCOMPARE(cm->contactIds().size(), originalCount + 1); // update a previously saved contact @@ -2413,6 +2443,443 @@ void tst_QContactAsync::relationshipSave() } } +void tst_QContactAsync::collectionFetch() +{ + QFETCH(QString, uri); + QScopedPointer<QContactManager> cm(prepareModel(uri)); + + QContactCollectionFetchRequest cfr; + QVERIFY(cfr.type() == QContactAbstractRequest::CollectionFetchRequest); + + // initial state - not started, no manager. + QVERIFY(!cfr.isActive()); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.start()); + QVERIFY(!cfr.cancel()); + QVERIFY(!cfr.waitForFinished()); + + // retrieve all collections. + cfr.setManager(cm.data()); + QCOMPARE(cfr.manager(), cm.data()); + QVERIFY(!cfr.isActive()); + QVERIFY(!cfr.isFinished()); + QVERIFY(!cfr.cancel()); + QVERIFY(!cfr.waitForFinished()); + qRegisterMetaType<QContactCollectionFetchRequest*>("QContactCollectionFetchRequest*"); + QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); + QVERIFY(!cfr.cancel()); // not started + + QVERIFY(cfr.start()); + //QVERIFY(cfr.isFinished() || !cfr.start()); // already started. // thread scheduling means this is untestable + QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); + QVERIFY(cfr.waitForFinished()); + QVERIFY(cfr.isFinished()); + + QVERIFY(spy.count() >= 1); // active + finished progress signals + spy.clear(); + + QList<QContactCollection> syncCols = cm->collections(); + QList<QContactCollection> cols = cfr.collections(); + QCOMPARE(cols.size(), syncCols.size()); + for (int i = 0; i < cols.size(); i++) { + QContactCollection curr = cols.at(i); + QVERIFY(syncCols.contains(curr)); + } + + // cancelling + int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. + while (true) { + QVERIFY(!cfr.cancel()); // not started + FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); + QVERIFY(cfr.start()); + if (!cfr.cancel()) { + // due to thread scheduling, async cancel might be attempted + // after the request has already finished.. so loop and try again. + spy.clear(); + cfr.waitForFinished(); + bailoutCount -= 1; + if (!bailoutCount) { +// qWarning("Unable to test cancelling due to thread scheduling!"); + bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; + break; + } + continue; + } + + // if we get here, then we are cancelling the request. + QVERIFY(cfr.waitForFinished()); + QVERIFY(cfr.isCanceled()); + + QVERIFY(spy.count() >= 1); // active + cancelled progress signals + spy.clear(); + break; + } + + // restart, and wait for progress after cancel. + while (true) { + QVERIFY(!cfr.cancel()); // not started + FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); + QVERIFY(cfr.start()); + if (!cfr.cancel()) { + // due to thread scheduling, async cancel might be attempted + // after the request has already finished.. so loop and try again. + cfr.waitForFinished(); + bailoutCount -= 1; + spy.clear(); + if (!bailoutCount) { + //qWarning("Unable to test cancelling due to thread scheduling!"); + bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; + break; + } + continue; + } + cfr.waitForFinished(); + QVERIFY(spy.count() >= 1); // active + cancelled progress signals + spy.clear(); + QVERIFY(!cfr.isActive()); + QVERIFY(cfr.state() == QContactAbstractRequest::CanceledState); + break; + } +} + +void tst_QContactAsync::collectionRemove() +{ + QFETCH(QString, uri); + QScopedPointer<QContactManager> cm(prepareModel(uri)); + QContactCollectionRemoveRequest crr; + QVERIFY(crr.type() == QContactAbstractRequest::CollectionRemoveRequest); + + // initial state - not started, no manager. + QVERIFY(!crr.isActive()); + QVERIFY(!crr.isFinished()); + QVERIFY(!crr.start()); + QVERIFY(!crr.cancel()); + QVERIFY(!crr.waitForFinished()); + + // specific collection set + QContactCollectionId removeId = cm->collections().last().id(); + if (cm->defaultCollection().id() == removeId) + removeId = cm->collections().first().id(); + crr.setCollectionId(removeId); + QVERIFY(crr.collectionIds() == QList<QContactCollectionId>() << removeId); + int originalCount = cm->collections().size(); + crr.setManager(cm.data()); + QCOMPARE(crr.manager(), cm.data()); + QVERIFY(!crr.isActive()); + QVERIFY(!crr.isFinished()); + QVERIFY(!crr.cancel()); + QVERIFY(!crr.waitForFinished()); + qRegisterMetaType<QContactCollectionRemoveRequest*>("QContactCollectionRemoveRequest*"); + QThreadSignalSpy spy(&crr, SIGNAL(stateChanged(QContactAbstractRequest::State))); + QVERIFY(!crr.cancel()); // not started + QVERIFY(crr.start()); + QVERIFY((crr.isActive() &&crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished()); + //QVERIFY(crr.isFinished() || !crr.start()); // already started. // thread scheduling means this is untestable + QVERIFY(crr.waitForFinished()); + QVERIFY(crr.isFinished()); + + QVERIFY(spy.count() >= 1); // active + finished progress signals + spy.clear(); + + QCOMPARE(cm->collections().size(), originalCount - 1); // should have removed that particular collection. + QVERIFY(crr.error() == QContactManager::NoError); + QVERIFY(crr.errorMap().isEmpty()); + + // remove all collections + QList<QContactCollectionId> allCollectionIds; + QList<QContactCollection> allCollections = cm->collections(); + for (int i = 0; i < allCollections.size(); ++i) + allCollectionIds << allCollections.at(i).id(); + crr.setCollectionIds(allCollectionIds); + + QVERIFY(!crr.cancel()); // not started + QVERIFY(crr.start()); + + QVERIFY((crr.isActive() && crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished()); + //QVERIFY(crr.isFinished() || !crr.start()); // already started. // thread scheduling means this is untestable + QVERIFY(crr.waitForFinished()); + QVERIFY(crr.isFinished()); + + QVERIFY(cm->collections().size() >= 1); // at least one collection must be left, since default collection cannot be removed. + QVERIFY(spy.count() >= 1); // active + finished progress signals + spy.clear(); + + // remove empty list + QList<QContactCollectionId> collectionIdList; + QContactCollectionRemoveRequest crr1; + crr1.setManager(cm.data()); + crr1.setCollectionIds(collectionIdList); + crr1.start(); + crr1.waitForFinished(); + QVERIFY(crr1.isFinished()); + QVERIFY(crr1.error() == QContactManager::NoError); + + // cancelling + QContactCollection temp; + temp.setMetaData(QContactCollection::KeyDescription, "Should not be removed!"); + cm->saveCollection(&temp); + crr.setCollectionId(temp.id()); + + int collectionCount = cm->collections().size(); + int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. + while (true) { + QVERIFY(!crr.cancel()); // not started + FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); + QVERIFY(spy.count() == 0); + QVERIFY(crr.start()); + if (!crr.cancel()) { + // due to thread scheduling, async cancel might be attempted + // after the request has already finished.. so loop and try again. + crr.waitForFinished(); + temp.setId(QContactCollectionId()); + if (!cm->saveCollection(&temp)) { + QSKIP("Unable to save temporary item for remove request cancellation test!"); + } + crr.setCollectionId(temp.id()); + bailoutCount -= 1; + if (!bailoutCount) { +// qWarning("Unable to test cancelling due to thread scheduling!"); + bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; + break; + } + spy.clear(); + continue; + } + + // if we get here, then we are cancelling the request. + QVERIFY(crr.waitForFinished()); + QVERIFY(crr.isCanceled()); + QCOMPARE(cm->collections().size(), collectionCount); // temp collection should not have been removed + QList<QContactCollectionId> removeCollectionIds; + QList<QContactCollection> removeCollections = cm->collections(); + for (int i = 0; i < removeCollections.size(); ++i) + removeCollectionIds << removeCollections.at(i).id(); + QVERIFY(containsAllCollectionIds(removeCollectionIds, crr.collectionIds())); + QVERIFY(spy.count() >= 1); // active + cancelled progress signals + spy.clear(); + break; + } + + // restart, and wait for progress after cancel. + while (true) { + QVERIFY(!crr.cancel()); // not started + FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); + QVERIFY(crr.start()); + if (!crr.cancel()) { + // due to thread scheduling, async cancel might be attempted + // after the request has already finished.. so loop and try again. + crr.waitForFinished(); + temp.setId(QContactCollectionId()); + if (!cm->saveCollection(&temp)) { + QSKIP("Unable to save temporary item for remove request cancellation test!"); + } + crr.setCollectionId(temp.id()); + bailoutCount -= 1; + if (!bailoutCount) { +// qWarning("Unable to test cancelling due to thread scheduling!"); + bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; + break; + } + spy.clear(); + continue; + } + crr.waitForFinished(); + QVERIFY(crr.isCanceled()); + QCOMPARE(cm->collections().size(), collectionCount); // temp collection should not have been removed + QList<QContactCollectionId> removeCollectionIds; + QList<QContactCollection> removeCollections = cm->collections(); + for (int i = 0; i < removeCollections.size(); ++i) + removeCollectionIds << removeCollections.at(i).id(); + QVERIFY(containsAllCollectionIds(removeCollectionIds, crr.collectionIds())); + QVERIFY(spy.count() >= 1); // active + cancelled progress signals + spy.clear(); + break; + } + + // now clean up our temp collection. + cm->removeCollection(temp.id()); + +} + +void tst_QContactAsync::collectionSave() +{ + QFETCH(QString, uri); + QScopedPointer<QContactManager> cm(prepareModel(uri)); + QContactCollectionSaveRequest csr; + QVERIFY(csr.type() == QContactAbstractRequest::CollectionSaveRequest); + + // initial state - not started, no manager. + QVERIFY(!csr.isActive()); + QVERIFY(!csr.isFinished()); + QVERIFY(!csr.start()); + QVERIFY(!csr.cancel()); + QVERIFY(!csr.waitForFinished()); + + // save a new item + int originalCount = cm->collections().size(); + QContactCollection testCollection; + testCollection.setMetaData(QContactCollection::KeyDescription, "test description"); + testCollection.setMetaData(QContactCollection::KeyName, "New collection"); + QList<QContactCollection> saveList; + saveList << testCollection; + csr.setManager(cm.data()); + QCOMPARE(csr.manager(), cm.data()); + QVERIFY(!csr.isActive()); + QVERIFY(!csr.isFinished()); + QVERIFY(!csr.cancel()); + QVERIFY(!csr.waitForFinished()); + qRegisterMetaType<QContactCollectionSaveRequest*>("QContactCollectionSaveRequest*"); + QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State))); + csr.setCollection(testCollection); + QCOMPARE(csr.collections(), saveList); + QVERIFY(!csr.cancel()); // not started + QVERIFY(csr.start()); + + QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); + //QVERIFY(csr.isFinished() || !csr.start()); // already started. // thread scheduling means this is untestable + QVERIFY(csr.waitForFinished()); + QVERIFY(csr.isFinished()); + QVERIFY(spy.count() >= 1); // active + finished progress signals + spy.clear(); + + QList<QContactCollection> expected = csr.collections(); + QCOMPARE(expected.size(), 1); + QList<QContactCollection> result; + result << cm->collection(csr.collections().at(0).id()); + + // find the saved one, compare. + foreach (const QContactCollection &col, result) + QVERIFY(col.id() == expected.at(0).id()); + + // update a previously saved collection + QVERIFY(!result.isEmpty()); // make sure that we were able to retrieve the required collection. + testCollection = result.first(); + testCollection.setMetaData(QContactCollection::KeyName, "test name"); + saveList.clear(); + saveList << testCollection; + csr.setCollections(saveList); + QCOMPARE(csr.collections(), saveList); + QVERIFY(!csr.cancel()); // not started + QVERIFY(csr.start()); + + QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); + //QVERIFY(csr.isFinished() || !csr.start()); // already started. // thread scheduling means this is untestable + QVERIFY(csr.waitForFinished()); + + QVERIFY(csr.isFinished()); + QVERIFY(spy.count() >= 1); // active + finished progress signals + spy.clear(); + + expected = csr.collections(); + result.clear(); + result = cm->collections(); + // find the saved one, compare. + foreach (const QContactCollection& col, result) { + if (col.id() == expected.at(0).id()) { + QVERIFY(col == expected.at(0)); // XXX TODO: if we change the semantic so that save merely updates the id...? + } + } + QCOMPARE(cm->collections().size(), originalCount + 1); // ie shouldn't have added an extra one (would be +2) + QVERIFY(csr.error() == QContactManager::NoError); + QVERIFY(csr.errorMap().isEmpty()); + + // save empty list + QList<QContactCollection> collectionList; + QContactCollectionSaveRequest csr1; + csr1.setManager(cm.data()); + csr1.setCollections(collectionList); + csr1.start(); + csr1.waitForFinished(); + QVERIFY(csr1.isFinished()); + QVERIFY(csr1.error() == QContactManager::NoError); + + // cancelling + QContactCollection temp; + temp.setMetaData(testCollection.metaData()); + temp.setExtendedMetaData("test", "shouldn't be saved"); + saveList.clear(); + saveList << temp; + csr.setCollections(saveList); + + int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. + while (true) { + QVERIFY(!csr.cancel()); // not started + FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); + QVERIFY(csr.start()); + if (!csr.cancel()) { + // due to thread scheduling, async cancel might be attempted + // after the request has already finished.. so loop and try again. + csr.waitForFinished(); + saveList = csr.collections(); + if (cm->collections().size() > (originalCount + 1) && !cm->removeCollection(saveList.at(0).id())) { + QSKIP("Unable to remove saved collection to test cancellation of collection save request"); + } + saveList.clear(); + saveList << temp; + csr.setCollections(saveList); + bailoutCount -= 1; + if (!bailoutCount) { +// qWarning("Unable to test cancelling due to thread scheduling!"); + bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; + break; + } + spy.clear(); + continue; + } + + // if we get here, then we are cancelling the request. + QVERIFY(csr.waitForFinished()); + QVERIFY(csr.isCanceled()); + QVERIFY(spy.count() >= 1); // active + cancelled progress signals + spy.clear(); + + // verify that the changes were not saved + expected.clear(); + QList<QContactCollection> allCollections = cm->collections(); + QVERIFY(!allCollections.contains(temp)); // should NOT contain it since it was cancelled. + QCOMPARE(allCollections.size(), originalCount + 1); + break; + } + // restart, and wait for progress after cancel. + + while (true) { + QVERIFY(!csr.cancel()); // not started + FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data()); + QVERIFY(csr.start()); + if (!csr.cancel()) { + // due to thread scheduling, async cancel might be attempted + // after the request has already finished.. so loop and try again. + csr.waitForFinished(); + saveList = csr.collections(); + if (cm->collections().size() > (originalCount + 1) && !cm->removeCollection(saveList.at(0).id())) { + QSKIP("Unable to remove saved item to test cancellation of item save request"); + } + saveList.clear(); + saveList << temp; + csr.setCollections(saveList); + bailoutCount -= 1; + if (!bailoutCount) { +// qWarning("Unable to test cancelling due to thread scheduling!"); + bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; + break; + } + spy.clear(); + continue; + } + csr.waitForFinished(); // now wait until finished (if it hasn't already). + QVERIFY(csr.isCanceled()); + QVERIFY(spy.count() >= 1); // active + cancelled progress signals + spy.clear(); + + // verify that the changes were not saved + expected.clear(); + QList<QContactCollection> allCollections = cm->collections(); + QVERIFY(!allCollections.contains(temp)); + QCOMPARE(cm->collections().size(), originalCount + 1); + break; + } +} + void tst_QContactAsync::maliciousManager() { // use the invalid manager: passes all requests through to base class @@ -2655,6 +3122,11 @@ QContactManager* tst_QContactAsync::prepareModel(const QString& managerUri) crb.setRelationshipType(QContactRelationship::IsSameAs()); cm->saveRelationship(&crb); + QContactCollection testCollection; + testCollection.setMetaData(QContactCollection::KeyName, "Test Collection"); + testCollection.setMetaData(QContactCollection::KeyDescription, "test collection"); + cm->saveCollection(&testCollection); + return cm; // TODO: cleanup once test is complete diff --git a/tests/auto/contacts/qcontactcollection/qcontactcollection.pro b/tests/auto/contacts/qcontactcollection/qcontactcollection.pro new file mode 100644 index 0000000000000000000000000000000000000000..bb659977648fb1b848be1d17ee942a6957628b4c --- /dev/null +++ b/tests/auto/contacts/qcontactcollection/qcontactcollection.pro @@ -0,0 +1,5 @@ +include(../../auto.pri) + +QT += contacts + +SOURCES += tst_qcontactcollection.cpp diff --git a/tests/auto/contacts/qcontactcollection/tst_qcontactcollection.cpp b/tests/auto/contacts/qcontactcollection/tst_qcontactcollection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d36f36c482e2779f28dbf741c863802cb7749577 --- /dev/null +++ b/tests/auto/contacts/qcontactcollection/tst_qcontactcollection.cpp @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QtCore/qset.h> + +#include <QtContacts/qcontacts.h> + + +QTCONTACTS_USE_NAMESPACE + +static inline QContactCollectionId makeId(const QString &managerName, uint id) +{ + return QContactCollectionId(QStringLiteral("qtcontacts:%1:").arg(managerName), QByteArray(reinterpret_cast<const char *>(&id), sizeof(uint))); +} + +class tst_QContactCollection: public QObject +{ +Q_OBJECT + +public: + tst_QContactCollection(); + virtual ~tst_QContactCollection(); + +private slots: + void metaData(); + void compare(); + void idComparison(); + void idHash(); + void idStringFunctions(); + void hash(); + void datastream(); + void traits(); + void idTraits(); +}; + +tst_QContactCollection::tst_QContactCollection() +{ +} + +tst_QContactCollection::~tst_QContactCollection() +{ +} + +void tst_QContactCollection::metaData() +{ + QContactCollection c; + QVERIFY(c.metaData().isEmpty()); + c.setExtendedMetaData(QString(QStringLiteral("test")), 5); + QCOMPARE(c.extendedMetaData(QString(QStringLiteral("test"))).toInt(), 5); + + QMap<QContactCollection::MetaDataKey, QVariant> mdm; + mdm.insert(QContactCollection::KeyName, QString(QStringLiteral("test2"))); + c.setMetaData(mdm); + QCOMPARE(c.metaData(), mdm); + QCOMPARE(c.metaData(QContactCollection::KeyName).toString(), QString(QStringLiteral("test2"))); +} + +void tst_QContactCollection::compare() +{ + QContactCollection c, c2; + QVERIFY(c == c2); + c.setExtendedMetaData(QStringLiteral("test"), 5); + QVERIFY(c != c2); + c2.setExtendedMetaData(QStringLiteral("test"), 5); + QVERIFY(c == c2); + + QMap<QContactCollection::MetaDataKey, QVariant> mdm; + mdm.insert(QContactCollection::KeyName, QStringLiteral("test2")); + c.setMetaData(mdm); + QVERIFY(c != c2); + c2.setMetaData(mdm); + QVERIFY(c == c2); + + c2 = QContactCollection(); + QVERIFY(c != c2); + c2 = c; + QVERIFY(c == c2); + + c.setId(makeId(QStringLiteral("a"), 1)); + QVERIFY(c != c2); + c2.setId(makeId(QStringLiteral("a"), 1)); + QVERIFY(c == c2); + c.setId(makeId(QStringLiteral("b"), 1)); + QVERIFY(c != c2); + c2.setId(c.id()); + QVERIFY(c == c2); + c.setId(makeId(QStringLiteral("b"), 2)); +} + +void tst_QContactCollection::idComparison() +{ + QContactCollectionId id1(makeId("a", 1)); + QContactCollectionId id2(makeId("a", 1)); + QVERIFY(!(id1 < id2)); + QVERIFY(!(id2 < id1)); + QVERIFY(id1 == id2); + + QContactCollectionId id3(makeId("a", 2)); + QContactCollectionId id4(makeId("b", 1)); + QContactCollectionId id5(makeId("b", 2)); + QVERIFY((((id1 < id3) && !(id3 < id1)) || ((id3 < id1) && !(id1 < id3))) && (id1 != id3)); + QVERIFY((((id1 < id4) && !(id4 < id1)) || ((id4 < id1) && !(id1 < id4))) && (id1 != id4)); + QVERIFY((((id3 < id4) && !(id4 < id3)) || ((id4 < id3) && !(id3 < id4))) && (id3 != id4)); + QVERIFY((((id1 < id5) && !(id5 < id1)) || ((id5 < id1) && !(id1 < id5))) && (id3 != id4)); + + QContactCollectionId id6; + QContactCollectionId id7(QString(), "1"); + QContactCollectionId id8(QString(), "2"); + QContactCollectionId id9(QStringLiteral("qtcontacts:basic:"), QByteArray()); + QVERIFY(id6.isNull()); + QVERIFY(id7.isNull()); + QVERIFY(id8.isNull()); + QVERIFY(id9.isNull()); + QVERIFY(id6 == id7); + QVERIFY(!(id6 < id7)); + QVERIFY(id7 == id6); + QVERIFY(!(id7 < id6)); + QVERIFY(id7 == id8); + QVERIFY(!(id7 < id8)); + QVERIFY(id8 == id7); + QVERIFY(!(id9 < id8)); + QVERIFY(id8 == id9); + QVERIFY(!(id8 < id9)); + QVERIFY(id9 == id8); + QVERIFY(!(id9 < id8)); + + QVERIFY(!(id1 == id6)); + QVERIFY(!(id1 < id6)); + QVERIFY(id6 < id1); + QVERIFY(!(id1 == id7)); + QVERIFY(!(id1 < id7)); + QVERIFY(id7 < id1); + QVERIFY(!(id1 == id8)); + QVERIFY(!(id1 < id8)); + QVERIFY(id8 < id1); + QVERIFY(!(id1 == id9)); + QVERIFY(!(id1 < id9)); + QVERIFY(id9 < id1); +} + +void tst_QContactCollection::idHash() +{ + QContactCollectionId id1(makeId("a", 1)); + QContactCollectionId id2(makeId("a", 1)); + QContactCollectionId id3(makeId("b", 1)); + QContactCollectionId id4(makeId("a", 2)); + // note that the hash function ignores the managerUri + QCOMPARE(qHash(id1), qHash(id2)); + QCOMPARE(qHash(id1), qHash(id3)); + QVERIFY(qHash(id1) != qHash(id4)); + + QSet<QContactCollectionId> set; + set.insert(id1); + set.insert(id2); + set.insert(id3); + set.insert(id4); + QCOMPARE(set.size(), 3); +} + +void tst_QContactCollection::idStringFunctions() +{ + // TODO: review test + QContactCollectionId id1(makeId("a", 1)); + QContactCollectionId id2(makeId("a", 1)); + QContactCollectionId id3(makeId("b", 1)); + QContactCollectionId id4(makeId("a", 2)); + QVERIFY(qHash(id1) == qHash(id2)); + QVERIFY(qHash(id1) != qHash(id4)); + + // note that the toString and fromString functions are + // engine and id specific. This test merely checks that + // the API is hooked up correctly. + + QVERIFY(id1.toString() == id2.toString()); + QVERIFY(id1.toString() != id3.toString()); + QVERIFY(id1.toString() != id4.toString()); + QVERIFY(id3.toString() != id4.toString()); + + // this should "work" -- string of the correct format + const uint numericId2 = 2u; + const QByteArray localId2 = QByteArray(reinterpret_cast<const char *>(&numericId2), sizeof(uint)); + QString prebuiltidstring = QString("qtcontacts") + QString(":") + QString("a") + QString("::") + localId2.toHex(); + QContactCollectionId rebuiltid = QContactCollectionId::fromString(prebuiltidstring); + QVERIFY(rebuiltid == id4); + QVERIFY(rebuiltid.localId() == id4.localId()); + + // this string has the right format and one parameter, but requires a working backend + prebuiltidstring = QString("qtcontacts") + QString(":") + QString("a") + QString(":") + QString("key=value") + QString(":") + localId2.toHex(); + rebuiltid = QContactCollectionId::fromString(prebuiltidstring); + QVERIFY(rebuiltid != id4); + QVERIFY(rebuiltid.localId() == id4.localId()); + + // this string has the right format and some parameters, but requires a working backend + prebuiltidstring = QString("qtcontacts") + QString(":") + QString("a") + QString(":") + QString("key=value&key2=value2") + QString(":") + localId2.toHex(); + rebuiltid = QContactCollectionId::fromString(prebuiltidstring); + QVERIFY(rebuiltid != id4); + QVERIFY(rebuiltid.localId() == id4.localId()); + + // this string has the right format but misses the value for a parameter + prebuiltidstring = QString("qtcontacts") + QString(":") + QString("a") + QString(":") + QString("key=value&key2=") + QString(":") + localId2.toHex(); + rebuiltid = QContactCollectionId::fromString(prebuiltidstring); + QVERIFY(rebuiltid != id4); + QVERIFY(rebuiltid.localId() == id4.localId()); + + // this string misses a field (the parameters) + prebuiltidstring = QString("qtcontacts") + QString(":") + QString("a") + QString(":") + localId2.toHex(); + rebuiltid = QContactCollectionId::fromString(prebuiltidstring); + QVERIFY(rebuiltid == QContactCollectionId()); // invalid so should be null. + + // this string misses two fields (params plus manager uri) + prebuiltidstring = QString("qtcontacts") + QString(":") + QString::number(2); + rebuiltid = QContactCollectionId::fromString(prebuiltidstring); + QVERIFY(rebuiltid == QContactCollectionId()); // invalid so should be null. + + // this string misses the prefix (qtorganizer) + prebuiltidstring = QString("notorganizer") + QString(":") + QString("a") + QString("::") + localId2.toHex(); + rebuiltid = QContactCollectionId::fromString(prebuiltidstring); + QVERIFY(rebuiltid == QContactCollectionId()); // invalid so should be null. + + // this string misses the manager uri + prebuiltidstring = QString("notorganizer") + QString(":::") + localId2.toHex(); + rebuiltid = QContactCollectionId::fromString(prebuiltidstring); + QVERIFY(rebuiltid == QContactCollectionId()); // invalid so should be null. +} + +void tst_QContactCollection::hash() +{ + // TODO: review tests + QContactCollectionId id(makeId("a", 1)); + QContactCollection c1; + c1.setId(id); + c1.setExtendedMetaData("key", "value"); + QContactCollection c2; + c2.setId(id); + c2.setExtendedMetaData("key", "value"); + QContactCollection c3; + c3.setId(id); + c3.setExtendedMetaData("key", "another value"); + QContactCollection c4; // no details + c4.setId(id); + QContactCollection c5; + c5.setId(id); + c5.setExtendedMetaData("key", "value"); + QVERIFY(qHash(c1) == qHash(c2)); + QVERIFY(qHash(c1) != qHash(c3)); + QVERIFY(qHash(c1) != qHash(c4)); + QVERIFY(qHash(c1) == qHash(c5)); +} + +void tst_QContactCollection::datastream() +{ + // collection datastreaming + QByteArray buffer; + QContactCollection collectionIn; + collectionIn.setExtendedMetaData("key", "value"); + QContactCollection collectionOut; + QContactCollectionId originalId; + + // first, stream an item with a complete id + { + QDataStream stream1(&buffer, QIODevice::WriteOnly); + QContactManager om("memory"); + QVERIFY(om.saveCollection(&collectionIn)); // fill in its ID + originalId = collectionIn.id(); + stream1 << collectionIn; + QVERIFY(buffer.size() > 0); + QDataStream stream2(buffer); + stream2 >> collectionOut; + QCOMPARE(collectionOut, collectionIn); // can use QCOMPARE for collections, since no detail keys. + } + + // second, stream an item with an id with the mgr uri set, local id null + { + QDataStream stream1(&buffer, QIODevice::WriteOnly); + collectionIn.setId(QContactCollectionId()); + stream1 << collectionIn; + QVERIFY(buffer.size() > 0); + QDataStream stream2(buffer); + stream2 >> collectionOut; + QCOMPARE(collectionOut, collectionIn); // can use QCOMPARE for collections, since no detail keys. + } + + // third, stream an item with a null id + { + QDataStream stream1(&buffer, QIODevice::WriteOnly); + collectionIn.setId(QContactCollectionId()); + stream1 << collectionIn; + QVERIFY(buffer.size() > 0); + QDataStream stream2(buffer); + stream2 >> collectionOut; + QVERIFY(collectionOut.metaData() == collectionIn.metaData()); + QVERIFY(collectionOut.id() == collectionIn.id()); // should both be null ids. + } + + // id datastreaming + buffer.clear(); + QContactCollectionId inputId; + QContactCollectionId outputId; + + // first, stream the whole id (mgr uri set, local id set) + { + inputId = originalId; + QString serializedId = inputId.toString(); + outputId = QContactCollectionId::fromString(serializedId); + QCOMPARE(inputId, outputId); + + inputId = originalId; + buffer.clear(); + QDataStream stream1(&buffer, QIODevice::WriteOnly); + stream1 << inputId; + QVERIFY(buffer.size() > 0); + QDataStream stream2(buffer); + stream2 >> outputId; + QCOMPARE(inputId, outputId); + } + + // second, stream a null id + { + inputId = QContactCollectionId(); + QString serializedId = inputId.toString(); + outputId = QContactCollectionId::fromString(serializedId); + QCOMPARE(inputId, outputId); + + inputId = QContactCollectionId(); + buffer.clear(); + QDataStream stream1(&buffer, QIODevice::WriteOnly); + stream1 << inputId; + QVERIFY(buffer.size() > 0); + QDataStream stream2(buffer); + stream2 >> outputId; + QCOMPARE(inputId, outputId); + } +} + +void tst_QContactCollection::traits() +{ + QCOMPARE(sizeof(QContactCollection), sizeof(void *)); + QVERIFY(QTypeInfo<QContactCollection>::isComplex); + QVERIFY(!QTypeInfo<QContactCollection>::isStatic); + QVERIFY(!QTypeInfo<QContactCollection>::isLarge); + QVERIFY(!QTypeInfo<QContactCollection>::isPointer); + QVERIFY(!QTypeInfo<QContactCollection>::isDummy); +} + +void tst_QContactCollection::idTraits() +{ + QCOMPARE(sizeof(QContactCollectionId), 2*sizeof(void *)); + QVERIFY(QTypeInfo<QContactCollectionId>::isComplex); + QVERIFY(!QTypeInfo<QContactCollectionId>::isStatic); + QVERIFY(QTypeInfo<QContactCollectionId>::isLarge); + QVERIFY(!QTypeInfo<QContactCollectionId>::isPointer); + QVERIFY(!QTypeInfo<QContactCollectionId>::isDummy); +} + +QTEST_MAIN(tst_QContactCollection) +#include "tst_qcontactcollection.moc" diff --git a/tests/auto/contacts/qcontactfilter/tst_qcontactfilter.cpp b/tests/auto/contacts/qcontactfilter/tst_qcontactfilter.cpp index e347c3fdd5b12ce93ac38cf4fd8273da84b5dc83..4df9a25f36cf4cacf1dbf724f4cc5830b4ea1f92 100644 --- a/tests/auto/contacts/qcontactfilter/tst_qcontactfilter.cpp +++ b/tests/auto/contacts/qcontactfilter/tst_qcontactfilter.cpp @@ -55,6 +55,11 @@ static inline QContactId makeId(const QString &managerName, uint id) return QContactId(QStringLiteral("qtcontacts:basic%1:").arg(managerName), QByteArray(reinterpret_cast<const char *>(&id), sizeof(uint))); } +static inline QContactCollectionId makeCollectionId(uint id) +{ + return QContactCollectionId(QStringLiteral("qtcontacts:basic:"), QByteArray(reinterpret_cast<const char *>(&id), sizeof(uint))); +} + class tst_QContactFilter : public QObject { Q_OBJECT @@ -81,9 +86,12 @@ private slots: void canonicalizedFilter_data(); void testFilter(); void testFilter_data(); + void collectionFilter(); void datastream(); void datastream_data(); + void testDebugStreamOut(); + void testDebugStreamOut_data(); void traits(); }; @@ -1210,6 +1218,40 @@ void tst_QContactFilter::testFilter_data() } } +void tst_QContactFilter::collectionFilter() +{ + QContactCollectionFilter icf; + + QVERIFY(icf.collectionIds().isEmpty()); + + QContactCollectionId id1 = makeCollectionId(5); + QContactCollectionId id2 = makeCollectionId(6); + QContactCollectionId id3 = makeCollectionId(7); + QContactCollectionId id4 = makeCollectionId(12); + QSet<QContactCollectionId> ids; + ids << id1 << id2 << id3; + + icf.setCollectionIds(ids); + QVERIFY(icf.collectionIds() == ids); + + icf.setCollectionId(id4); + ids.clear(); + ids << id4; + QVERIFY(icf.collectionIds() == ids); + + QContactCollectionFilter icf2; + icf2 = icf; + QVERIFY(icf2.collectionIds() == ids); + + QContactFilter fil; + fil = icf; + QVERIFY(fil.type() == QContactFilter::CollectionFilter); + + QContactCollectionFilter icf3(fil); + QVERIFY(fil.type() == QContactFilter::CollectionFilter); + QVERIFY(icf3.collectionIds() == ids); +} + void tst_QContactFilter::datastream() { QFETCH(QContactFilter, filterIn); @@ -1286,6 +1328,58 @@ void tst_QContactFilter::datastream_data() } } +void tst_QContactFilter::testDebugStreamOut() +{ + QFETCH(QContactFilter, filterIn); + QFETCH(QString, messageExpected); + + QTest::ignoreMessage(QtDebugMsg, messageExpected.toUtf8()); + qDebug() << filterIn; +} + +void tst_QContactFilter::testDebugStreamOut_data() +{ + QTest::addColumn<QContactFilter>("filterIn"); + QTest::addColumn<QString>("messageExpected"); + + { + QContactCollectionFilter filter; + QContactCollectionId id1 = makeCollectionId(5); + QContactCollectionId id2 = makeCollectionId(6); + QContactCollectionId id3 = makeCollectionId(7); + QContactCollectionId id4 = makeCollectionId(12); + QSet<QContactCollectionId> ids; + ids << id1 << id2 << id3; + filter.setCollectionIds(ids); + // Testing method setCollectionIds + QTest::newRow("collection") << (QContactFilter)filter << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::05000000), QContactCollectionId(qtcontacts:basic::06000000), QContactCollectionId(qtcontacts:basic::07000000))))"; + + filter.setCollectionId(id2); + // Testing method setCollectionId (and the related clearing of the collection) + QTest::newRow("collection") << (QContactFilter)filter << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::06000000))))"; + filter.setCollectionId(id4); + // Testing again method setCollectionId (and the related clearing of the collection) + QTest::newRow("collection") << (QContactFilter)filter << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::0c000000))))"; + ids.clear(); + ids << id4; + // Testing again method setCollectionIds + QTest::newRow("collection") << (QContactFilter)filter << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::0c000000))))"; + + QContactCollectionFilter filter2; + filter2 = filter; + // Testing again method setCollectionIds on the copied filter + QTest::newRow("collection") << (QContactFilter)filter2 << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::0c000000))))"; + + QContactFilter fil; + fil = filter; + // Testing that the assignment/conversion went fine + QTest::newRow("collection") << (QContactFilter)fil << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::0c000000))))"; + + QContactCollectionFilter filter3(fil); + QTest::newRow("collection") << (QContactFilter)filter3 << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::0c000000))))"; + } +} + void tst_QContactFilter::traits() { QCOMPARE(sizeof(QContactFilter), sizeof(void *)); diff --git a/tests/auto/contacts/qcontactmanager/tst_qcontactmanager.cpp b/tests/auto/contacts/qcontactmanager/tst_qcontactmanager.cpp index 7e7f8d0a06d7ec735dfc0404b3cad610e019ec7d..94395c859921c745d4604fb64bd92dd2db8e248a 100644 --- a/tests/auto/contacts/qcontactmanager/tst_qcontactmanager.cpp +++ b/tests/auto/contacts/qcontactmanager/tst_qcontactmanager.cpp @@ -150,6 +150,10 @@ private slots: void contactType(); void lateDeletion(); void compareVariant(); + void createCollection(); + void modifyCollection(); + void removeCollection(); + void saveContactIntoCollections(); #if defined(USE_VERSIT_PLZ) void partialSave(); @@ -190,6 +194,10 @@ private slots: void lateDeletion_data() {addManagers();} void testInterSectionOfIdFilters_data() {addManagers();} void testInterSectionOfIdAndDetailFilters_data() {addManagers();} + void createCollection_data() {addManagers();} + void modifyCollection_data() {addManagers();} + void removeCollection_data() {addManagers();} + void saveContactIntoCollections_data() {addManagers();} }; // Helper class that connects to a signal on ctor, and disconnects on dtor @@ -1474,15 +1482,23 @@ void tst_QContactManager::memoryManager() nc.setLastName("Civilian"); c.saveDetail(&nc); m1.saveContact(&c); + + // reset ids c.setId(QContactId()); + c.setCollectionId(QContactCollectionId()); + QContact c2; QContactName nc2 = c2.detail(QContactName::Type); c2 = c; nc2.setMiddleName("Public"); c2.saveDetail(&nc2); + m2.saveContact(&c2); // save c2 first; c will be given a higher id m2.saveContact(&c); // save c to m2 + + // reset ids c.setId(QContactId()); + c.setCollectionId(QContactCollectionId()); nc.setSuffix("MD"); c.saveDetail(&nc); m3.saveContact(&c); @@ -1871,6 +1887,7 @@ void tst_QContactManager::signalEmission() qRegisterMetaType<QContactId>("QContactId"); qRegisterMetaType<QList<QContactId> >("QList<QContactId>"); qRegisterMetaType<QList<QContactDetail::DetailType> >("QList<QContactDetail::DetailType>"); + QSignalSpy spyCA(m1.data(), SIGNAL(contactsAdded(QList<QContactId>))); QSignalSpy spyCM(m1.data(), SIGNAL(contactsChanged(QList<QContactId>, QList<QContactDetail::DetailType>))); QSignalSpy spyCR(m1.data(), SIGNAL(contactsRemoved(QList<QContactId>))); @@ -1879,8 +1896,10 @@ void tst_QContactManager::signalEmission() QTestSignalSink cmsink(m1.data(), SIGNAL(contactsChanged(QList<QContactId>, QList<QContactDetail::DetailType>))); QTestSignalSink crsink(m1.data(), SIGNAL(contactsRemoved(QList<QContactId>))); + QList<QVariant> args; QList<QContactId> arg; + QList<QContactCollectionId> collectionIdList; QContact c; QList<QContact> batchAdd; QList<QContactId> batchRemove; @@ -3209,6 +3228,153 @@ void tst_QContactManager::compareVariant() QVERIFY((comparison + expected) == 0); } +void tst_QContactManager::createCollection() +{ + QFETCH(QString, uri); + QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri)); + + qRegisterMetaType<QList<QContactCollectionId> >("QList<QContactCollectionId>"); + QSignalSpy collectionsAddedSpy(cm.data(), SIGNAL(collectionsAdded(QList<QContactCollectionId>))); + QByteArray collectionName = QUuid::createUuid().toByteArray(); + + // create collection + { + QContactCollection col; + col.setMetaData(QContactCollection::KeyName, collectionName); + QVERIFY(cm->saveCollection(&col)); + } + + // check "collectionsAdded" signal + QCOMPARE(collectionsAddedSpy.count(), 1); + QList<QContactCollectionId> ids = collectionsAddedSpy.takeFirst().at(0).value<QList<QContactCollectionId> >(); + QCOMPARE(ids.count(), 1); + + // query for new collection + { + QContactCollection col = cm->collection(ids.at(0)); + QVERIFY(!col.id().isNull()); + QCOMPARE(col.id().toString(), ids.at(0).toString()); + QCOMPARE(col.metaData(QContactCollection::KeyName).toByteArray(), collectionName); + } +} + +void tst_QContactManager::modifyCollection() +{ + QFETCH(QString, uri); + QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri)); + + qRegisterMetaType<QList<QContactCollectionId> >("QList<QContactCollectionId>"); + QSignalSpy collectionsAddedSpy(cm.data(), SIGNAL(collectionsAdded(QList<QContactCollectionId>))); + QSignalSpy collectionsChangedSpy(cm.data(), SIGNAL(collectionsChanged(QList<QContactCollectionId>))); + + QContactCollectionId colId; + QByteArray collectionName = QUuid::createUuid().toByteArray(); + + // save a new collection + { + QContactCollection col; + col.setMetaData(QContactCollection::KeyName, collectionName); + QVERIFY(cm->saveCollection(&col)); + QTRY_COMPARE(collectionsAddedSpy.count(), 1); + colId = col.id(); + QVERIFY(!colId.isNull()); + } + + // edit collection + { + QCOMPARE(collectionsChangedSpy.count(), 0); + QContactCollection col = cm->collection(colId); + QByteArray newCollectionName = QUuid::createUuid().toByteArray(); + col.setMetaData(QContactCollection::KeyName, newCollectionName); + QVERIFY(cm->saveCollection(&col)); + + // check signal "collectionsChanged" fired contains the collection id + QTRY_COMPARE(collectionsChangedSpy.count(), 1); + QList<QContactCollectionId> ids = collectionsChangedSpy.takeFirst().at(0).value<QList<QContactCollectionId> >(); + QCOMPARE(ids.at(0).toString(), colId.toString()); + + // check if the collection name was updated + QContactCollection col2 = cm->collection(colId); + QCOMPARE(col2.metaData(QContactCollection::KeyName).toByteArray(), newCollectionName); + } +} + +void tst_QContactManager::removeCollection() +{ + QFETCH(QString, uri); + QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri)); + + qRegisterMetaType<QList<QContactCollectionId> >("QList<QContactCollectionId>"); + QSignalSpy collectionsAddedSpy(cm.data(), SIGNAL(collectionsAdded(QList<QContactCollectionId>))); + QSignalSpy collectionsRemovedSpy(cm.data(), SIGNAL(collectionsRemoved(QList<QContactCollectionId>))); + + QContactCollectionId colId; + + // save a new collection + { + QContactCollection col; + QByteArray collectionName = QUuid::createUuid().toByteArray(); + col.setMetaData(QContactCollection::KeyName, collectionName); + QVERIFY(cm->saveCollection(&col)); + QTRY_COMPARE(collectionsAddedSpy.count(), 1); + colId = col.id(); + } + QList<QContactCollection> collections = cm->collections(); + + // remove collection + cm->removeCollection(colId); + + // check "collectionsRemoved" signal + QTRY_COMPARE(collectionsRemovedSpy.count(), 1); + QList<QContactCollectionId> ids = collectionsRemovedSpy.takeFirst().at(0).value<QList<QContactCollectionId> >(); + QCOMPARE(ids.at(0).toString(), colId.toString()); + + + // check if the correct collection was removed + QList<QContactCollection> collectionsAfterRemoval = cm->collections(); + QCOMPARE(collections.count() - 1, collectionsAfterRemoval.count()); + Q_FOREACH (const QContactCollection &col, collectionsAfterRemoval) { + collections.removeAll(col); + } + QCOMPARE(collections.count(), 1); + QCOMPARE(collections.at(0).id().toString(), colId.toString()); +} + +void tst_QContactManager::saveContactIntoCollections() +{ + QFETCH(QString, uri); + QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri)); + + qRegisterMetaType<QList<QContactCollectionId> >("QList<QContactCollectionId>"); + QSignalSpy collectionsAddedSpy(cm.data(), SIGNAL(collectionsAdded(QList<QContactCollectionId>))); + QByteArray collectionName = QUuid::createUuid().toByteArray(); + QContactCollectionId colId; + QContactId cId; + + // create collection + { + QContactCollection col; + col.setMetaData(QContactCollection::KeyName, collectionName); + QVERIFY(cm->saveCollection(&col)); + QTRY_COMPARE(collectionsAddedSpy.count(), 1); + colId = col.id(); + } + + // create contact + { + QContact c = createContact("Alice", "Last", "12345"); + c.setCollectionId(colId); + cm->saveContact(&c); + cId = c.id(); + } + + // query new contact and check for collection + { + QContact c = cm->contact(cId); + QCOMPARE(c.collectionId().toString(), colId.toString()); + } +} + void tst_QContactManager::compareVariant_data() { QTest::addColumn<QVariant>("a"); diff --git a/tests/auto/contacts/qmlcontacts/qmlcontacts.pro b/tests/auto/contacts/qmlcontacts/qmlcontacts.pro index 540543d768f27cd09467c1a598bac0ee28851489..0a918772668826cdd79e2db051bb483b0ac842a5 100644 --- a/tests/auto/contacts/qmlcontacts/qmlcontacts.pro +++ b/tests/auto/contacts/qmlcontacts/qmlcontacts.pro @@ -12,8 +12,10 @@ OTHER_FILES += \ testcases/ContactsSavingTestCase.qml \ testcases/ContactsSignalingTestCase.qml \ testcases/ContactsTestHelper.qml \ + testcases/tst_collection.qml \ testcases/tst_contact_add_detail.qml \ testcases/tst_contact_addresses.qml \ + testcases/tst_contact_collection_filter.qml \ testcases/tst_contact_detail_access.qml \ testcases/tst_contactdetail.qml \ testcases/tst_contact_emails.qml \ diff --git a/tests/auto/contacts/qmlcontacts/testcases/ContactsSavingTestCase.qml b/tests/auto/contacts/qmlcontacts/testcases/ContactsSavingTestCase.qml index bc2911178398e398928e6c118f5fb1aac021d027..b5e2ded90015ca6c9287a68fab8afb8cd3241ca2 100644 --- a/tests/auto/contacts/qmlcontacts/testcases/ContactsSavingTestCase.qml +++ b/tests/auto/contacts/qmlcontacts/testcases/ContactsSavingTestCase.qml @@ -48,6 +48,7 @@ TestCase { id: contactsSavingTestCase property SignalSpy spy + property SignalSpy collectionSpy property bool debug: false ContactsTestConfiguration { @@ -69,6 +70,15 @@ TestCase { contactsSavingTestCase); spy.target = model; spy.signalName = "contactsChanged"; + + collectionSpy = Qt.createQmlObject( + "import QtTest 1.0;" + + "SignalSpy {" + + "}", + contactsSavingTestCase); + collectionSpy.target = model + collectionSpy.signalName = "collectionsChanged" + return spy; } @@ -83,6 +93,13 @@ TestCase { spy.wait(); } + // Verify that the collectionsChanged signal is emitted + function waitForCollectionsChanged() { + logDebug("waitForCollectionsChanged"); + collectionSpy.wait(); + } + + // Wait until duration has elapsed, or the contactsChanged signal is emitted function waitUntilContactsChanged(duration) { logDebug("waitUntilContactsChanged"); diff --git a/tests/auto/contacts/qmlcontacts/testcases/tst_contact_collection_filter.qml b/tests/auto/contacts/qmlcontacts/testcases/tst_contact_collection_filter.qml new file mode 100644 index 0000000000000000000000000000000000000000..074adb52affa11e071ce8712abdaba4b05c6e7e5 --- /dev/null +++ b/tests/auto/contacts/qmlcontacts/testcases/tst_contact_collection_filter.qml @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2015 Canonical Ltd +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtContacts 5.0 + +ContactsSavingTestCase { + id: testcase + name: "Contacts collection filter test" + + property string defaultCollectionId: "" + + ContactModel { + id: model + manager: getManagerUnderTest() + autoUpdate: true + } + + Collection { + id: testCollection + name: 'My collection filter test' + description: 'collection filter test' + } + + Contact { + id: contact1; + Name { + firstName: "A" + } + PhoneNumber { + number: "1111111111" + } + } + + Contact { + id: contact2; + Name { + firstName: "B" + } + PhoneNumber { + number: "2222222222" + } + } + + Contact { + id: contact3; + Name { + firstName: "John Joe" + } + PhoneNumber { + number: "3333333333" + } + } + + function createCollectionFilter(ids) + { + var filter = Qt.createQmlObject( + "import QtContacts 5.0;" + + "CollectionFilter {}", + testcase); + + filter.ids = ids + return filter; + } + + function initTestCase() { + initTestForModel(model); + waitUntilContactsChanged(); + compare(model.collections.length, 1); + defaultCollectionId = model.collections[0].collectionId + // The wait is needed so the model is populated + // (e.g. with garbage left from previous test runs) + // before cleanup() is called. + emptyContacts(model); + model.saveCollection(testCollection) + waitForCollectionsChanged(); + compare(model.collections.length, 2); + model.saveContact(contact1); + waitForContactsChanged(); + compare(contact1.collectionId, defaultCollectionId); + contact2.collectionId = testCollection.collectionId + model.saveContact(contact2); + waitForContactsChanged(); + contact3.collectionId = testCollection.collectionId + model.saveContact(contact3); + waitForContactsChanged(); + compare(model.contacts.length, 3); + } + + function test_collectionFilter(data) { + var filterDefaultCollection = createCollectionFilter([defaultCollectionId]); + model.filter = filterDefaultCollection; + waitForContactsChanged(); + compare(model.contacts.length, 1); + + var filterOnlyNewCollection = createCollectionFilter([testCollection.collectionId]); + model.filter = filterOnlyNewCollection; + waitForContactsChanged(); + compare(model.contacts.length, 2); + + var filterNewCollectionAndDefaultCollection = createCollectionFilter([defaultCollectionId, testCollection.collectionId]) + model.filter = filterNewCollectionAndDefaultCollection; + waitForContactsChanged(); + compare(model.contacts.length, 3); + + var filterEmpty = createCollectionFilter([]) + model.filter = filterEmpty; + waitForContactsChanged(); + compare(model.contacts.length, 0); + + var filterValidAndInvalidIds = createCollectionFilter([defaultCollectionId, "12345678", testCollection.collectionId]) + model.filter = filterValidAndInvalidIds; + waitForContactsChanged(); + compare(model.contacts.length, 3); + + var filterWithInvalidId = createCollectionFilter(["12345678"]) + model.filter = filterWithInvalidId; + waitForContactsChanged(); + compare(model.contacts.length, 0); + } +}