// 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 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<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);
++indexRight;
}
else
{
resultContacts->AppendL(firstLeftContact);
++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]);
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);
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
{
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<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;
}