--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/phonebookengines/VirtualPhonebook/VPbkCntModel/src/CContact.cpp Wed Sep 01 12:29:52 2010 +0100
@@ -0,0 +1,622 @@
+/*
+* Copyright (c) 2002-2007 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: The virtual phonebook contact
+*
+*/
+
+
+
+#include "CContact.h"
+#include <cntitem.h>
+#include <babitflags.h>
+
+#include <VPbkError.h>
+#include <MVPbkContactObserver.h>
+#include <TVPbkFieldTypeMapping.h>
+#include <MVPbkFieldType.h>
+#include <MVPbkContactStoreProperties.h>
+#include <CVPbkContactLinkArray.h>
+#include <CVPbkContactFieldCollection.h>
+#include <MVPbkStoreContactProperties.h>
+
+#include "CContactStore.h"
+#include "CFieldTypeMap.h"
+#include "TNewContactField.h"
+#include "CViewContact.h"
+#include "CContactLink.h"
+
+namespace VPbkCntModel {
+
+// ======== LOCAL CLASSES ========
+
+/**
+ * Internal store contact properties class,
+ * use CContactItem to implement MVPbkStoreContactProperties methods
+ *
+ */
+class CVPbkStoreContactProperties :
+ public CBase,
+ public MVPbkStoreContactProperties
+ {
+ public:
+ static CVPbkStoreContactProperties* NewL(
+ CContactItem& aContactItem,
+ CContactDatabase& aContactDb );
+
+ ~CVPbkStoreContactProperties();
+ protected: // MVPbkStoreContactProperties
+ TTime LastModifiedL() const;
+ TPtrC GuidL() const;
+ private:
+ CVPbkStoreContactProperties(
+ CContactItem& aContactItem,
+ CContactDatabase& aContactDb );
+
+ private:
+ CContactItem& iContactItem;
+ CContactDatabase& iContactDb;
+ };
+
+CVPbkStoreContactProperties* CVPbkStoreContactProperties::NewL(
+ CContactItem& aContactItem,
+ CContactDatabase& aContactDb )
+ {
+ CVPbkStoreContactProperties* self = new(ELeave) CVPbkStoreContactProperties(
+ aContactItem,
+ aContactDb );
+ return self;
+ }
+
+CVPbkStoreContactProperties::CVPbkStoreContactProperties(
+ CContactItem& aContactItem,
+ CContactDatabase& aContactDb ) :
+ iContactItem( aContactItem ),
+ iContactDb( aContactDb )
+ {
+ }
+
+CVPbkStoreContactProperties::~CVPbkStoreContactProperties()
+ {
+ }
+
+TTime CVPbkStoreContactProperties::LastModifiedL() const
+ {
+ return iContactItem.LastModified();
+ }
+
+TPtrC CVPbkStoreContactProperties::GuidL() const
+ {
+ return iContactItem.UidStringL(iContactDb.MachineId());
+ }
+// ======== LOCAL FUNCTIONS ========
+
+TInt FindMatchingField( const MVPbkBaseContactFieldCollection& aFields,
+ const MVPbkFieldType* aFieldType,
+ const MVPbkFieldTypeList& aMasterFieldTypeList )
+ {
+ TInt result = KErrNotFound;
+
+ const TInt fieldCount = aFields.FieldCount();
+ const TInt maxMatchPriority = aMasterFieldTypeList.MaxMatchPriority();
+ for ( TInt matchPriority = 0; matchPriority <= maxMatchPriority;
+ ++matchPriority )
+ {
+ for ( TInt i = 0; i < fieldCount; ++i )
+ {
+ const MVPbkFieldType* fieldType =
+ aFields.FieldAt( i ).MatchFieldType( matchPriority );
+ if ( fieldType && fieldType->IsSame(*aFieldType) )
+ {
+ result = i;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+// ======== MEMBER FUNCTIONS ========
+
+inline CContact::CContact(
+ CContactStore& aParentStore,
+ CContactItem& aContactItem,
+ TBool aIsNewContact ) :
+ iIsNewContact( aIsNewContact ),
+ iFields( *this, aContactItem.CardFields() ),
+ iParentStore( aParentStore ),
+ iLastUpdatedGroupContactId( KNullContactId )
+ {
+ }
+
+inline void CContact::ConstructL()
+ {
+ }
+
+CContact* CContact::NewL(
+ CContactStore& aParentStore,
+ CContactItem* aContactItem,
+ TBool aIsNewContact /* = EFalse */)
+ {
+ CContact* self = new(ELeave) CContact( aParentStore, *aContactItem,
+ aIsNewContact );
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ CleanupStack::Pop( self );
+ // Potentially leaving initialisation is done; take parameter ownership now
+ self->iContactItem = aContactItem;
+ return self;
+ }
+
+CContact::~CContact()
+ {
+ // Incase there was a problem when updating the time stamps of contacts
+ // belonging to a group we need to make sure the contact is unlocked by
+ // closing it. Symbian documentation says that despite the trailing L
+ // in the function's name, CloseContactL cannot leave. Also specifying
+ // a contact item that is not open, or cannot be found, is harmless.
+ if ( iLastUpdatedGroupContactId != KNullContactId )
+ {
+ iParentStore.NativeDatabase().CloseContactL( iLastUpdatedGroupContactId );
+ }
+
+ iParentStore.ContactDestroyed( iContactItem, iModified );
+ delete iContactItem;
+ delete iAddedContacts;
+ }
+
+void CContact::SetContact( CContactItem* aContactItem )
+ {
+ if ( aContactItem && aContactItem != iContactItem )
+ {
+ delete iContactItem;
+ iContactItem = aContactItem;
+ iFields.SetContact( *this, iContactItem->CardFields() );
+ }
+ }
+
+const CFieldTypeMap& CContact::FieldTypeMap() const
+ {
+ return iParentStore.FieldTypeMap();
+ }
+
+MVPbkObjectHierarchy& CContact::ParentObject() const
+ {
+ return iParentStore;
+ }
+
+const MVPbkStoreContactFieldCollection& CContact::Fields() const
+ {
+ return iFields;
+ }
+
+TBool CContact::IsSame( const MVPbkStoreContact& aOtherContact ) const
+ {
+ if ( &iParentStore == &aOtherContact.ContactStore() )
+ {
+ return ( iContactItem->Id() ==
+ static_cast<const CContact&>(aOtherContact).iContactItem->Id() );
+ }
+ return EFalse;
+ }
+
+TBool CContact::IsSame( const MVPbkViewContact& aOtherContact ) const
+ {
+ return aOtherContact.IsSame( *this, &ContactStore() );
+ }
+
+MVPbkContactLink* CContact::CreateLinkLC() const
+ {
+ return iParentStore.CreateLinkLC( iContactItem->Id() );
+ }
+
+void CContact::LockL(MVPbkContactObserver& aObserver) const
+ {
+ iParentStore.LockContactL( *this, aObserver );
+ }
+
+void CContact::DeleteL(MVPbkContactObserver& aObserver) const
+ {
+ iParentStore.NativeDatabase().CloseContactL( iContactItem->Id() );
+ iParentStore.DeleteContactL( iContactItem->Id(), aObserver );
+ }
+
+TBool CContact::MatchContactStore(const TDesC& aContactStoreUri) const
+ {
+ return iParentStore.MatchContactStore( aContactStoreUri );
+ }
+
+TBool CContact::MatchContactStoreDomain( const TDesC& aContactStoreDomain ) const
+ {
+ return iParentStore.MatchContactStoreDomain( aContactStoreDomain );
+ }
+
+MVPbkContactBookmark* CContact::CreateBookmarkLC() const
+ {
+ return iParentStore.CreateBookmarkLC( iContactItem->Id() );
+ }
+
+MVPbkContactStore& CContact::ParentStore() const
+ {
+ return iParentStore;
+ }
+
+MVPbkStoreContactFieldCollection& CContact::Fields()
+ {
+ return iFields;
+ }
+
+MVPbkStoreContactField* CContact::CreateFieldLC( const MVPbkFieldType&
+ aFieldType ) const
+ {
+ // Match the field type to the Contact Db's system template
+ CContactItemField* newField = iParentStore.CreateFieldLC( aFieldType );
+ if ( !newField )
+ {
+ User::Leave( KErrNotSupported );
+ }
+
+ // Create a wrapper for the newly created field
+ TNewContactField* fieldWrapper =
+ new(ELeave) TNewContactField( const_cast<CContact&>(*this), newField );
+ CleanupStack::Pop( newField );
+ CleanupDeletePushL( fieldWrapper );
+
+ // Return the wrapper
+ return fieldWrapper;
+ }
+
+TInt CContact::AddFieldL( MVPbkStoreContactField* aField )
+ {
+ __ASSERT_ALWAYS( aField, VPbkError::Panic( VPbkError::ENullContactField ) );
+ __ASSERT_ALWAYS( &aField->ParentContact() == this,
+ VPbkError::Panic(VPbkError::EInvalidContactField) );
+ // Test that the client doesn't pass an existing field of this contact as
+ // a new one
+ __ASSERT_ALWAYS( aField != iFields.FieldPointer(),
+ VPbkError::Panic(VPbkError::EInvalidContactField) );
+
+ // After all the checks the field can be cast back to the wrapper that was
+ // created in CreateFieldLC
+ TNewContactField* fieldWrapper = static_cast<TNewContactField*>( aField );
+
+ // Add the Contact Model field to the contact item
+ iContactItem->AddFieldL( *fieldWrapper->NativeField() );
+ // Field added succesfully, release wrapper's ownership
+ fieldWrapper->ReleaseNativeField();
+
+ // Delete fieldWrapper. This function must not leave after deletion
+ // of fieldWrapper because CreateFieldLC has put fieldWrapper
+ // into the cleanup stack and client pops it after this function
+ delete fieldWrapper;
+ // The field is appended to the contact -> return the last field index
+ return iContactItem->CardFields().Count() - 1;
+ }
+
+void CContact::RemoveField(TInt aIndex)
+ {
+ __ASSERT_ALWAYS( aIndex >= 0 && aIndex < iFields.FieldCount(),
+ VPbkError::Panic(VPbkError::EInvalidFieldIndex) );
+ __ASSERT_ALWAYS( !iParentStore.StoreProperties().ReadOnly(),
+ VPbkError::Panic(VPbkError::EInvalidAccessToReadOnlyContact ) );
+
+ iContactItem->RemoveField( aIndex );
+ }
+
+void CContact::RemoveAllFields()
+ {
+ __ASSERT_ALWAYS( !iParentStore.StoreProperties().ReadOnly(),
+ VPbkError::Panic(VPbkError::EInvalidAccessToReadOnlyContact ) );
+
+ iContactItem->CardFields().Reset();
+ }
+
+void CContact::CommitL( MVPbkContactObserver& aObserver ) const
+ {
+ iParentStore.CommitContactL( *this, aObserver );
+ }
+
+MVPbkContactLinkArray* CContact::GroupsJoinedLC() const
+ {
+ CVPbkContactLinkArray* result = CVPbkContactLinkArray::NewLC();
+
+ if ( iContactItem->Type() == KUidContactCard )
+ {
+ CContactCard* contactCard = static_cast<CContactCard*>( iContactItem );
+ CContactIdArray* groups = contactCard->GroupsJoinedLC();
+ const TInt count = groups->Count();
+ for ( TInt i = 0; i < count; ++i )
+ {
+ MVPbkContactLink* link = iParentStore.CreateLinkLC( (*groups)[i] );
+ result->AppendL( link );
+ CleanupStack::Pop(); // link
+ }
+ CleanupStack::PopAndDestroy( groups );
+ }
+
+ return result;
+ }
+
+TInt CContact::MaxNumberOfFieldL( const MVPbkFieldType& aType ) const
+ {
+ if ( iParentStore.StoreProperties().SupportedFields().ContainsSame(aType) )
+ {
+ return KVPbkStoreContactUnlimitedNumber;
+ }
+ return 0;
+ }
+
+MVPbkContactGroup* CContact::Group()
+ {
+ MVPbkContactGroup* result = NULL;
+
+ if ( iContactItem->Type() == KUidContactGroup )
+ {
+ result = this;
+ }
+
+ return result;
+ }
+
+void CContact::SetGroupLabelL( const TDesC& aLabel )
+ {
+ TVPbkFieldTypeMapping typeMapping;
+ typeMapping.SetNonVersitType( EVPbkNonVersitTypeGenericLabel );
+ const MVPbkFieldType* labelType =
+ typeMapping.FindMatch( iParentStore.MasterFieldTypeList() );
+
+ TInt labelFieldIndex = FindMatchingField( Fields(),
+ labelType,
+ iParentStore.MasterFieldTypeList() );
+
+ // Update the timestamp of the all contacts that belong to the group.
+ // This is needed for synch service as it checks the timestamps of contacts
+ // and get's the group information from the vCard of a contact.
+ UpdateTimeStampOfAllContactsInGroupL();
+
+ if ( labelFieldIndex != KErrNotFound )
+ {
+ // field already exists
+ MVPbkStoreContactField& labelField = Fields().FieldAt( labelFieldIndex );
+ MVPbkContactFieldTextData::Cast( labelField.FieldData()).SetTextL(aLabel );
+ }
+ else
+ {
+ // field does not exist => add it
+ MVPbkStoreContactField* labelField = CreateFieldLC( *labelType );
+ MVPbkContactFieldTextData::Cast( labelField->FieldData() ).SetTextL( aLabel );
+ AddFieldL( labelField );
+ CleanupStack::Pop(); // labelField
+ }
+ }
+
+TPtrC CContact::GroupLabel() const
+ {
+ TVPbkFieldTypeMapping typeMapping;
+ typeMapping.SetNonVersitType( EVPbkNonVersitTypeGenericLabel );
+ const MVPbkFieldType* labelType =
+ typeMapping.FindMatch( iParentStore.MasterFieldTypeList() );
+
+ TInt labelFieldIndex = FindMatchingField( Fields(),
+ labelType,
+ iParentStore.MasterFieldTypeList() );
+
+ if ( labelFieldIndex != KErrNotFound )
+ {
+ const MVPbkBaseContactField& labelField = Fields().FieldAt( labelFieldIndex );
+ return MVPbkContactFieldTextData::Cast(labelField.FieldData()).Text();
+ }
+ else
+ {
+ return KNullDesC();
+ }
+ }
+
+// --------------------------------------------------------------------------
+// CContact::UpdateTimeStampOfAllContactsInGroupL
+// --------------------------------------------------------------------------
+//
+void CContact::UpdateTimeStampOfAllContactsInGroupL( )
+ {
+ MVPbkContactLinkArray* contactsInGroup = ItemsContainedLC();
+
+ // Loop through all contacts in the group and update time stamps
+ for (TInt i=0; i < contactsInGroup->Count(); i++ )
+ {
+ UpdateTimeStampOfContactInGroupL( contactsInGroup->At(i) );
+ }
+
+ CleanupStack::PopAndDestroy(); // contactsInGroup
+ }
+
+// --------------------------------------------------------------------------
+// CContact::UpdateTimeStampOfContactInGroupL
+// --------------------------------------------------------------------------
+//
+void CContact::UpdateTimeStampOfContactInGroupL(const MVPbkContactLink& aContactLink )
+ {
+ const CContactLink& link = static_cast<const CContactLink&>( aContactLink );
+
+ // Store the id of currently processed contact in the group
+ iLastUpdatedGroupContactId = link.ContactId();
+
+ // Try to open the contact for editing and then commit.
+ // This will update the timestamp of the contact.
+ // In case of a leave, we will make sure in the destructor that the
+ // contact is unlocked (see CContact::~CContact).
+ CContactItem* contact =
+ iParentStore.NativeDatabase().OpenContactL( link.ContactId() );
+ CleanupStack::PushL(contact);
+
+ iParentStore.NativeDatabase().CommitContactL(*contact);
+ CleanupStack::PopAndDestroy(contact);
+
+ // No need to store the id anymore.
+ iLastUpdatedGroupContactId = KNullContactId;
+ }
+
+void CContact::AddContactL( const MVPbkContactLink& aContactLink )
+ {
+ // We have to maintain iAddedContacts ID array here because
+ // the AddContactToGroup(id, id) method does not update
+ // the native contact group we have in hand. It updates the
+ // database only. So, in the ItemsContainedLC function
+ // we have to know both the group members in the database
+ // and the group members that have been added after this group
+ // has been read from database
+ const CContactLink& link = static_cast<const CContactLink&>( aContactLink );
+
+ // Read the contact so that
+ // AddContactToGroupL(CContactItem &aItem, CContactItem &aGroup)
+ // can be used.
+ CContactItem* contact =
+ iParentStore.NativeDatabase().ReadContactLC( link.ContactId() );
+
+ if ( !iAddedContacts )
+ {
+ iAddedContacts = CContactIdArray::NewL();
+ }
+ if ( iAddedContacts->Find( link.ContactId() ) == KErrNotFound )
+ {
+ iAddedContacts->AddL( link.ContactId() );
+ }
+
+ // Use AddContactToGroupL(CContactItem &aItem, CContactItem &aGroup)
+ // instead of
+ // AddContactToGroupL(TContactItemId aItemId, TContactItemId aGroupId)
+ // because otherwise the member iContactItem won't be updated and commiting
+ // it would loose the information about added contact
+ TRAPD( err1, iParentStore.NativeDatabase().AddContactToGroupL(
+ *contact, *iContactItem ) );
+ if ( err1 != KErrNone )
+ {
+ iAddedContacts->Remove( iAddedContacts->Count() - 1 );
+ User::Leave( err1 );
+ }
+
+ // Update the timestamp of the added contact. This is needed for
+ // synch service as it checks the timestamps of contacts and get's the group
+ // information from the vCard of a contact.
+ TRAPD( err2, UpdateTimeStampOfContactInGroupL( aContactLink ) );
+ if ( err2 != KErrNone )
+ {
+ iAddedContacts->Remove( iAddedContacts->Count() - 1 );
+ iParentStore.NativeDatabase().RemoveContactFromGroupL(
+ *contact, *iContactItem );
+ User::Leave( err2 );
+ }
+ CleanupStack::PopAndDestroy( contact );
+ }
+
+void CContact::RemoveContactL( const MVPbkContactLink& aContactLink )
+ {
+ const CContactLink& link = static_cast<const CContactLink&>( aContactLink );
+
+ // Read the contact so that
+ // RemoveContactFromGroupL(CContactItem &aItem, CContactItem &aGroup)
+ // can be used.
+ CContactItem* contact =
+ iParentStore.NativeDatabase().ReadContactLC( link.ContactId() );
+
+ // First update the timestamp of the removed contact. This is needed for
+ // synch service as it checks the timestamps of contacts and get's the group
+ // information from the vCard of a contact. If updating fails this function
+ // will leave and contact won't be removed from the group.
+ UpdateTimeStampOfContactInGroupL(aContactLink);
+
+ TInt index = KErrNotFound;
+ if ( iAddedContacts )
+ {
+ index = iAddedContacts->Find( link.ContactId() );
+ }
+
+ // Use RemoveContactFromGroupL(CContactItem &aItem, CContactItem &aGroup)
+ // instead of
+ // RemoveContactFromGroupL(TContactItemId aItemId, TContactItemId aGroupId)
+ // because otherwise the member iContactItem won't be updated and commiting
+ // it would loose the information about removed contact
+ iParentStore.NativeDatabase().RemoveContactFromGroupL(
+ *contact, *iContactItem );
+ if ( index != KErrNotFound )
+ {
+ iAddedContacts->Remove( index );
+ }
+ CleanupStack::PopAndDestroy( contact );
+ }
+
+MVPbkContactLinkArray* CContact::ItemsContainedLC() const
+ {
+ CVPbkContactLinkArray* result = CVPbkContactLinkArray::NewLC();
+ TInt i;
+
+ const CContactGroup* thisGroup = static_cast<const CContactGroup*>( iContactItem );
+ // 1. append the IDs found in the group
+ const CContactIdArray* contacts = thisGroup->ItemsContained();
+ const TInt count = ( contacts ? contacts->Count() : 0 );
+ for ( i = 0; i < count; ++i )
+ {
+ MVPbkContactLink* link = iParentStore.CreateLinkLC( (*contacts)[i] );
+ result->AppendL( link );
+ CleanupStack::Pop(); // link
+ }
+ // 2. append the IDs in iAddedContacts
+ const TInt addedCount = ( iAddedContacts ? iAddedContacts->Count() : 0 );
+ for ( i = 0; i < addedCount; ++i )
+ {
+ // add the contact if the iAddedContact[i] was not in contacts already
+ if ( !contacts ||
+ contacts->Find( (*iAddedContacts)[i]) == KErrNotFound )
+ {
+ MVPbkContactLink* link = iParentStore.CreateLinkLC( (*iAddedContacts)[i] );
+ result->AppendL( link );
+ CleanupStack::Pop(); // link
+ }
+ }
+ return result;
+ }
+
+TAny* CContact::StoreContactExtension(TUid aExtensionUid)
+{
+ if( aExtensionUid == KMVPbkStoreContactExtension2Uid )
+ return static_cast<MVPbkStoreContact2*>( this );
+ return NULL;
+}
+
+MVPbkStoreContactProperties* CContact::PropertiesL() const
+ {
+ return CVPbkStoreContactProperties::NewL( *iContactItem, iParentStore.NativeDatabase() );
+ }
+
+void CContact::SetAsOwnL(MVPbkContactObserver& aObserver) const
+ {
+ iParentStore.SetAsOwnL( *this, aObserver );
+ }
+
+TAny* CContact::BaseContactExtension( TUid aExtensionUid )
+ {
+ if( aExtensionUid == KVPbkBaseContactExtension2Uid )
+ return static_cast<MVPbkBaseContact2*>( this );
+ return NULL;
+ }
+
+TBool CContact::IsOwnContact( TInt& aError ) const
+ {
+ aError = KErrNone;
+ return ( iContactItem->Type() == KUidContactOwnCard );
+ }
+
+
+} // namespace VPbkCntModel
+
+// end of file