diff -r 5b6f26637ad3 -r f4a778e096c2 phonebookengines/contactsmodel/cntplsql/src/cviewcontactmanager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/phonebookengines/contactsmodel/cntplsql/src/cviewcontactmanager.cpp Wed Sep 01 12:29:52 2010 +0100 @@ -0,0 +1,1183 @@ +// 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 +#include "cntviewprivate.h" +#include "cviewcontactmanager.h" + +#include +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#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(KContactsArrayGranularity); + iUnsortedViewContacts = new(ELeave) RPointerArray(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(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* 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& 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= KNumberOfContactsToReadPerMerge) + { + return EReadFullForMerge; + } + } + else if(iViewPreferences & (EUnSortedAtBeginning | EUnSortedAtEnd)) + { + // It's an unsortable contact, + // just append it to unsortable view contacts list. + 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) + { + if (!(aTypeToInclude & (EGroupsOnly | EICCEntriesOnly))) + { + correctType = ETrue; + } + } + else if (aContactTypeUid == KUidContactOwnCard) + { + if (!(aTypeToInclude & (EGroupsOnly | EICCEntriesOnly | EContactCardsOnly))) + { + 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& 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* CViewContactManager::MergeL(RPointerArray& aLeftContacts, RPointerArray& aRightContacts) + { + TInt indexLeft(0); + TInt indexRight(0); + const TInt KCountLeft = aLeftContacts.Count(); + + RPointerArray* resultContacts = new(ELeave) RPointerArray(KContactsArrayGranularity); + CleanupStack::PushL(TCleanupItem(ResetAndDestroyRPointerArrayPointer, resultContacts)); + + while(indexLeft < KCountLeft && indexRight < aRightContacts.Count()) + { + CViewContact* firstLeftContact = static_cast(aLeftContacts[indexLeft]); + CViewContact* firstRightContact = static_cast(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); + ++indexRight; + } + else + { + resultContacts->AppendL(firstLeftContact); + ++indexLeft; + if(diff == 0) + { + aRightContacts.Remove(indexRight); + delete firstRightContact; + } + } + } + + while(indexLeft < KCountLeft) + { + CViewContact* firstLeftContact = static_cast(aLeftContacts[indexLeft]); + resultContacts->AppendL(firstLeftContact); + ++indexLeft; + } + + while(indexRight < aRightContacts.Count()) + { + CViewContact* firstRightContact = static_cast(aRightContacts[indexRight]); + 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((*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); + + 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(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(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 + { + 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); + } + + HBufC* buf = FieldsWithSeparatorLC(viewContact,aSeparator); + + 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) + 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(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(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 instance. + +@return None. +*/ +void CViewContactManager::ResetAndDestroyRPointerArrayPointer(TAny *aArray) + { + RPointerArray *parray = static_cast< RPointerArray * >(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; + }