plugins/contacts/symbian/plugin/src/cntsymbianengine.cpp
changeset 0 876b1a06bc25
child 5 603d3f8b6302
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 #include <QFileSystemWatcher>
       
    42 #include <QFile>
       
    43 #include <QUuid>
       
    44 #include <QTimer>
       
    45 
       
    46 #include <QDebug>
       
    47 
       
    48 #include <qtcontacts.h>
       
    49 #include <qcontactname.h>
       
    50 
       
    51 #include "cntsymbianengine.h"
       
    52 #include "qcontactchangeset.h"
       
    53 #include "cntsymbiandatabase.h"
       
    54 #include "cnttransformcontact.h"
       
    55 #include "cntsymbiantransformerror.h"
       
    56 #include "cntsymbianfilterdbms.h"
       
    57 #include "cntsymbianfiltersql.h"
       
    58 #include "cntsymbiansorterdbms.h"
       
    59 #include "cntrelationship.h"
       
    60 #include "cntdisplaylabel.h"
       
    61 #include "cntsymbiansrvconnection.h"
       
    62 
       
    63 typedef QList<QContactLocalId> QContactLocalIdList;
       
    64 typedef QPair<QContactLocalId, QContactLocalId> QOwnCardPair;
       
    65 
       
    66 // NOTE: There is a bug with RVCT compiler which causes the local stack
       
    67 // variable to corrupt if the called function leaves. As a workaround we are
       
    68 // reserving the objects from heap so it will not get corrupted.
       
    69 // This of course applies only to those stack variables which are passed to
       
    70 // the called function or the return value of the function is placed to the
       
    71 // variable
       
    72 
       
    73 /* ... The macros changed names */
       
    74 #if QT_VERSION < QT_VERSION_CHECK(4, 6, 0)
       
    75 #define QT_TRAP_THROWING QT_TRANSLATE_SYMBIAN_LEAVE_TO_EXCEPTION
       
    76 #define QT_TRYCATCH_LEAVING QT_TRANSLATE_EXCEPTION_TO_SYMBIAN_LEAVE
       
    77 #endif
       
    78 
       
    79 CntSymbianEngine::CntSymbianEngine(const QMap<QString, QString>& parameters, QContactManager::Error* error)
       
    80     : m_dataBase(0),
       
    81       m_srvConnection(0),
       
    82       m_managerUri(0),
       
    83       m_transformContact(0),
       
    84       m_contactFilter(0),
       
    85 #ifndef SYMBIAN_BACKEND_USE_SQLITE
       
    86       m_contactSorter(0),
       
    87 #endif
       
    88       m_relationship(0),
       
    89       m_displayLabel(0)
       
    90 {
       
    91     *error = QContactManager::NoError;
       
    92 
       
    93     m_dataBase = new CntSymbianDatabase(this, error);
       
    94     
       
    95     // Database opened successfully
       
    96     if (*error == QContactManager::NoError) {
       
    97         m_managerUri = QContactManager::buildUri(CNT_SYMBIAN_MANAGER_NAME, parameters);
       
    98         m_transformContact = new CntTransformContact;
       
    99         m_srvConnection    = new CntSymbianSrvConnection(this);
       
   100 #ifdef SYMBIAN_BACKEND_USE_SQLITE
       
   101         m_contactFilter    = new CntSymbianFilter(*this, *m_dataBase->contactDatabase(), *m_srvConnection, *m_transformContact);
       
   102 #else
       
   103         m_contactFilter    = new CntSymbianFilter(*m_dataBase->contactDatabase());
       
   104         m_contactSorter    = new CntSymbianSorterDbms(*m_dataBase->contactDatabase(), *m_transformContact);
       
   105 #endif
       
   106         m_relationship     = new CntRelationship(m_dataBase->contactDatabase(), m_managerUri);
       
   107         m_displayLabel     = new CntDisplayLabel();
       
   108 #ifdef SYMBIAN_BACKEND_USE_SQLITE
       
   109         connect(m_displayLabel, SIGNAL(displayLabelChanged()), this, SIGNAL(dataChanged()));
       
   110 #endif
       
   111     }
       
   112 }
       
   113 
       
   114 CntSymbianEngine::~CntSymbianEngine()
       
   115 {
       
   116     delete m_contactFilter; // needs to be deleted before database
       
   117     delete m_dataBase;
       
   118     delete m_srvConnection;
       
   119     delete m_transformContact;
       
   120 #ifndef SYMBIAN_BACKEND_USE_SQLITE
       
   121     delete m_contactSorter;
       
   122 #endif    
       
   123     delete m_relationship;
       
   124     delete m_displayLabel;
       
   125 }
       
   126 
       
   127 /*!
       
   128  * Returns a list of the ids of contacts that match the supplied \a filter, sorted according to the given \a sortOrders.
       
   129  * Any error that occurs will be stored in \a error. Uses either the Symbian backend native filtering or in case of an
       
   130  * unsupported filter, the generic (slow) filtering of QContactManagerEngine.
       
   131  */
       
   132 QList<QContactLocalId> CntSymbianEngine::contactIds(
       
   133         const QContactFilter& filter,
       
   134         const QList<QContactSortOrder>& sortOrders,
       
   135         QContactManager::Error* error) const
       
   136 {
       
   137     *error = QContactManager::NoError;
       
   138     QList<QContactLocalId> result;
       
   139     
       
   140     bool filterSupported(true);
       
   141     result = m_contactFilter->contacts(filter, sortOrders, filterSupported, error);
       
   142             
       
   143 #ifdef SYMBIAN_BACKEND_USE_SQLITE
       
   144     
       
   145         // Remove possible false positives
       
   146         if(!filterSupported && *error == QContactManager::NotSupportedError)
       
   147             {
       
   148             //fetch all contacts
       
   149             *error = QContactManager::NoError;
       
   150             result = m_contactFilter->contacts(QContactFilter(),sortOrders, filterSupported, error);
       
   151             
       
   152             //slow filtering
       
   153             result = slowFilter(filter, result, error);
       
   154             
       
   155             //slow sorting until it's supported in SQL requests
       
   156             result = slowSort(result, sortOrders, error);
       
   157             }
       
   158         
       
   159 #else
       
   160         // Remove possible false positives
       
   161         if(!filterSupported && *error == QContactManager::NoError)
       
   162             result = slowFilter(filter, result, error);
       
   163         
       
   164         // Sort the matching contacts
       
   165         if(!sortOrders.isEmpty()&& *error == QContactManager::NoError ) {
       
   166             if(m_contactSorter->sortOrderSupported(sortOrders)) {
       
   167                 result = m_contactSorter->sort(result, sortOrders, error);
       
   168             } else {
       
   169                 result = slowSort(result, sortOrders, error);
       
   170             }
       
   171         }
       
   172 #endif
       
   173 
       
   174     return result;
       
   175 }
       
   176 
       
   177 QList<QContact> CntSymbianEngine::contacts(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders, const QContactFetchHint& fh, QContactManager::Error* error) const
       
   178 {
       
   179     *error = QContactManager::NoError;
       
   180 
       
   181     // special case: use optimized fetch if
       
   182     //   * only display labels are requested
       
   183     //   * the filter is a detail filter for QContactType's TypeContact
       
   184     //   * there are no sort orders
       
   185     QStringList detailRestrictions = fh.detailDefinitionsHint();
       
   186     if (detailRestrictions.count() == 1 &&
       
   187         detailRestrictions.at(0) == QContactDisplayLabel::DefinitionName &&
       
   188         filter.type() == QContactFilter::ContactDetailFilter &&
       
   189         QContactDetailFilter(filter).detailDefinitionName() == QContactType::DefinitionName &&
       
   190         QContactDetailFilter(filter).value().toString() == QContactType::TypeContact &&
       
   191         sortOrders.isEmpty()) {
       
   192         return m_srvConnection->searchAllContactNames(error);
       
   193     }
       
   194 
       
   195     QList<QContact> contacts;
       
   196     QList<QContactLocalId> contactIds = this->contactIds(filter, sortOrders, error);
       
   197     if (*error == QContactManager::NoError ) {
       
   198         foreach (QContactLocalId id, contactIds) {
       
   199             QContact contact = this->contact(id, fh, error);
       
   200             if (*error != QContactManager::NoError) {
       
   201                 return QList<QContact>(); // return empty list if error occurred
       
   202             }
       
   203             contacts.append(contact);
       
   204         }
       
   205     }
       
   206 
       
   207     return contacts;
       
   208 }
       
   209 
       
   210 /*!
       
   211  * Read a contact from the contact database.
       
   212  *
       
   213  * \param contactId The Id of the contact to be retrieved.
       
   214  * \param error Qt error code.
       
   215  * \return A QContact for the requested QContactLocalId value or 0 if the read
       
   216  *  operation was unsuccessful (e.g. contact not found).
       
   217  */
       
   218 QContact CntSymbianEngine::contact(const QContactLocalId& contactId, const QContactFetchHint& fetchHint, QContactManager::Error* error) const
       
   219 {
       
   220     // special case: use optimized fetch if only display label is requested
       
   221     QStringList detailRestrictions = fetchHint.detailDefinitionsHint();
       
   222     if (detailRestrictions.count() == 1 &&
       
   223         detailRestrictions.at(0) == QContactDisplayLabel::DefinitionName) {
       
   224         return m_srvConnection->searchContactName(contactId, error);
       
   225     }
       
   226     
       
   227     QContact* contact = new QContact();
       
   228     TRAPD(err, *contact = fetchContactL(contactId, detailRestrictions));
       
   229     CntSymbianTransformError::transformError(err, error);
       
   230 
       
   231     if(*error == QContactManager::NoError) {
       
   232         updateDisplayLabel(*contact);
       
   233         //check relationship only if there are no definition restrictions, otherwise
       
   234         //skip this time expensive operation.       
       
   235         if( (!fetchHint.optimizationHints() & QContactFetchHint::NoRelationships)) {
       
   236             QContactManager::Error relationshipError;
       
   237             // XXX can also consult fetchHint.relationships list
       
   238             QList<QContactRelationship> relationships = this->relationships(QString(), contact->id(), QContactRelationship::Either, &relationshipError);
       
   239             if (relationshipError != QContactManager::NoError &&
       
   240                 relationshipError != QContactManager::DoesNotExistError) { // means that no relationships found
       
   241                 *error = relationshipError;
       
   242             }
       
   243             QContactManagerEngine::setContactRelationships(contact, relationships);
       
   244         }
       
   245     }
       
   246     return *QScopedPointer<QContact>(contact);
       
   247 }
       
   248 
       
   249 bool CntSymbianEngine::saveContact(QContact* contact, QContactManager::Error* error)
       
   250 {
       
   251     QContactChangeSet changeSet;
       
   252     bool ret = doSaveContact(contact, changeSet, error);
       
   253     changeSet.emitSignals(this);
       
   254     return ret;
       
   255 }
       
   256 
       
   257 /*! \reimp */
       
   258 bool CntSymbianEngine::saveContacts(QList<QContact> *contacts, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error* error)
       
   259 {
       
   260     *error = QContactManager::NoError;
       
   261     
       
   262     if (errorMap) {
       
   263         // if the errormap argument is null, we just don't do fine-grained reporting.            
       
   264         errorMap->clear();
       
   265     }    
       
   266     
       
   267     if (!contacts) {
       
   268         *error = QContactManager::BadArgumentError;
       
   269         return false;
       
   270     }
       
   271 
       
   272     QContactChangeSet changeSet;
       
   273     for (int i = 0; i < contacts->count(); i++) {
       
   274         QContact current = contacts->at(i);
       
   275         QContactManager::Error functionError = QContactManager::NoError;
       
   276         if (!doSaveContact(&current, changeSet, &functionError)) {
       
   277             *error = functionError;
       
   278             if (errorMap) {
       
   279                 errorMap->insert(i, functionError);
       
   280             }
       
   281         } else {
       
   282             (*contacts)[i] = current;
       
   283         }
       
   284     }
       
   285     changeSet.emitSignals(this);
       
   286     return (*error == QContactManager::NoError);
       
   287 }
       
   288 
       
   289 /*!
       
   290  * Uses the generic filtering implementation of QContactManagerEngine to filter
       
   291  * contacts one-by-one. Really slow when filtering a lot of contacts because
       
   292  * every contact needs to be loaded from the database before filtering.
       
   293  */
       
   294 QList<QContactLocalId> CntSymbianEngine::slowFilter(
       
   295         const QContactFilter& filter,
       
   296         const QList<QContactLocalId>& contacts,
       
   297         QContactManager::Error* error
       
   298         ) const
       
   299 {
       
   300     QList<QContactLocalId> result;
       
   301     for (int i(0); i < contacts.count(); i++) {
       
   302         QContactLocalId id = contacts.at(i);
       
   303 
       
   304         // Check if this is a false positive. If not, add to the result set.
       
   305         if(QContactManagerEngine::testFilter(filter, contact(id, QContactFetchHint(), error)))
       
   306             result << id;
       
   307     }
       
   308     return result;
       
   309 }
       
   310 
       
   311 QList<QContactLocalId> CntSymbianEngine::slowSort(
       
   312         const QList<QContactLocalId>& contactIds,
       
   313         const QList<QContactSortOrder>& sortOrders,
       
   314         QContactManager::Error* error) const
       
   315 {
       
   316     // Get unsorted contacts
       
   317     QList<QContact> unsortedContacts;
       
   318     foreach (QContactLocalId id, contactIds) {
       
   319         QContact c = contact(id, QContactFetchHint(), error);
       
   320         if (*error != QContactManager::NoError)
       
   321             return QList<QContactLocalId>();
       
   322         unsortedContacts << c;
       
   323     }
       
   324     return QContactManagerEngine::sortContacts(unsortedContacts, sortOrders);
       
   325 }
       
   326 
       
   327 bool CntSymbianEngine::doSaveContact(QContact* contact, QContactChangeSet& changeSet, QContactManager::Error* error)
       
   328 {
       
   329     bool ret = false;
       
   330     if(contact && !validateContact(*contact, error))
       
   331         return false;
       
   332 
       
   333     // If contact has GUid and no local Id, try to find it in database
       
   334     if (contact && !contact->localId() &&
       
   335         contact->details(QContactGuid::DefinitionName).count() > 0) {
       
   336         QContactDetailFilter guidFilter;
       
   337         guidFilter.setDetailDefinitionName(QContactGuid::DefinitionName, QContactGuid::FieldGuid);
       
   338         QContactGuid guidDetail = static_cast<QContactGuid>(contact->details(QContactGuid::DefinitionName).at(0));
       
   339         guidFilter.setValue(guidDetail.guid());
       
   340 
       
   341         QContactManager::Error err;
       
   342         QList<QContactLocalId> localIdList = contactIds(guidFilter,
       
   343                 QList<QContactSortOrder>(), &err);
       
   344         if (err == QContactManager::NoError && localIdList.count() > 0) {
       
   345             QScopedPointer<QContactId> contactId(new QContactId());
       
   346             contactId->setLocalId(localIdList.at(0));
       
   347             contactId->setManagerUri(m_managerUri);
       
   348             contact->setId(*contactId);
       
   349         }
       
   350     }
       
   351 
       
   352     // Check parameters
       
   353     if(!contact) {
       
   354         *error = QContactManager::BadArgumentError;
       
   355         ret = false;
       
   356     // Update an existing contact
       
   357     } else if(contact->localId()) {
       
   358         if(contact->id().managerUri() == m_managerUri) {
       
   359             ret = updateContact(*contact, changeSet, error);
       
   360         } else {
       
   361             *error = QContactManager::BadArgumentError;
       
   362             ret = false;
       
   363         }
       
   364     // Create new contact
       
   365     } else {
       
   366         ret = addContact(*contact, changeSet, error);
       
   367     }
       
   368 
       
   369     if (ret)
       
   370         updateDisplayLabel(*contact);
       
   371 
       
   372     return ret;
       
   373 }
       
   374 
       
   375 /*!
       
   376  * Private leaving implementation for contact()
       
   377  */
       
   378 QContact CntSymbianEngine::fetchContactL(const QContactLocalId &localId, const QStringList& detailDefinitionsHint) const
       
   379 {
       
   380     // A contact with a zero id is not expected to exist.
       
   381     // Symbian contact database uses id 0 internally as the id of the
       
   382     // system template.
       
   383     if(localId == 0)
       
   384         User::Leave(KErrNotFound);
       
   385 
       
   386     // Read the contact from the CContactDatabase
       
   387     CContactItem* contactItem(0);
       
   388     if (!detailDefinitionsHint.isEmpty()) {
       
   389         // Create a view definition with only the fields that map to the fetch hint
       
   390         CContactItemViewDef *viewDef = CContactItemViewDef::NewLC(
       
   391             CContactItemViewDef::EIncludeFields, CContactItemViewDef::EMaskHiddenFields);
       
   392         foreach (QString detailDefinitionHint, detailDefinitionsHint) {
       
   393             QList<TUid> uids = m_transformContact->itemFieldUidsL(detailDefinitionHint);
       
   394             foreach (TUid uid, uids) {
       
   395                 viewDef->AddL(uid);
       
   396             }
       
   397         }
       
   398         contactItem = m_dataBase->contactDatabase()->ReadContactL(localId, *viewDef);
       
   399         CleanupStack::PopAndDestroy(viewDef);
       
   400     } else {
       
   401         // The fetch hint does not contain detail definitions hint so get all
       
   402         // the contact item fields that are available
       
   403         contactItem = m_dataBase->contactDatabase()->ReadContactL(localId);
       
   404     }
       
   405     CleanupStack::PushL(contactItem);
       
   406 
       
   407     // Convert to a QContact
       
   408     QContact contact = m_transformContact->transformContactL(*contactItem);
       
   409 
       
   410     // Transform details that are not available until the contact has been saved
       
   411     m_transformContact->transformPostSaveDetailsL(*contactItem, contact, *m_dataBase->contactDatabase(), m_managerUri);
       
   412 
       
   413     CleanupStack::PopAndDestroy(contactItem);
       
   414 
       
   415     return contact;
       
   416 }
       
   417 
       
   418 /*!
       
   419  * Add the specified contact item to the persistent contacts store.
       
   420  *
       
   421  * \param contact The QContact to be saved.
       
   422  * \param id The Id of new contact
       
   423  * \param qtError Qt error code.
       
   424  * \return Error status
       
   425  */
       
   426 bool CntSymbianEngine::addContact(QContact& contact, QContactChangeSet& changeSet, QContactManager::Error* qtError)
       
   427 {
       
   428     // Attempt to persist contact, trapping errors
       
   429     int err(0);
       
   430     QContactLocalId id(0);
       
   431     TRAP(err, id = addContactL(contact));
       
   432 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK
       
   433     if(err == KErrNone)
       
   434     {
       
   435         changeSet.insertAddedContact(id);
       
   436         m_dataBase->appendContactEmitted(id);
       
   437     }
       
   438 #endif
       
   439     CntSymbianTransformError::transformError(err, qtError);
       
   440     return (err==KErrNone);
       
   441 }
       
   442 
       
   443 /*!
       
   444  * Private leaving implementation for addContact()
       
   445  *
       
   446  * \param contact The contact item to save in the database.
       
   447  * \return The new contact ID.
       
   448  */
       
   449 int CntSymbianEngine::addContactL(QContact &contact)
       
   450 {
       
   451     int id(0);
       
   452 
       
   453     //handle normal contact
       
   454     if(contact.type() == QContactType::TypeContact)
       
   455     {
       
   456         // Create a new contact card.
       
   457         CContactItem* contactItem = CContactCard::NewLC();
       
   458         m_transformContact->transformContactL(contact, *contactItem);
       
   459 
       
   460         // Add to the database
       
   461         id = m_dataBase->contactDatabase()->AddNewContactL(*contactItem);
       
   462 
       
   463         // Reload contact item
       
   464         CleanupStack::PopAndDestroy(contactItem);
       
   465         contactItem = 0;
       
   466         contactItem = m_dataBase->contactDatabase()->ReadContactLC(id);
       
   467 
       
   468         // Transform details that are not available until the contact has been saved
       
   469         m_transformContact->transformPostSaveDetailsL(*contactItem, contact, *m_dataBase->contactDatabase(), m_managerUri);
       
   470         CleanupStack::PopAndDestroy(contactItem);
       
   471     }
       
   472     //group contact
       
   473     else if(contact.type() == QContactType::TypeGroup)
       
   474     {
       
   475         // Create a new group, which is added to the database
       
   476         CContactItem* contactItem = m_dataBase->contactDatabase()->CreateContactGroupLC();
       
   477 
       
   478         //set the id for the contact, needed by update
       
   479         id = contactItem->Id();
       
   480         QScopedPointer<QContactId> contactId(new QContactId());
       
   481         contactId->setLocalId(QContactLocalId(id));
       
   482         contactId->setManagerUri(m_managerUri);
       
   483         contact.setId(*contactId);
       
   484         CleanupStack::PopAndDestroy(contactItem);
       
   485         contactItem = 0;
       
   486 
       
   487         //update contact, will add the fields to the already saved group
       
   488         updateContactL(contact);
       
   489 
       
   490         // Transform details that are not available until the contact has been saved
       
   491         contactItem = m_dataBase->contactDatabase()->ReadContactLC(id);
       
   492         m_transformContact->transformPostSaveDetailsL(*contactItem, contact, *m_dataBase->contactDatabase(), m_managerUri);
       
   493         CleanupStack::PopAndDestroy(contactItem);
       
   494     }
       
   495     // Leave with an error
       
   496     else
       
   497     {
       
   498         User::Leave(KErrInvalidContactDetail);
       
   499     }
       
   500 
       
   501     // Return the new ID.
       
   502     return id;
       
   503 }
       
   504 
       
   505 /*!
       
   506  * Update an existing contact entry in the database.
       
   507  *
       
   508  * \param contact The contact to update in the database.
       
   509  * \param qtError Qt error code.
       
   510  * \return Error status.
       
   511  */
       
   512 bool CntSymbianEngine::updateContact(QContact& contact, QContactChangeSet& changeSet, QContactManager::Error* qtError)
       
   513 {
       
   514     int err(0);
       
   515     TRAP(err, updateContactL(contact));
       
   516 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK
       
   517     if(err == KErrNone)
       
   518     {
       
   519         //TODO: check what to do with groupsChanged
       
   520         changeSet.insertChangedContact(contact.localId());
       
   521         m_dataBase->appendContactEmitted(contact.localId());
       
   522     }
       
   523 #endif
       
   524     CntSymbianTransformError::transformError(err, qtError);
       
   525     return (err==KErrNone);
       
   526 }
       
   527 
       
   528 /*!
       
   529  * Private leaving implementation for updateContact()
       
   530  *
       
   531  * \param contact The contact to update in the database.
       
   532  */
       
   533 void CntSymbianEngine::updateContactL(QContact &contact)
       
   534 {
       
   535     // Need to open the contact for write, leaving this item
       
   536     // on the cleanup stack to unlock the item in the event of a leave.
       
   537     CContactItem* contactItem = m_dataBase->contactDatabase()->OpenContactLX(contact.localId());
       
   538     CleanupStack::PushL(contactItem);
       
   539 
       
   540     // Cannot update contact type. The client needs to do this itself.
       
   541     if ((contact.type() == QContactType::TypeContact && contactItem->Type() != KUidContactCard &&
       
   542             contactItem->Type() != KUidContactOwnCard) ||
       
   543         (contact.type() == QContactType::TypeGroup && contactItem->Type() != KUidContactGroup)){
       
   544         User::Leave(KErrAlreadyExists);
       
   545     }
       
   546     
       
   547     // Copy the data from QContact to CContactItem
       
   548     m_transformContact->transformContactL(contact, *contactItem);
       
   549 
       
   550     // Write the entry using the converted  contact
       
   551     // note commitContactL removes empty fields from the contact
       
   552     m_dataBase->contactDatabase()->CommitContactL(*contactItem);
       
   553 
       
   554     // Update "last modified" time stamp; the contact item needs to be
       
   555     // explicitly refreshed by reading it again from the database
       
   556     CleanupStack::PopAndDestroy(contactItem);
       
   557     contactItem = 0;
       
   558     contactItem = m_dataBase->contactDatabase()->ReadContactLC(contact.localId());
       
   559     m_transformContact->transformPostSaveDetailsL(*contactItem, contact, *m_dataBase->contactDatabase(), m_managerUri);
       
   560 
       
   561     updateDisplayLabel(contact);
       
   562 
       
   563     CleanupStack::PopAndDestroy(contactItem);
       
   564     CleanupStack::PopAndDestroy(1); // commit lock
       
   565 }
       
   566 
       
   567 /*!
       
   568  * Remove the specified contact object from the database.
       
   569  *
       
   570  * The removal of contacts from the underlying contacts model database
       
   571  * is performed in transactions of maximum 50 items at a time. E.g.
       
   572  * deleting 177 contacts would be done in 3 transactions of 50 and a
       
   573  * final transaction of 27.
       
   574  *
       
   575  * \param contact The QContact to be removed.
       
   576  * \param qtError Qt error code.
       
   577  * \return Error status.
       
   578  */
       
   579 bool CntSymbianEngine::removeContact(const QContactLocalId &id, QContactChangeSet& changeSet, QContactManager::Error* qtError)
       
   580 {
       
   581     // removeContactL() can't throw c++ exception
       
   582     TRAPD(err, removeContactL(id));
       
   583 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK
       
   584     if(err == KErrNone)
       
   585     {
       
   586         //TODO: check what to do with groupsChanged?
       
   587         changeSet.insertRemovedContact(id);
       
   588         m_dataBase->appendContactEmitted(id);
       
   589     }
       
   590 #endif
       
   591     CntSymbianTransformError::transformError(err, qtError);
       
   592     return (err==KErrNone);
       
   593 }
       
   594 
       
   595 /*!
       
   596  * Private leaving implementation for removeContact
       
   597  */
       
   598 void CntSymbianEngine::removeContactL(QContactLocalId id)
       
   599 {
       
   600     // A contact with a zero id is not expected to exist.
       
   601     // Symbian contact database uses id 0 internally as the id of the
       
   602     // system template.
       
   603     if(id == 0)
       
   604         User::Leave(KErrNotFound);
       
   605 
       
   606     //TODO: in future QContactLocalId will be a class so this will need to be changed.
       
   607     TContactItemId cId = static_cast<TContactItemId>(id);
       
   608 
       
   609     //TODO: add code to remove all relationships.
       
   610 
       
   611     m_dataBase->contactDatabase()->DeleteContactL(cId);
       
   612 #ifdef SYMBIAN_BACKEND_S60_VERSION_32
       
   613     // In S60 3.2 hardware (observerd with N96) there is a problem when saving and 
       
   614     // deleting contacts in quick successive manner. At some point the database
       
   615     // starts leaving with KErrNotReady (-18). This happens randomly at either
       
   616     // DeleteContactL() or AddNewContactL(). The only only thing that seems to
       
   617     // help is to compress the database after deleting a contact. 
       
   618     // 
       
   619     // Needles to say that this will have a major negative effect on performance!
       
   620     // TODO: A better solution must be found.
       
   621     m_dataBase->contactDatabase()->CompactL();
       
   622 #endif
       
   623 }
       
   624 
       
   625 bool CntSymbianEngine::removeContact(const QContactLocalId& contactId, QContactManager::Error* error)
       
   626 {
       
   627     QContactManager::Error err;
       
   628     QContactLocalId selfCntId = selfContactId(&err); // err ignored
       
   629     QContactChangeSet changeSet;
       
   630     TBool ret = removeContact(contactId, changeSet, error);
       
   631 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK
       
   632     if (ret && contactId == selfCntId ) {
       
   633         QOwnCardPair ownCard(selfCntId, QContactLocalId(0));
       
   634         changeSet.setOldAndNewSelfContactId(ownCard);
       
   635     }
       
   636     changeSet.emitSignals(this);
       
   637 #endif
       
   638     return ret;
       
   639 }
       
   640 
       
   641 void CntSymbianEngine::updateDisplayLabel(QContact& contact) const
       
   642 {
       
   643     QContactManager::Error error(QContactManager::NoError);
       
   644     QString label = synthesizedDisplayLabel(contact, &error);
       
   645     if(error == QContactManager::NoError) {
       
   646         setContactDisplayLabel(&contact, label);
       
   647     }
       
   648 }
       
   649 
       
   650 bool CntSymbianEngine::removeContacts(const QList<QContactLocalId>& contactIds, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error* error)
       
   651 {
       
   652     *error = QContactManager::NoError;
       
   653     
       
   654     if (errorMap) {
       
   655         // if the errormap argument is null, we just don't do fine-grained reporting.            
       
   656         errorMap->clear();
       
   657     }
       
   658     
       
   659     if (contactIds.count() == 0) {
       
   660         *error = QContactManager::BadArgumentError;
       
   661         return false;
       
   662     }
       
   663     
       
   664     QContactManager::Error err;
       
   665     QContactLocalId selfCntId = selfContactId(&err); // err ignored
       
   666 
       
   667     QContactChangeSet changeSet;
       
   668     for (int i = 0; i < contactIds.count(); i++) {
       
   669         QContactLocalId current = contactIds.at(i);
       
   670         QContactManager::Error functionError = QContactManager::NoError;
       
   671         if (!removeContact(current, changeSet, &functionError)) {
       
   672             *error = functionError;
       
   673             if (errorMap) {
       
   674                 errorMap->insert(i, functionError);
       
   675             }
       
   676         }
       
   677 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK
       
   678         else {
       
   679             if (current == selfCntId ) {
       
   680                 QOwnCardPair ownCard(selfCntId, QContactLocalId(0));
       
   681                 changeSet.setOldAndNewSelfContactId(ownCard);
       
   682             }
       
   683         }
       
   684 #endif
       
   685     }
       
   686     changeSet.emitSignals(this);
       
   687     return (*error == QContactManager::NoError);
       
   688 }
       
   689 
       
   690 /* relationships */
       
   691 
       
   692 bool CntSymbianEngine::isRelationshipTypeSupported(const QString &relationshipType, const QString &contactType) const
       
   693 {
       
   694     return m_relationship->isRelationshipTypeSupported(relationshipType, contactType);
       
   695 }
       
   696 
       
   697 QList<QContactRelationship> CntSymbianEngine::relationships(const QString& relationshipType, const QContactId& participantId, QContactRelationship::Role role, QContactManager::Error* error) const
       
   698 {
       
   699     //retrieve the relationships
       
   700     return m_relationship->relationships(relationshipType, participantId, role, error);
       
   701 }
       
   702 
       
   703 bool CntSymbianEngine::saveRelationship(QContactRelationship* relationship, QContactManager::Error* error)
       
   704 {
       
   705     //save the relationship
       
   706     QSet<QContactLocalId> affectedContactIds;
       
   707     bool returnValue = m_relationship->saveRelationship(&affectedContactIds, relationship, error);
       
   708 
       
   709 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK
       
   710     //affected contacts
       
   711     QContactChangeSet changeSet;
       
   712     changeSet.insertAddedRelationshipsContacts(affectedContactIds.toList());
       
   713 
       
   714     //add contacts to the list that shouldn't be emitted
       
   715     m_dataBase->appendContactsEmitted(affectedContactIds.toList());
       
   716 
       
   717     //emit signals
       
   718     changeSet.emitSignals(this);
       
   719 #endif
       
   720 
       
   721     return returnValue;
       
   722 }
       
   723 
       
   724 bool CntSymbianEngine::saveRelationships(QList<QContactRelationship>* relationships, QMap<int, QContactManager::Error>* errorMap, QContactManager::Error* error)
       
   725 {
       
   726     //save the relationships
       
   727     QSet<QContactLocalId> affectedContactIds;
       
   728     bool returnValue = m_relationship->saveRelationships(&affectedContactIds, relationships, errorMap, error);
       
   729     
       
   730 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK
       
   731     //affected contacts
       
   732     QContactChangeSet changeSet;
       
   733     changeSet.insertAddedRelationshipsContacts(affectedContactIds.toList());
       
   734 
       
   735     //add contacts to the list that shouldn't be emitted
       
   736     m_dataBase->appendContactsEmitted(affectedContactIds.toList());
       
   737 
       
   738     //emit signals
       
   739     changeSet.emitSignals(this);
       
   740 #endif
       
   741 
       
   742     return returnValue;
       
   743 }
       
   744 
       
   745 bool CntSymbianEngine::removeRelationship(const QContactRelationship& relationship, QContactManager::Error* error)
       
   746 {
       
   747     //remove the relationship
       
   748     QSet<QContactLocalId> affectedContactIds;
       
   749     bool returnValue = m_relationship->removeRelationship(&affectedContactIds, relationship, error);
       
   750     
       
   751 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK
       
   752     //affected contacts
       
   753     QContactChangeSet changeSet;
       
   754     changeSet.insertRemovedRelationshipsContacts(affectedContactIds.toList());
       
   755 
       
   756     //add contacts to the list that shouldn't be emitted
       
   757     m_dataBase->appendContactsEmitted(affectedContactIds.toList());
       
   758 
       
   759     //emit signals
       
   760     changeSet.emitSignals(this);
       
   761 #endif
       
   762 
       
   763     return returnValue;
       
   764 }
       
   765 
       
   766 bool CntSymbianEngine::removeRelationships(const QList<QContactRelationship>& relationships, QMap<int, QContactManager::Error>* errorMap, QContactManager::Error* error)
       
   767 {
       
   768     //remove the relationships
       
   769     QSet<QContactLocalId> affectedContactIds;
       
   770     bool returnValue = m_relationship->removeRelationships(&affectedContactIds, relationships, errorMap, error);
       
   771     
       
   772 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK
       
   773     //affected contacts
       
   774     QContactChangeSet changeSet;
       
   775     changeSet.insertRemovedRelationshipsContacts(affectedContactIds.toList());
       
   776 
       
   777     //add contacts to the list that shouldn't be emitted
       
   778     m_dataBase->appendContactsEmitted(affectedContactIds.toList());
       
   779 
       
   780     //emit signals
       
   781     changeSet.emitSignals(this);
       
   782 #endif
       
   783 
       
   784     return returnValue;
       
   785 }
       
   786 
       
   787 QMap<QString, QContactDetailDefinition> CntSymbianEngine::detailDefinitions(const QString& contactType, QContactManager::Error* error) const
       
   788 {
       
   789     if (contactType != QContactType::TypeContact && contactType != QContactType::TypeGroup) {
       
   790         *error = QContactManager::InvalidContactTypeError;
       
   791         return QMap<QString, QContactDetailDefinition>();
       
   792     }
       
   793 
       
   794     *error = QContactManager::NoError;
       
   795 
       
   796     // First get the default definitions
       
   797     QMap<QString, QMap<QString, QContactDetailDefinition> > schemaDefinitions = QContactManagerEngine::schemaDefinitions();
       
   798 
       
   799     // And then ask contact transformer to do the modifications required
       
   800     QMap<QString, QContactDetailDefinition> schemaForType = schemaDefinitions.value(contactType);
       
   801     m_transformContact->detailDefinitions(schemaForType, contactType, error);
       
   802 
       
   803     return schemaForType;
       
   804 }
       
   805 
       
   806 bool CntSymbianEngine::hasFeature(QContactManager::ManagerFeature feature, const QString& contactType) const
       
   807 {
       
   808     bool returnValue(false);
       
   809 
       
   810     // TODO: update for SIM contacts later
       
   811     if (contactType != QContactType::TypeContact && contactType != QContactType::TypeGroup)
       
   812         return false;
       
   813 
       
   814     switch (feature) {
       
   815         /*
       
   816         TODO:
       
   817         How about the others? like:
       
   818         Groups,
       
   819         ActionPreferences,
       
   820         MutableDefinitions,
       
   821         Relationships,
       
   822         ArbitraryRelationshipTypes,
       
   823         RelationshipOrdering,
       
   824         DetailOrdering,
       
   825         SelfContact,
       
   826         Anonymous,
       
   827         ChangeLogs
       
   828         */
       
   829     case QContactManager::Groups:
       
   830     case QContactManager::Relationships:
       
   831     case QContactManager::SelfContact: {
       
   832         returnValue = true;
       
   833         break;
       
   834     }
       
   835 
       
   836     default:
       
   837         returnValue = false;
       
   838     }
       
   839 
       
   840     return returnValue;
       
   841 }
       
   842 
       
   843 bool CntSymbianEngine::isFilterSupported(const QContactFilter& filter) const
       
   844 {
       
   845     return m_contactFilter->filterSupported(filter);
       
   846 }
       
   847 
       
   848 /* Synthesise the display label of a contact */
       
   849 QString CntSymbianEngine::synthesizedDisplayLabel(const QContact& contact, QContactManager::Error* error) const
       
   850 {
       
   851     *error = QContactManager::NoError;
       
   852     return m_displayLabel->synthesizedDisplayLabel(contact, error);
       
   853 }
       
   854 
       
   855 bool CntSymbianEngine::setSelfContactId(const QContactLocalId& contactId, QContactManager::Error* error)
       
   856 {
       
   857     if (contactId <= 0) {
       
   858         *error = QContactManager::BadArgumentError;
       
   859         return false;
       
   860     }
       
   861 
       
   862 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK
       
   863     QContactManager::Error e;
       
   864     QContactLocalId selfCntId = selfContactId( &e ); // err ignored
       
   865    
       
   866     QContactChangeSet changeSet;
       
   867     QOwnCardPair ownCard(selfCntId, contactId);
       
   868 #endif
       
   869     
       
   870     TContactItemId id(contactId);
       
   871     CContactItem* symContact = 0;
       
   872     TRAPD(err,
       
   873         symContact = m_dataBase->contactDatabase()->ReadContactL(id);
       
   874         m_dataBase->contactDatabase()->SetOwnCardL(*symContact);
       
   875         );
       
   876     delete symContact;
       
   877 
       
   878 #ifdef SYMBIAN_BACKEND_SIGNAL_EMISSION_TWEAK    
       
   879     if(err == KErrNone)
       
   880        {
       
   881        m_dataBase->appendContactEmitted(id);
       
   882        
       
   883        changeSet.setOldAndNewSelfContactId(ownCard);
       
   884        changeSet.emitSignals( this );
       
   885        }
       
   886 #endif
       
   887     
       
   888     CntSymbianTransformError::transformError(err, error);
       
   889     return (err==KErrNone);
       
   890 }
       
   891 
       
   892 QContactLocalId CntSymbianEngine::selfContactId(QContactManager::Error* error) const
       
   893 {
       
   894     *error = QContactManager::NoError;
       
   895     QContactLocalId id = 0;
       
   896 
       
   897     TContactItemId myCard = m_dataBase->contactDatabase()->OwnCardId();
       
   898     if (myCard < 0) {
       
   899     *error = QContactManager::DoesNotExistError;
       
   900     }
       
   901     else {
       
   902         id = myCard;
       
   903     }
       
   904     return id;
       
   905 }
       
   906 
       
   907 /*!
       
   908  * Returns the list of data types supported by the Symbian S60 engine
       
   909  */
       
   910 QList<QVariant::Type> CntSymbianEngine::supportedDataTypes() const
       
   911 {
       
   912     QList<QVariant::Type> st;
       
   913     st.append(QVariant::String);
       
   914 
       
   915     return st;
       
   916 }
       
   917 
       
   918 QString CntSymbianEngine::managerName() const
       
   919 {
       
   920     return CNT_SYMBIAN_MANAGER_NAME;
       
   921 }
       
   922 
       
   923 /*
       
   924  * 'async' code borrowed from memory engine - actually does sync operations.
       
   925  * This will be replaced by the thread request worker when it is stable.
       
   926  */
       
   927 
       
   928 
       
   929 
       
   930 /*! \reimp */
       
   931 void CntSymbianEngine::requestDestroyed(QContactAbstractRequest* req)
       
   932 {
       
   933     m_asynchronousOperations.removeOne(req);
       
   934 }
       
   935 
       
   936 /*! \reimp */
       
   937 bool CntSymbianEngine::startRequest(QContactAbstractRequest* req)
       
   938 {
       
   939     if (!m_asynchronousOperations.contains(req))
       
   940         m_asynchronousOperations.enqueue(req);
       
   941     updateRequestState(req, QContactAbstractRequest::ActiveState);
       
   942     QTimer::singleShot(0, this, SLOT(performAsynchronousOperation()));
       
   943     return true;
       
   944 }
       
   945 
       
   946 /*! \reimp */
       
   947 bool CntSymbianEngine::cancelRequest(QContactAbstractRequest* req)
       
   948 {
       
   949     updateRequestState(req, QContactAbstractRequest::CanceledState);
       
   950     return true;
       
   951 }
       
   952 
       
   953 /*! \reimp */
       
   954 bool CntSymbianEngine::waitForRequestProgress(QContactAbstractRequest* req, int msecs)
       
   955 {
       
   956     Q_UNUSED(msecs);
       
   957 
       
   958     if (!m_asynchronousOperations.removeOne(req))
       
   959         return false; // didn't exist.
       
   960 
       
   961     // replace at head of queue
       
   962     m_asynchronousOperations.insert(0, req);
       
   963 
       
   964     // and perform the operation.
       
   965     performAsynchronousOperation();
       
   966 
       
   967     return true;
       
   968 }
       
   969 
       
   970 /*! \reimp */
       
   971 bool CntSymbianEngine::waitForRequestFinished(QContactAbstractRequest* req, int msecs)
       
   972 {
       
   973     // in our implementation, we always complete any operation we start.
       
   974     // so, waitForRequestFinished is equivalent to waitForRequestProgress.
       
   975     return waitForRequestProgress(req, msecs);
       
   976 }
       
   977 
       
   978 /*!
       
   979  * This slot is called some time after an asynchronous request is started.
       
   980  * It performs the required operation, sets the result and returns.
       
   981  */
       
   982 void CntSymbianEngine::performAsynchronousOperation()
       
   983 {
       
   984     QContactAbstractRequest *currentRequest;
       
   985 
       
   986     // take the first pending request and finish it
       
   987     if (m_asynchronousOperations.isEmpty())
       
   988         return;
       
   989     currentRequest = m_asynchronousOperations.dequeue();
       
   990 
       
   991     // check to see if it is cancelling; if so, remove it from the queue and return.
       
   992     if (currentRequest->state() == QContactAbstractRequest::CanceledState) {
       
   993         return;
       
   994     }
       
   995 
       
   996     // store up changes, and emit signals once at the end of the (possibly batch) operation.
       
   997     QContactChangeSet changeSet;
       
   998 
       
   999     // Now perform the active request and emit required signals.
       
  1000     Q_ASSERT(currentRequest->state() == QContactAbstractRequest::ActiveState);
       
  1001     switch (currentRequest->type()) {
       
  1002         case QContactAbstractRequest::ContactFetchRequest:
       
  1003         {
       
  1004             QContactFetchRequest* r = static_cast<QContactFetchRequest*>(currentRequest);
       
  1005             QContactFilter filter = r->filter();
       
  1006             QList<QContactSortOrder> sorting = r->sorting();
       
  1007             QContactFetchHint fh = r->fetchHint();
       
  1008 
       
  1009             QContactManager::Error operationError;
       
  1010             QList<QContact> requestedContacts = contacts(filter, sorting, fh, &operationError);
       
  1011 
       
  1012             // update the request with the results.
       
  1013             updateContactFetchRequest(r, requestedContacts, operationError, QContactAbstractRequest::FinishedState); // emit resultsAvailable()
       
  1014         }
       
  1015         break;
       
  1016 
       
  1017         case QContactAbstractRequest::ContactLocalIdFetchRequest:
       
  1018         {
       
  1019             QContactLocalIdFetchRequest* r = static_cast<QContactLocalIdFetchRequest*>(currentRequest);
       
  1020             QContactFilter filter = r->filter();
       
  1021             QList<QContactSortOrder> sorting = r->sorting();
       
  1022 
       
  1023             QContactManager::Error operationError = QContactManager::NoError;
       
  1024             QList<QContactLocalId> requestedContactIds = contactIds(filter, sorting, &operationError);
       
  1025 
       
  1026             updateContactLocalIdFetchRequest(r, requestedContactIds, operationError, QContactAbstractRequest::FinishedState);
       
  1027         }
       
  1028         break;
       
  1029 
       
  1030         case QContactAbstractRequest::ContactSaveRequest:
       
  1031         {
       
  1032             QContactSaveRequest* r = static_cast<QContactSaveRequest*>(currentRequest);
       
  1033             QList<QContact> contacts = r->contacts();
       
  1034 
       
  1035             QContactManager::Error operationError = QContactManager::NoError;
       
  1036             QMap<int, QContactManager::Error> errorMap;
       
  1037             saveContacts(&contacts, &errorMap, &operationError);
       
  1038 
       
  1039             updateContactSaveRequest(r, contacts, operationError, errorMap, QContactAbstractRequest::FinishedState); // there will always be results of some form.  emit resultsAvailable().
       
  1040         }
       
  1041         break;
       
  1042 
       
  1043         case QContactAbstractRequest::ContactRemoveRequest:
       
  1044         {
       
  1045             // this implementation provides scant information to the user
       
  1046             // the operation either succeeds (all contacts matching the filter were removed)
       
  1047             // or it fails (one or more contacts matching the filter could not be removed)
       
  1048             // if a failure occurred, the request error will be set to the most recent
       
  1049             // error that occurred during the remove operation.
       
  1050             QContactRemoveRequest* r = static_cast<QContactRemoveRequest*>(currentRequest);
       
  1051             QContactManager::Error operationError = QContactManager::NoError;
       
  1052             QList<QContactLocalId> contactsToRemove = r->contactIds();
       
  1053             QMap<int, QContactManager::Error> errorMap;
       
  1054 
       
  1055             for (int i = 0; i < contactsToRemove.size(); i++) {
       
  1056                 QContactManager::Error tempError;
       
  1057                 removeContact(contactsToRemove.at(i), changeSet, &tempError);
       
  1058 
       
  1059                 errorMap.insert(i, tempError);                
       
  1060                 if (tempError != QContactManager::NoError) {
       
  1061                     operationError = tempError;
       
  1062                 }
       
  1063             }
       
  1064 
       
  1065             updateContactRemoveRequest(r, operationError, errorMap, QContactAbstractRequest::FinishedState);
       
  1066         }
       
  1067         break;
       
  1068 
       
  1069         case QContactAbstractRequest::DetailDefinitionFetchRequest:
       
  1070         {
       
  1071             QContactDetailDefinitionFetchRequest* r = static_cast<QContactDetailDefinitionFetchRequest*>(currentRequest);
       
  1072             QContactManager::Error operationError = QContactManager::NoError;
       
  1073             QMap<int, QContactManager::Error> errorMap;
       
  1074             QMap<QString, QContactDetailDefinition> requestedDefinitions;
       
  1075             QStringList names = r->definitionNames();
       
  1076             if (names.isEmpty())
       
  1077                 names = detailDefinitions(r->contactType(), &operationError).keys(); // all definitions.
       
  1078 
       
  1079             QContactManager::Error tempError;
       
  1080             for (int i = 0; i < names.size(); i++) {
       
  1081                 QContactDetailDefinition current = detailDefinition(names.at(i), r->contactType(), &tempError);
       
  1082                 requestedDefinitions.insert(names.at(i), current);
       
  1083 
       
  1084                 errorMap.insert(i, tempError);              
       
  1085                 if (tempError != QContactManager::NoError) {
       
  1086                     operationError = tempError;
       
  1087                 }
       
  1088             }
       
  1089 
       
  1090             updateDefinitionFetchRequest(r, requestedDefinitions, operationError, errorMap, QContactAbstractRequest::FinishedState);
       
  1091         }
       
  1092         break;
       
  1093 
       
  1094         case QContactAbstractRequest::DetailDefinitionSaveRequest:
       
  1095         {
       
  1096             // symbian engine currently does not support mutable definitions.
       
  1097         }
       
  1098         break;
       
  1099 
       
  1100         case QContactAbstractRequest::DetailDefinitionRemoveRequest:
       
  1101         {
       
  1102             // symbian engine currently does not support mutable definitions.
       
  1103         }
       
  1104         break;
       
  1105 
       
  1106         case QContactAbstractRequest::RelationshipFetchRequest:
       
  1107         {
       
  1108             QContactRelationshipFetchRequest* r = static_cast<QContactRelationshipFetchRequest*>(currentRequest);
       
  1109             QContactManager::Error operationError = QContactManager::NoError;
       
  1110             QList<QContactManager::Error> operationErrors;
       
  1111             QList<QContactRelationship> allRelationships = relationships(QString(), QContactId(), QContactRelationship::Either, &operationError);
       
  1112             QList<QContactRelationship> requestedRelationships;
       
  1113 
       
  1114             // select the requested relationships.
       
  1115             for (int i = 0; i < allRelationships.size(); i++) {
       
  1116                 QContactRelationship currRel = allRelationships.at(i);
       
  1117                 if (r->first() != QContactId() && r->first() != currRel.first())
       
  1118                     continue;
       
  1119                 if (r->second() != QContactId() && r->second() != currRel.second())
       
  1120                     continue;
       
  1121                 if (!r->relationshipType().isEmpty() && r->relationshipType() != currRel.relationshipType())
       
  1122                     continue;
       
  1123                 requestedRelationships.append(currRel);
       
  1124             }
       
  1125 
       
  1126             // update the request with the results.
       
  1127             updateRelationshipFetchRequest(r, requestedRelationships, operationError, QContactAbstractRequest::FinishedState);
       
  1128         }
       
  1129         break;
       
  1130 
       
  1131         case QContactAbstractRequest::RelationshipRemoveRequest:
       
  1132         {
       
  1133             QContactRelationshipRemoveRequest* r = static_cast<QContactRelationshipRemoveRequest*>(currentRequest);
       
  1134             QContactManager::Error operationError = QContactManager::NoError;
       
  1135             QList<QContactRelationship> relationshipsToRemove = r->relationships();
       
  1136             QMap<int, QContactManager::Error> errorMap;
       
  1137 
       
  1138             for (int i = 0; i < relationshipsToRemove.size(); i++) {
       
  1139                 QContactManager::Error tempError;
       
  1140                 removeRelationship(relationshipsToRemove.at(i), &tempError);
       
  1141 
       
  1142                 errorMap.insert(i, tempError);
       
  1143                 if (tempError != QContactManager::NoError) {
       
  1144                     operationError = tempError;
       
  1145                 }
       
  1146             }
       
  1147 
       
  1148             updateRelationshipRemoveRequest(r, operationError, errorMap, QContactAbstractRequest::FinishedState);
       
  1149         }
       
  1150         break;
       
  1151 
       
  1152         case QContactAbstractRequest::RelationshipSaveRequest:
       
  1153         {
       
  1154             QContactRelationshipSaveRequest* r = static_cast<QContactRelationshipSaveRequest*>(currentRequest);
       
  1155             QContactManager::Error operationError = QContactManager::NoError;
       
  1156             QMap<int, QContactManager::Error> errorMap;
       
  1157             QList<QContactRelationship> requestRelationships = r->relationships();
       
  1158             QList<QContactRelationship> savedRelationships;
       
  1159 
       
  1160             QContactManager::Error tempError;
       
  1161             for (int i = 0; i < requestRelationships.size(); i++) {
       
  1162                 QContactRelationship current = requestRelationships.at(i);
       
  1163                 saveRelationship(&current, &tempError);
       
  1164                 savedRelationships.append(current);
       
  1165 
       
  1166                 errorMap.insert(i, tempError);
       
  1167                 if (tempError != QContactManager::NoError) {
       
  1168                     operationError = tempError;
       
  1169                 }
       
  1170             }
       
  1171 
       
  1172             // update the request with the results.
       
  1173             updateRelationshipSaveRequest(r, savedRelationships, operationError, errorMap, QContactAbstractRequest::FinishedState);
       
  1174         }
       
  1175         break;
       
  1176 
       
  1177         default: // unknown request type.
       
  1178         break;
       
  1179     }
       
  1180 
       
  1181     // now emit any signals we have to emit
       
  1182     changeSet.emitSignals(this);
       
  1183 }
       
  1184 
       
  1185 #ifndef PBK_UNIT_TEST
       
  1186 /* Factory lives here in the basement */
       
  1187 QContactManagerEngine* CntSymbianFactory::engine(const QMap<QString, QString>& parameters, QContactManager::Error* error)
       
  1188 {
       
  1189     return new CntSymbianEngine(parameters, error);
       
  1190 }
       
  1191 
       
  1192 QString CntSymbianFactory::managerName() const
       
  1193 {
       
  1194     return CNT_SYMBIAN_MANAGER_NAME;
       
  1195 }
       
  1196 
       
  1197 Q_EXPORT_PLUGIN2(qtcontacts_symbian, CntSymbianFactory);
       
  1198 
       
  1199 #endif  //PBK_UNIT_TEST