--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/phonebookengines/contactsmodel/cntplsql/src/cviewcontactmanager.cpp Tue Feb 02 10:12:17 2010 +0200
@@ -0,0 +1,1199 @@
+// Copyright (c) 2007-2009 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:
+//
+
+/**
+ @file
+ @internalComponent
+ @released
+*/
+
+#include <cntviewsortplugin.h>
+#include "cntviewprivate.h"
+#include "cviewcontactmanager.h"
+
+#include <cntviewbase.h>
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include <cntviewsortpluginbase.h>
+#endif
+
+const TInt KContactsArrayGranularity = 100;
+const TInt KUnsortedArrayGranularity = 16;
+
+/**
+@SYMPatchable
+@publishedPartner
+@released
+
+Patchable constant for the number of CViewContacts to read into memory
+before merging them into a larger sorted list (via a heap sort).
+When the view contacts are merged, their text fields are released from memory.
+The larger the constant the faster a View containing large numbers of items is created.
+However, a larger constant will use more RAM for large Views.
+
+The constant can be changed at ROM build time using patchdata OBY keyword.
+*/
+IMPORT_C extern const TInt KNumberOfContactsToReadPerMerge;
+
+/**
+@internalComponent
+@released
+
+Number of Contacts to process per invocation of the Sorter
+Local Views can't do much whilst sorting, but we want to allow other
+Active Objects in the thread to run. Use to be 50 for the DBMS version, 75 here to match
+performance (i.e. should occupy the server for the same amount of time).
+*/
+const TInt KNumberOfContactsToReadPerChunk = 75;
+
+
+/**
+Private constructor
+*/
+CViewContactManager::CViewContactManager(CContactLocalView& aLocalView, MLplViewIteratorManager& aLplViewMgr, TContactViewPreferences aViewPreferences, CViewContactSortPlugin* aSortPlugin)
+: iLocalView(aLocalView),
+ iViewSessionId(KPLViewSessionIdNull),
+ iViewContacts(NULL),
+ iUnsortedViewContacts(NULL),
+ iViewContactsBuffer(NULL),
+ iSortPluginImpl(aSortPlugin),
+ iViewPreferences(aViewPreferences),
+ iLplViewItemMgr(aLplViewMgr)
+ {
+ }
+
+
+/**
+Destructor
+*/
+CViewContactManager::~CViewContactManager()
+ {
+ if(iViewSessionId != KPLViewSessionIdNull)
+ {
+ iLplViewItemMgr.CloseView(iViewSessionId);
+ }
+
+ delete iIdleSorter;
+
+ if(iViewContacts)
+ {
+ iViewContacts->ResetAndDestroy();
+ delete iViewContacts;
+ }
+ if (iUnsortedViewContacts)
+ {
+ iUnsortedViewContacts->ResetAndDestroy();
+ delete iUnsortedViewContacts;
+ }
+
+ if(iViewContactsBuffer)
+ {
+ iViewContactsBuffer->ResetAndDestroy();
+ delete iViewContactsBuffer;
+ }
+
+ //The class doesn't own iSortPluginImpl,so don't release it here.
+ }
+
+
+/**
+Static factory constructor. Uses two phase construction and
+leaves nothing on the CleanupStack.
+
+@return A pointer to the newly created CViewContactManager object.
+*/
+CViewContactManager* CViewContactManager::NewL(CContactLocalView& aLocalView, MLplPersistenceLayerFactory& aFactory, const CContactTextDef& aTextDef, TContactViewPreferences aViewPreferences, CViewContactSortPlugin* aSortPlugin)
+ {
+ MLplViewIteratorManager& lplViewIteratorManager = aFactory.GetViewIteratorManagerL();
+ CViewContactManager* self = new(ELeave) CViewContactManager(aLocalView, lplViewIteratorManager, aViewPreferences, aSortPlugin);
+ CleanupStack::PushL(self);
+ self->ConstructL(aFactory, aTextDef);
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+
+/**
+Constructor
+*/
+void CViewContactManager::ConstructL(MLplPersistenceLayerFactory& aFactory, const CContactTextDef& aTextDef)
+ {
+ iViewContacts = new(ELeave) RPointerArray<CViewContact>(KContactsArrayGranularity);
+ iUnsortedViewContacts = new(ELeave) RPointerArray<CViewContact>(KUnsortedArrayGranularity);
+
+ // Collation method by index 0 stays the same (only changes at a reboot)
+ iCollationMethod = *Mem::CollationMethodByIndex(0);
+ iCollationMethod.iFlags |= TCollationMethod::EIgnoreNone;
+
+ iViewSessionId = iLplViewItemMgr.OpenViewL(aTextDef, iViewPreferences);
+
+ //Create idle thread to prepare sort asynchronisely
+ iIdleSorter = CIdleContactSorter::NewL(*this, aFactory, iViewPreferences);
+ }
+
+
+/**
+Start sorting with stored sort order.
+*/
+void CViewContactManager::SortL()
+ {
+ iIdleSorter->Stop();
+
+ iLplViewItemMgr.BeginIterateL(iViewSessionId);
+ ResetSort();
+
+ iIdleSorter->Start();
+ }
+
+/**
+Start sorting with given sort order/text definition.
+
+@param aTextDef new text definition would be used for sorting.
+*/
+void CViewContactManager::SortL(const CContactTextDef& aTextDef)
+ {
+ //Stop current sorting.
+ iIdleSorter->Stop();
+
+ iLplViewItemMgr.EndIterateL(iViewSessionId);
+ iLplViewItemMgr.ChangeSortOrderL(iViewSessionId, aTextDef);
+
+ //Restart iterating with changed sort order
+ iLplViewItemMgr.BeginIterateL(iViewSessionId);
+
+ ResetSort();
+ iIdleSorter->Start();
+ }
+
+
+/**
+Start current sorting.
+*/
+void CViewContactManager::StopSortL()
+ {
+ //Stop current sorting.
+ iIdleSorter->Stop();
+
+ //Call the lpl layer to stop iterating view contacts.
+ iLplViewItemMgr.EndIterateL(iViewSessionId);
+ }
+
+
+/**
+Reset contact ids arrays for resort or initial sort, this is called
+when database complete restoring data or view is creating(first sort).
+*/
+void CViewContactManager::ResetSort()
+ {
+ ASSERT(iViewContacts);
+ iViewContacts->ResetAndDestroy();
+
+ ASSERT(iUnsortedViewContacts);
+ iUnsortedViewContacts->ResetAndDestroy();
+
+ if(iViewContactsBuffer)
+ {
+ iViewContactsBuffer->ResetAndDestroy();
+ delete iViewContactsBuffer;
+ iViewContactsBuffer = NULL;
+ }
+ }
+
+
+/**
+Implementation of MContactViewSortObserver interface, this is called with:
+1. TIccViewNotify_IccOnlyLocked: The view is ICCEntryOnly view and ICC store is locked.
+2. TIccViewNotify_IccUnlocked: The view include ICCEntry and ICC store is unlocked
+ (resort is needed)
+
+@param aIccViewNotify flag to show if ICC store is locked or not.
+@return ETrue if there is still view contact in database and needs idle
+ sorting asynchroniser call back again.
+
+@leave KErrNoMemory Out of memory.
+@leave KErrNotFound The view session cannot be found.
+*/
+void CViewContactManager::IccViewNotifyL(TInt aIccViewNotify)
+ {
+ switch(aIccViewNotify)
+ {
+ case TIccViewNotify_IccOnlyLocked:
+ iLocalView.SetState(CContactLocalView::EReady);
+ break;
+ case TIccViewNotify_IccUnlocked:
+ iLocalView.SetState(CContactLocalView::ENotReady);
+ iLplViewItemMgr.EndIterateL(iViewSessionId);
+ break;
+ }
+ }
+
+
+/**
+Implementation of MContactViewSortObserver interface, this is called by
+the idle sorting asynchroniser repeatly.
+
+@param aSortErr if there is error occurs in idle sorting asynchroniser.
+@return ETrue if there is still view contact in database and needs idle
+ sorting asynchroniser call back again.
+*/
+TBool CViewContactManager::IdleSorterNotifyL(TInt aSortErr)
+ {
+ if(aSortErr != KErrNone)
+ {
+ TInt err = KErrNone;
+ TRAP(err, iLplViewItemMgr.EndIterateL(iViewSessionId));
+ iLocalView.SortComplete(aSortErr);
+ return EFalse;
+ }
+
+ if(!iViewContactsBuffer)
+ {
+ //Create a temporary buffer for reading view contact objects.
+ iViewContactsBuffer = new(ELeave) RPointerArray<CViewContact>(KContactsArrayGranularity);
+ }
+
+ CViewContactManager::TReadState readState = ReadInViewContactsL(*iViewContactsBuffer);
+
+ if(readState == EReadFullChunk)
+ {
+ // Full chunk of view contact items have been read, so request another read.
+ return ETrue;
+ }
+
+ //Reach here only when readState is EReadFullForMerge or EReadCompleted
+ if (iSortPluginImpl)
+ {
+ // prepare View Sort plug-in
+ User::LeaveIfError(iSortPluginImpl->SortStart(CViewContactSortPlugin::ESortStartFull, iViewContactsBuffer->Count()));
+ }
+
+ // sort the view contacts in buffer
+ HeapSortL(*iViewContactsBuffer);
+
+ if(iViewContacts->Count() > 0)
+ {
+ //There are sorted view objects stored in iViewContacts, merging is needed.
+ RPointerArray<CViewContact>* mergedArray = MergeL(*iViewContacts, *iViewContactsBuffer);
+
+ // all view contact objects have been merged into iViewContacts,
+ // clean up the buffer array.
+ iViewContactsBuffer->Reset();
+
+ iViewContacts->Close();
+ delete iViewContacts;
+ iViewContacts = mergedArray;
+ }
+ else
+ {
+ //It's first time we get a list of sorted view contat objects
+ iViewContacts->Close();
+ delete iViewContacts;
+ iViewContacts = iViewContactsBuffer;
+ iViewContactsBuffer = NULL;
+ }
+
+ if (iSortPluginImpl)
+ {
+ iSortPluginImpl->SortCompleted();
+ }
+
+ if(readState == EReadCompleted)
+ {
+ //Sorting is complete.
+ iLplViewItemMgr.EndIterateL(iViewSessionId);
+ iLocalView.SortComplete(KErrNone);
+ return EFalse;
+ }
+
+ return ETrue;
+ }
+
+
+/**
+Fill in the given view contacts buffer by asking underlying persistence layer,
+and add all unsorted view contacts into unsorted array directly.
+
+@param aViewContacts CViewContact objects array to fill in and sort.
+@return One of enum in TReadState, see the definition in TReadState for detail
+*/
+CViewContactManager::TReadState CViewContactManager::ReadInViewContactsL(RPointerArray<CViewContact>& aViewContacts)
+ {
+ TInt total = aViewContacts.Count();
+
+ // process a chunk of contacts
+ CViewContact* contact = NULL;
+
+ //Here to use current sorting view preference in iIdleSorter who knows
+ //if we can access ICCEntry store now, therefore decides if we should read in
+ //ICCEntry contacts from the database.
+ TContactViewPreferences sortViewPref = iIdleSorter->SortViewPreferences();
+
+ for(TInt i = 0; i<KNumberOfContactsToReadPerChunk; ++i)
+ {
+ contact = iLplViewItemMgr.NextItemL(iViewSessionId, sortViewPref);
+ if(contact == NULL)
+ {
+ return EReadCompleted;
+ }
+
+ CleanupStack::PushL(contact);
+ if(IsContactSortable(*contact,iViewPreferences))
+ {
+ aViewContacts.AppendL(contact);
+ CleanupStack::Pop(contact);
+ if(++total >= KNumberOfContactsToReadPerMerge)
+ {
+ return EReadFullForMerge;
+ }
+ }
+ else if(iViewPreferences & (EUnSortedAtBeginning | EUnSortedAtEnd))
+ {
+ // It's an unsortable contact, just append its lightweight object
+ // to unsortable view contacts list.
+ contact->ChangeToLightweightObject();
+ iUnsortedViewContacts->AppendL(contact);
+ CleanupStack::Pop(contact);
+ }
+ else
+ {
+ CleanupStack::PopAndDestroy(contact);
+ }
+ }
+
+ return EReadFullChunk;
+ }
+
+
+/**
+Check if the contact type uid matchs the view preferences.
+
+@param aContactTypeUid contact type uid to check.
+@param aTypeToInclude view preferences have flags to define which contact the view should have.
+@return ETrue if the uid match or reverse.
+*/
+TBool CViewContactManager::ContactCorrectType(TUid aContactTypeUid, TContactViewPreferences aTypeToInclude)
+ {
+ TBool correctType(EFalse);
+ if (aContactTypeUid==KUidContactCard || aContactTypeUid==KUidContactOwnCard)
+ {
+ // Ignore Unsorted Contacts flags & White Space flag
+ // catch non- contact views
+ // Should be EContactsOnly, EContactAndGroups & EICCEntriesAndContacts
+ if (0 == ((aTypeToInclude & ~(ESingleWhiteSpaceIsEmptyField | EIgnoreUnSorted | EUnSortedAtBeginning | EUnSortedAtEnd))
+ & (EGroupsOnly | EICCEntriesOnly))) // Ignore 'UnSorted' flags, exclude Groups Only & ICC Only
+ {
+ correctType = ETrue;
+ }
+ }
+ else if (aContactTypeUid == KUidContactGroup)
+ {
+ if (aTypeToInclude & (EGroupsOnly | EContactAndGroups))
+ {
+ correctType = ETrue;
+ }
+ }
+ else if (aContactTypeUid == KUidContactICCEntry)
+ {
+ if (aTypeToInclude & (EICCEntriesOnly | EICCEntriesAndContacts))
+ {
+ correctType = ETrue;
+ }
+ }
+ return correctType;
+ }
+
+
+/**
+Heap sort the give view contacts array.
+
+@param aContacts the array of view contacts to be sorted.
+@leave leave errors from CContactViewBase::CompareContactsAndIdsL
+*/
+void CViewContactManager::HeapSortL(RPointerArray<CViewContact>& aContacts)
+ {
+ // HeapSort (stolen from RPointerArrayBase)
+ TInt ss = aContacts.Count();
+ if ( ss>1 )
+ {
+ TInt sh = ss>>1;
+ FOREVER
+ {
+ CViewContact* si;
+ if (sh != 0)
+ {
+ // make heap
+ --sh;
+ si = aContacts[sh];
+ }
+ else
+ {
+ // sort heap
+ --ss;
+ si = aContacts[ss];
+ aContacts[ss] = aContacts[0];
+ if (ss == 1)
+ {
+ aContacts[0] = si;
+ break;
+ }
+ }
+
+ // sift down
+ TInt ii = sh;
+ TInt jj = sh;
+ FOREVER
+ {
+ jj = (jj+1)<<1;
+ if ((jj >= ss) || (iLocalView.CompareContactsAndIdsL(*aContacts[jj-1],*aContacts[jj]) > 0))
+ {
+ --jj;
+ }
+
+ if ((jj >= ss) || (iLocalView.CompareContactsAndIdsL(*aContacts[jj],*si) <= 0))
+ {
+ break;
+ }
+
+ aContacts[ii] = aContacts[jj];
+ ii = jj;
+ } //FOREVER
+
+ aContacts[ii] = si;
+ } //FOREVER
+ } //if (ss > 1)
+ }
+
+
+/**
+Merge two view contacts array into a result array, here we always assume the items in
+aRightContacts are fulfilled objects.
+
+@param aLeftContacts the left array of view contacts to be merged.
+@param aRightContacts the right array of view contacts to be merged.
+@return the final merged array.
+*/
+RPointerArray<CViewContact>* CViewContactManager::MergeL(RPointerArray<CViewContact>& aLeftContacts, RPointerArray<CViewContact>& aRightContacts)
+ {
+ TInt indexLeft(0);
+ TInt indexRight(0);
+ const TInt KCountLeft = aLeftContacts.Count();
+
+ RPointerArray<CViewContact>* resultContacts = new(ELeave) RPointerArray<CViewContact>(KContactsArrayGranularity);
+ CleanupStack::PushL(TCleanupItem(ResetAndDestroyRPointerArrayPointer, resultContacts));
+
+ while(indexLeft < KCountLeft && indexRight < aRightContacts.Count())
+ {
+ CViewContact* firstLeftContact = static_cast<CViewContact*>(aLeftContacts[indexLeft]);
+ CViewContact* firstRightContact = static_cast<CViewContact*>(aRightContacts[indexRight]);
+
+ if(firstLeftContact->IsLightweightObject())
+ {
+ //The current contact in iViewContact is lightweight object, so we need to load the
+ //fulfilled view contact to compare.
+ CViewContact* contact = iLplViewItemMgr.ItemAtL(firstLeftContact->Id(), iViewSessionId);
+ if(contact == NULL)
+ {
+ //The already sorted contact is not in database!!!
+ User::Leave(KErrNotFound);
+ }
+
+ CleanupStack::PushL(contact);
+ firstLeftContact->CopyL(*contact);
+ CleanupStack::PopAndDestroy(contact);
+ }
+
+ TInt diff = CompareContactsAndIdsL(*firstLeftContact, *firstRightContact);
+
+ if(diff > 0)
+ {
+ resultContacts->AppendL(firstRightContact);
+ firstRightContact->ChangeToLightweightObject();
+ ++indexRight;
+ }
+ else
+ {
+ resultContacts->AppendL(firstLeftContact);
+ firstLeftContact->ChangeToLightweightObject();
+ ++indexLeft;
+ if(diff == 0)
+ {
+ aRightContacts.Remove(indexRight);
+ delete firstRightContact;
+ }
+ }
+ }
+
+ while(indexLeft < KCountLeft)
+ {
+ CViewContact* firstLeftContact = static_cast<CViewContact*>(aLeftContacts[indexLeft]);
+ resultContacts->AppendL(firstLeftContact);
+ ++indexLeft;
+ }
+
+ while(indexRight < aRightContacts.Count())
+ {
+ CViewContact* firstRightContact = static_cast<CViewContact*>(aRightContacts[indexRight]);
+
+ //change all the fullfil view objects to lightweight into the merged list.
+ firstRightContact->ChangeToLightweightObject();
+
+ resultContacts->AppendL(firstRightContact);
+ ++indexRight;
+ }
+
+ CleanupStack::Pop(resultContacts);
+
+ return resultContacts;
+ }
+
+
+/**
+Insert a view contact object into existing sorted view contacts array(iViewContacts).
+
+@param aNewContact the new view contact object to be inserted.
+@param aSortByIdOnly if the sorting method is sorting by id only
+@param aStart start searching position in existing view contacts array.
+@param aIndex the index of the view contact after insertion.
+@leave KErrNoMemory or leave error from CContactViewBase::CompareContactsAndIdsL
+*/
+TInt CViewContactManager::InsertViewContactL(const CViewContact* aNewContact, TBool aSortByIdOnly, TInt aStart)
+ {
+ TInt maxPos = iViewContacts->Count() - 1;
+ TInt minPos = aStart;
+ TInt position = 0;
+ TInt error(KErrNone);
+
+ //Using binary search to find the sorting position for the new contact.
+ if (maxPos >= 0)
+ {
+ if (iSortPluginImpl)
+ {
+ // prepare View Sort plug-in
+ User::LeaveIfError(iSortPluginImpl->SortStart(CViewContactSortPlugin::ESortStartInsertOne, 1));
+ }
+
+ position = (minPos + maxPos + 1) / 2;
+
+ while (maxPos >= minPos)
+ {
+ TInt diff = 0;
+ CViewContact* currContact = static_cast<CViewContact*>((*iViewContacts)[position]);
+
+ if (aSortByIdOnly)
+ {
+ diff = aNewContact->Id() - currContact->Id();
+ }
+ else
+ {
+ //The view contacts stored in sortable contact list(iViewContacts) have been changed to
+ //lightweight objects after sorting has done for memory saving purpose.
+ if(currContact->IsLightweightObject())
+ {
+ //The current contact in iViewContact is lightweight object, so we need to load the
+ //full content into the view contact before compare.
+ CViewContact* contact = iLplViewItemMgr.ItemAtL(currContact->Id(), iViewSessionId);
+ if(contact == NULL)
+ {
+ //the sorted contact is not in the database!!!
+ User::Leave(KErrNotFound);
+ }
+
+ CleanupStack::PushL(contact);
+ diff = CompareContactsAndIdsL(*aNewContact, *contact);
+ CleanupStack::PopAndDestroy(contact);
+ }
+ else
+ {
+ //It's fulfilled object already, use it to compare directly
+ diff = CompareContactsAndIdsL(*aNewContact, *currContact);
+ }
+ }
+
+
+ if (diff == 0)
+ {
+ // duplicate Id
+ error = KErrAlreadyExists;
+ break;
+ }
+
+ if (diff < 0)
+ {
+ maxPos = position - 1;
+ }
+ else // diff > 0
+ {
+ minPos = position + 1;
+ }
+
+ position = (minPos + maxPos + 1) / 2;
+ } //while (maxPos >= minPos)
+
+ if (iSortPluginImpl)
+ {
+ iSortPluginImpl->SortCompleted();
+ }
+ }
+
+ User::LeaveIfError(error);
+
+ //Make the view contact object to be lightweighted before insert to iViewContacts.
+ const_cast<CViewContact*>(aNewContact)->ChangeToLightweightObject();
+ iViewContacts->InsertL(aNewContact, position);
+
+ return position;
+ }
+
+/**
+Get a reference of a view contact by given index to existing view contacts array,
+if the the requested view object is in lightweight(only stored contact item id and
+contact type) the function will fill the whole content to the object.
+
+@param aIndex the index to the contact ids array.
+@leave KErrNoMemory or KErrNotFound
+@return the reference to the requested contact view object(always be fulfiled),
+ the constrains of const is to keep to the same definition of the function
+ in local view which calls down to this function.
+*/
+const CViewContact& CViewContactManager::ContactAtL(TInt aIndex) const
+ {
+ CViewContact& viewContact = ViewContactAtL(aIndex);
+
+ if(viewContact.IsLightweightObject())
+ {
+ CViewContact* contact = iLplViewItemMgr.ItemAtL(viewContact.Id(), iViewSessionId);
+ if(contact == NULL)
+ {
+ User::Leave(KErrNotFound);
+ }
+
+ CleanupStack::PushL(contact);
+ viewContact.CopyL(*contact);
+ CleanupStack::PopAndDestroy(contact);
+ }
+
+ return viewContact;
+ }
+
+
+/**
+Get the view contact object by given index to existing view contacts array.
+
+@param aIndex the index to the contact ids array.
+@param aLoadFullContent if the view contact need to load into full content.
+@leave KErrNotFound
+@return the pointer to the requested contact view object but caller doesn't own the object
+ aLoadFullContent is ETrue if the object was a lightweight object but
+ has been loaded into full content in the function.
+*/
+CViewContact& CViewContactManager::ViewContactAtL(TInt aIndex) const
+ {
+ const TInt unsortedCount = iUnsortedViewContacts->Count();
+ const TInt sortedCount = iViewContacts->Count();
+
+ if(aIndex >= (unsortedCount + sortedCount))
+ {
+ //Out of Bounds.
+ User::Leave(KErrNotFound);
+ }
+
+ CViewContact* viewContact = NULL;
+
+ if(iViewPreferences & EUnSortedAtBeginning)
+ {
+ if(aIndex < unsortedCount)
+ {
+ //contact in unsorted array
+ viewContact = (*iUnsortedViewContacts)[aIndex];
+ }
+ else
+ {
+ //contact in sorted array
+ viewContact = (*iViewContacts)[aIndex - unsortedCount];
+ }
+ }
+ else if ((iViewPreferences & EUnSortedAtEnd) && (aIndex>=sortedCount))
+ {
+ viewContact = (*iUnsortedViewContacts)[aIndex - sortedCount];
+ }
+ else
+ {
+ viewContact = (*iViewContacts)[aIndex];
+ }
+
+ ASSERT(viewContact != NULL);
+ return *viewContact;
+ }
+
+
+/**
+Get the contact item id by given index.
+
+@param aIndex the index to the contact ids array.
+@leave KErrNotFound
+@return the contact item id.
+*/
+TContactItemId CViewContactManager::AtL(TInt aIndex) const
+ {
+ return ViewContactAtL(aIndex).Id();
+ }
+
+
+/**
+Get the number of contacts in the veiw.
+
+@return the number of contacts in the veiw.
+*/
+TInt CViewContactManager::Count() const
+ {
+ ASSERT(iViewContacts);
+ ASSERT(iUnsortedViewContacts);
+
+ return iViewContacts->Count() + iUnsortedViewContacts->Count();
+ }
+
+
+/**
+Find if the given contact item id is in the view.
+
+@param aContactId The contact item id to be retrieved from database.
+@return Index of the requested view contact or KErrNotFound.
+*/
+TInt CViewContactManager::FindL(TContactItemId aContactId) const
+ {
+ TInt index = KErrNotFound;
+ CViewContact* contact = CViewContact::NewLC(aContactId);
+ const TInt unSortedCount = iUnsortedViewContacts->Count();
+
+ // first look in unsorted contacts
+ if(unSortedCount > 0)
+ {
+ // contact may be in the unsorted array
+ index = iUnsortedViewContacts->Find(contact,TIdentityRelation<CViewContact>(CContactViewBase::IdsMatch));
+
+ if ((index != KErrNotFound) && (iViewPreferences & EUnSortedAtEnd))
+ {
+ // account for sorted array size
+ index = index + iViewContacts->Count();
+ }
+ }
+
+ // if not found try sorted contacts
+ if (index == KErrNotFound)
+ {
+ //contact may be in the sorted array
+ index = iViewContacts->Find(contact,TIdentityRelation<CViewContact>(CContactViewBase::IdsMatch));
+
+ if ((index != KErrNotFound) && (iViewPreferences & EUnSortedAtBeginning))
+ {
+ // account for unsorted array size
+ index = index + unSortedCount;
+ }
+ }
+
+ CleanupStack::PopAndDestroy(contact);
+ return index;
+ }
+
+
+/**
+Get all fields content of a view contact and separate them with charaters in aSeparators.
+
+@param aIndex the index of view contact.
+@param aSeparator the charactors to split the fields content.
+@return Buffer of the formated fields content.
+*/
+HBufC* CViewContactManager::AllFieldsLC(TInt aIndex, const TDesC& aSeparator) const
+ {
+ TBool ifWasFullObject = EFalse;
+
+ CViewContact& viewContact = ViewContactAtL(aIndex);
+ if(viewContact.IsLightweightObject())
+ {
+ CViewContact* contact = iLplViewItemMgr.ItemAtL(viewContact.Id(), iViewSessionId);
+ if(contact == NULL)
+ {
+ User::Leave(KErrNotFound);
+ }
+
+ CleanupStack::PushL(contact);
+ viewContact.CopyL(*contact);
+ CleanupStack::PopAndDestroy(contact);
+
+ ifWasFullObject = ETrue;
+ }
+
+ HBufC* buf = FieldsWithSeparatorLC(viewContact,aSeparator);
+
+ if(ifWasFullObject)
+ {
+ //loadFullContent is set in ViewContactAtL if the viewContact was a lightweight object and
+ //loaded to be full content object in that function. So we need to change it to lightweight
+ //in order to save more memory.
+ viewContact.ChangeToLightweightObject();
+ }
+ return buf;
+ }
+
+
+/**
+Insert a contact into the view.
+
+@param aContactId the contact item id of the contact to be inserted.
+@param aViewPreferences view preference which defines the view.
+@return Index of inserted contact or KErrNotFound.
+*/
+TInt CViewContactManager::InsertL(const TContactItemId aContactId, TContactViewPreferences& aViewPreferences)
+ {
+ if(!iIdleSorter->InsertViewPreferences(aViewPreferences))
+ {
+ return KErrNotFound;
+ }
+
+ TInt index = KErrNotFound;
+
+ CViewContact* contact = iLplViewItemMgr.ItemAtL(aContactId, iViewSessionId);
+ if(contact == NULL)
+ {
+ //there is not a contact in database owning the contact id.
+ return KErrNotFound;
+ }
+
+ CleanupStack::PushL(contact);
+
+ if(ContactCorrectType(contact->ContactTypeUid(),aViewPreferences))
+ {
+ if(IsContactSortable(*contact, iViewPreferences))
+ {
+ //Contact has normal fields and can be added to the standard sorted array
+ //Insert using Sort Plugin compare method, and get new index
+ index = InsertViewContactL(contact, EFalse, 0);
+ if (iViewPreferences & EUnSortedAtBeginning)
+ {
+ index += iUnsortedViewContacts->Count();
+ }
+ }
+ else if (iViewPreferences & (EUnSortedAtBeginning | EUnSortedAtEnd))
+ {
+ // unsortable contacts go at the end or beginning
+ // we want this to be stable (e.g. when ICC becomes unlocked)
+ contact->ChangeToLightweightObject();
+ iUnsortedViewContacts->AppendL(contact);
+ index = iUnsortedViewContacts->Count() - 1;
+
+ // calc new index
+ if (iViewPreferences & EUnSortedAtEnd)
+ {
+ index += iViewContacts->Count();
+ }
+ }
+ }
+
+ CleanupStack::Pop(contact);
+
+ if(index == KErrNotFound)
+ {
+ //The contact is not inserted, so delete it.
+ delete contact;
+ }
+
+ return index;
+ }
+
+
+/**
+Remove a contact from the view.
+
+@param aContactId the contact item id of the contact to be removed.
+@return Index of removed contact or KErrNotFound.
+*/
+TInt CViewContactManager::RemoveL(const TContactItemId aContactId)
+ {
+ TInt index=KErrNotFound;
+ const TInt unSortedCount=iUnsortedViewContacts->Count();
+
+ CViewContact* contact = CViewContact::NewLC(aContactId);
+
+ // first look in unsorted contacts
+ if(unSortedCount > 0)
+ {
+ // contact may be in the unsorted array
+ index = iUnsortedViewContacts->Find(contact,TIdentityRelation<CViewContact>(CContactViewBase::IdsMatch));
+
+ if ((index != KErrNotFound) && (iViewPreferences & EUnSortedAtEnd))
+ {
+ CViewContact* viewContact = (*iUnsortedViewContacts)[index];
+ iUnsortedViewContacts->Remove(index);
+ delete viewContact;
+
+ // account for sorted array size
+ index = index + iViewContacts->Count();
+ }
+ }
+
+ // if not found try sorted contacts
+ if (index == KErrNotFound)
+ {
+ //contact may be in the sorted array
+ index = iViewContacts->Find(contact,TIdentityRelation<CViewContact>(CContactViewBase::IdsMatch));
+
+ if (index != KErrNotFound)
+ {
+ CViewContact* viewContact = (*iViewContacts)[index];
+ iViewContacts->Remove(index);
+ delete viewContact;
+
+ if (iViewPreferences & EUnSortedAtBeginning)
+ {
+ // account for unsorted array size
+ index = index + unSortedCount;
+ }
+ }
+ }
+
+ CleanupStack::PopAndDestroy(contact);
+ return index;
+ }
+
+
+/**
+This a delegating method to call IsICCSynchronised in iIdleSorter which is
+also observing ICC synchronization
+
+@return EFalse when iIdleSorter is waiting for ICC cards to be synchronized.
+*/
+TBool CViewContactManager::IsICCSynchronised() const
+ {
+ return iIdleSorter->IsICCSynchronised();
+ }
+
+
+/**
+Cleanup stack call up to handle RPointerArray.
+
+@param aArray Pointer to an RPointerArray<CContactItemField> instance.
+
+@return None.
+*/
+void CViewContactManager::ResetAndDestroyRPointerArrayPointer(TAny *aArray)
+ {
+ RPointerArray<CViewContact> *parray = static_cast< RPointerArray<CViewContact> * >(aArray);
+ parray->Close();
+ delete parray;
+ }
+
+/**
+This function determines whether a contact should be added to the normal
+sorted array (iContacts) or put into the alternative iUnSortedContacts array.
+Depending on the view preferences, these "unsorted" contacts are either
+ignored (deleted), or added to the beginning or end of the iContacts
+sorted list by At(), Find() etc... methods.
+
+@param aContact the view contact object to be checked
+@param aViewPreferences view preferences that determine if aContact is sortable.
+@return ETrue if the view contact object is sortable.
+*/
+TBool CViewContactManager::IsContactSortable(const CViewContact& aContact, TContactViewPreferences& aViewPreferences) const
+ {
+ TBool sortableContact=EFalse;
+
+ // only test contact if it matters
+ if(aViewPreferences & (EIgnoreUnSorted | EUnSortedAtBeginning | EUnSortedAtEnd)) //Distinguish Unsorted
+ {
+ if (iSortPluginImpl)
+ {
+ // use plugin
+ sortableContact = iSortPluginImpl->ViewContactIsSortable(aContact);
+ }
+ else
+ {
+ // ask contact itself
+ sortableContact = aContact.IsSortable();
+ }
+ }
+ else
+ {
+ // Sortable
+ sortableContact = ETrue;
+ }
+
+ return sortableContact;
+ }
+
+
+/**
+Used for view sorting and insersion. In order to give a stable result
+if contact details match, it falls back to comparing contact IDs.
+If a contact view sort plugin is loaded it uses its SortCompareViewContactsL() method.
+
+@param aFirst the first view contact to be compared.
+@param aSecond the second view contact to be compared.
+@return 0 if two view contacts' id are equal.
+*/
+TInt CViewContactManager::CompareContactsAndIdsL(const CViewContact& aFirst, const CViewContact& aSecond) const
+ {
+ TInt result(0);
+
+ if (iSortPluginImpl)
+ {
+ // use View Sort plugin
+ result = iSortPluginImpl->SortCompareViewContactsL(aFirst,aSecond);
+ }
+ else
+ {
+ // no View sort plugin loaded
+ result = TextCompareFieldsL(aFirst, aSecond);
+ }
+
+ if (result == 0)
+ {
+ result = aFirst.Id() - aSecond.Id();
+ }
+
+ return result;
+ }
+
+
+/** Collates two contact items' field contents.
+
+This is done by comparing each contact item on a field by field basis starting
+with the loosest collation level initially, and then progressing to tighter
+collation levels only if the items are considered the same at the looser level.
+This is required so that items differing by case only are sorted correctly.
+If a field isn't present, then the comparison is done using the first field
+that is present.
+
+Faster than static CompareFieldsL() method as it uses prefetched collation method.
+
+@param aFirst The first contact item.
+@param aSecond The second contact item.
+@return A positive value indicates that aFirst's field contents are greater
+than aSecond's (so that aFirst would occur after aSecond in a sort order).
+A negative value indicates that aFirst's field contents are less than aSecond's
+(so that aFirst would occur before aSecond in a sort order). Zero indicates
+that aFirst and aSecond have identical field contents. */
+TInt CViewContactManager::TextCompareFieldsL(const CViewContact& aFirst,const CViewContact& aSecond) const
+ {
+ const TInt KDefaultCollationLevel=3;
+ TInt comparison = 0; // result of comparison, Zero = fields are identical
+ TInt collationLevel = 0;
+
+ do
+ {
+ comparison = CViewContactManager::CompareFieldsWithCollationLevel(aFirst, aSecond, collationLevel, &iCollationMethod);
+ ++collationLevel;
+ }
+ while (comparison == 0 && collationLevel <= KDefaultCollationLevel);
+
+ return comparison;
+ }
+
+
+TInt CViewContactManager::CompareFieldsWithCollationLevel(const CViewContact& aFirst, const CViewContact& aSecond, TInt aCollationLevel, const TCollationMethod* aCollateMethod)
+ {
+ const TInt KLastField = aFirst.FieldCount() - 1;
+
+ TInt retval = 0; // result of comparison, Zero = fields are identical
+ TInt firstField(-1);
+ TInt secondField(-1);
+ TPtrC first;
+ TPtrC second;
+
+ for (TInt counter=0; !retval && (counter <= KLastField); ++counter)
+ {
+ // if the 1st populated field has a greater index than counter,
+ // that means we'd get the same result from FindFirstPopulatedField.
+ // So, don't bother. Of course we always have to run it at least once.
+
+ if (firstField < counter)
+ {
+ first.Set(aFirst.FindFirstPopulatedField(counter, firstField));
+ }
+
+ if (secondField < counter)
+ {
+ second.Set(aSecond.FindFirstPopulatedField(counter, secondField));
+ }
+
+ // no fields in either item
+ if ((firstField < 0) && (secondField < 0))
+ {
+ break;
+ }
+
+ if (firstField < 0)
+ {
+ // first item sorts lower
+ retval = -1;
+ }
+ else if (secondField < 0)
+ {
+ // second item sorts lower
+ retval = 1;
+ }
+ else
+ {
+ // set counter to the first field populated by either contact
+ while ((firstField > counter) && (secondField > counter))
+ {
+ ++counter;
+ }
+
+ retval = first.CompareC(second, aCollationLevel, aCollateMethod);
+ }
+ }
+
+ return retval;
+ }
+
+
+/** Allocates and returns a descriptor filled with the contents of all the fields
+in a contact item.
+
+The fields are separated by the specified separator.
+
+@param aContacts An array of contact items.
+@param aIndex An index into the specified array.
+@param aSeparator The text to use to separate the fields.
+@return A pointer to a heap descriptor containing the contents of each of the
+contact item's fields. The field separator is appended to each field except
+the last one. The pointer is left on the cleanup stack. */
+HBufC* CViewContactManager::FieldsWithSeparatorLC(const CViewContact& aViewContact,const TDesC& aSeparator) const
+ {
+ // Calculate the length of the buffer.
+ TInt bufLength = 0;
+ const TInt separatorLength = aSeparator.Length();
+ const TInt numFields = aViewContact.FieldCount();
+
+ for (TInt ii = 0; ii < numFields; ++ii)
+ {
+ bufLength += aViewContact.Field(ii).Length() + separatorLength;
+ }
+
+ HBufC* buf = HBufC::NewLC(bufLength);
+ TPtr bufPtr(buf->Des());
+
+ // Fill the buffer.
+ for (TInt j = 0; j < numFields; ++j)
+ {
+ bufPtr.Append(aViewContact.Field(j));
+
+ // Only put a separator in if this isn't the last field.
+ if (j != numFields-1)
+ {
+ bufPtr.Append(aSeparator);
+ }
+ }
+ return buf;
+ }