phonebookui/Phonebook2/UIControls/src/CPbk2AdaptiveSearchGridFiller.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 15 Jul 2010 18:22:55 +0300
branchRCL_3
changeset 17 2666d9724c76
parent 15 34879f5cfc63
permissions -rw-r--r--
Revision: 201025 Kit: 2010127

/*
* Copyright (c) 2005-2007 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Phonebook2 contact editor dialog.
*
*/


// INCLUDE FILES
#include "CPbk2AdaptiveSearchGridFiller.h"
#include "MVPbkViewContact.h"
#include "MVPbkContactViewBase.h"
#include "MPbk2ContactNameFormatter.h"
#include "MPbk2FilteredViewStack.h"

#include <MVPbkBaseContactFieldCollection.h>
#include <MVPbkContactFieldTextData.h>
#include <MVPbkBaseContactField.h>


#include <MPbk2ContactViewSupplier.h>
#include <MPbk2ApplicationServices.h>
#include <MPbk2AppUi.h>
#include <CPbk2StoreConfiguration.h>
#include <MPbk2ContactNameFormatter2.h>
#include <FindUtil.h>
#include <badesca.h>
#include <featmgr.h>

#include <CPsRequestHandler.h>

const TInt KMaxAdaptiveGridCacheCount = 10;
const TInt KAdaptiveSearchKeyMapGranularity = 100;
const TInt KAdaptiveSearchRefineStep = 25;
const TInt KContactFormattingFlags = MPbk2ContactNameFormatter::EPreserveLeadingSpaces |
            MPbk2ContactNameFormatter::EReplaceNonGraphicChars |
            MPbk2ContactNameFormatter::EDisableCompanyNameSeparator;

namespace {
enum TNameOrder
    {
    ETopContactOrderNumber = 0,     //TC control data, not shown
    ENameFirstPart,                 //Contact name data
    ENameSecondPart,                //Contact name data
    ENameCompanyPart                //to support Company name
    };
} // namespace

NONSHARABLE_CLASS(CPbk2AdaptiveGrid) : public CBase
	{
	HBufC* iFindText;
	HBufC* iKeyMap;

	public:

		CPbk2AdaptiveGrid()
			{
			}

		~CPbk2AdaptiveGrid()
			{
			delete iFindText;
			delete iKeyMap;
			}

		void SetKeyMapL( const TDesC& aFindText, const TDesC& aKeyMap )
			{
			delete iFindText;
			delete iKeyMap;

			iFindText = iKeyMap = NULL;

			iFindText = aFindText.AllocL();
			iKeyMap = aKeyMap.AllocL();
			}

		const TDesC& GetFindText() const
			{
			return *iFindText;
			}

		const TDesC& GetKeyMap() const
			{
			return *iKeyMap;
			}
	};

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::CPbk2ContactEditorDlg
// --------------------------------------------------------------------------
//
CPbk2AdaptiveSearchGridFiller::CPbk2AdaptiveSearchGridFiller( CAknSearchField& aField, MPbk2ContactNameFormatter& aNameFormatter )
	: CActive( CActive::EPriorityIdle ), iSearchField( aField ), iNameFormatter( aNameFormatter ),
	iInvalidateAdaptiveSearchGrid( EFalse ),iSetFocusToSearchGrid( ETrue )
    {
	CActiveScheduler::Add( this );
    }

// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::~CPbk2ContactEditorDlg
// --------------------------------------------------------------------------
//
CPbk2AdaptiveSearchGridFiller::~CPbk2AdaptiveSearchGridFiller()
    {
    Cancel();
    if ( iPsHandler )
        {
        iPsHandler->RemoveObserver( this );
        delete iPsHandler;
        }
	delete iKeyMap;
	delete iCurrentGrid;
	iAdaptiveGridCache.ResetAndDestroy();
	delete iSearchString;
	delete iFindUtil;
	iDigraphContactsTitleArray.ResetAndDestroy();
    }


// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::NewL
// --------------------------------------------------------------------------
//
CPbk2AdaptiveSearchGridFiller* CPbk2AdaptiveSearchGridFiller::NewL(  CAknSearchField& aField, MPbk2ContactNameFormatter& aNameFormatter )
    {
    CPbk2AdaptiveSearchGridFiller* self =
        new(ELeave) CPbk2AdaptiveSearchGridFiller( aField, aNameFormatter );
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }


// --------------------------------------------------------------------------
// CPbk2ContactEditorDlg::ConstructL
// --------------------------------------------------------------------------
//
void CPbk2AdaptiveSearchGridFiller::ConstructL()
    {
	iKeyMap = HBufC::NewL( KAdaptiveSearchKeyMapGranularity );
	iFindUtil = CFindUtil::NewL();
    // UI Language
	TLanguage uiLanguage = User::Language();
	if ( uiLanguage != ELangJapanese && uiLanguage != ELangPrcChinese && 
	        uiLanguage != ELangHongKongChinese && uiLanguage != ELangTaiwanChinese &&
	        uiLanguage != ELangKorean )
	    {  
        iPsHandler = CPSRequestHandler::NewL();
        iPsHandler->AddObserverL( this );
	    }
    }

void CPbk2AdaptiveSearchGridFiller::StartFillingL( const MVPbkContactViewBase& aView,
        const TDesC& aSearchString, TBool aClearCache )
	{
	
    if( aClearCache )
        {
        ClearCache();
        }
    
	if ( IsActive() && iView == &aView && iViewItemCount == aView.ContactCountL() 
	        && iSearchString && !iSearchString->Compare( aSearchString ) )
	    {
	    return;
	    }
	else
	    {
	    StopFilling();
	    }

    CPbk2AdaptiveGrid* keyMap = KeyMapFromCache( aSearchString );
    
    if( keyMap )
        {
        iSearchField.SetAdaptiveGridChars( keyMap->GetKeyMap() );
        return;
        }
    
	iViewItemCount = aView.ContactCountL();
	delete iSearchString;
	iSearchString = NULL;

	iSearchString = aSearchString.AllocL();

	// If there is no search word, the user is not searching any contacts
	// so we should reset the array to prepare for the next searching.
    if ( iSearchString->Length() == 0 )
    	{
    	iDigraphContactsTitleArray.ResetAndDestroy();
    	}
	iView = &aView;

	iKeyMap->Des().Zero();

	iCounter = 0;

	if ( iSearchString->Length() <= KPsAdaptiveGridSupportedMaxLen && GridFromPsEngineL( aView ) )
	    {
	    return;
	    }
	
	SetActive();
	TRequestStatus* status = &iStatus;
	User::RequestComplete( status, KErrNone );
	}

void CPbk2AdaptiveSearchGridFiller::StopFilling()
	{
	Cancel();
	iView = NULL;
	}

void CPbk2AdaptiveSearchGridFiller::RunL()
	{
	if( !iView )
		{
		return;
		}

	TInt stopCount = iCounter + KAdaptiveSearchRefineStep;
	const TInt itemCount = iView->ContactCountL();
    
	if( stopCount > itemCount )
		{
		stopCount = itemCount;
		}

	TInt maxSpacesNumber = 0;

    CDesC16Array* searchWordsArray = SplitSearchFieldTextIntoArrayLC( *iSearchString );
    TInt searchStringSpacesNumber = searchWordsArray->MdcaCount() - 1;
    
	for( ; iCounter < stopCount; iCounter++ )
		{
		const MVPbkViewContact& contact = iView->ContactAtL( iCounter );
		const TInt titleLength = iNameFormatter.MaxTitleLength( contact.Fields(), KContactFormattingFlags );
		HBufC* title = NULL;
		
		if( FeatureManager::FeatureSupported( KFeatureIdFfContactsCompanyNames ) )
            {
            MPbk2ContactNameFormatter2* nameformatterExtension =
                    reinterpret_cast<MPbk2ContactNameFormatter2*>( iNameFormatter.
                    ContactNameFormatterExtension( MPbk2ContactNameFormatterExtension2Uid ) );
            if ( nameformatterExtension && titleLength )
                {
                title = nameformatterExtension->GetContactTitleWithCompanyNameL( contact.Fields(),
                    KContactFormattingFlags );
                
                }
            }
        else if ( titleLength )
            {
            title = iNameFormatter.GetContactTitleL( contact.Fields(), KContactFormattingFlags );
            
            // In FDN, the number will be displayed in the list if the contact is no name.
            // If it is, set the search string as NULL.
            if ( IsActualTitleEmpty( contact ) )    
                {
                delete title;
                title = NULL;
            	}
            }
		
		if ( !title )
		    {
		    title = HBufC::NewL( titleLength );
		    }

        CleanupStack::PushL( title );
		BuildGridL( *title, searchWordsArray, iKeyMap );
		
		// check number of spaces in the contact title
		TInt numberOfSpaces = NumberOfSpacesInString( *title );
		if ( numberOfSpaces > maxSpacesNumber )
		    {
		    maxSpacesNumber = numberOfSpaces;
		    }
		// Check if the contact's title include drgraphs,
		// if it is, add it to array to save.
		if ( IsDigraphContactsTitleL( *title ) )
			{			
			iDigraphContactsTitleArray.AppendL( title );
			CleanupStack::Pop(); //title
			}
		else
			{
			CleanupStack::PopAndDestroy( title );
			}
		}
	// If there are titles in array, we should add them to build grids again,
	// because the contacts include drgraphs will be filtered 
	// when the application builds grids again.
    if ( iDigraphContactsTitleArray.Count()!= 0 )
    	{
    	for( TInt i(0); i < iDigraphContactsTitleArray.Count() ; i++ )
    		{
    		TPtr ptrContactsTitle = iDigraphContactsTitleArray[i]->Des();
        	BuildGridL( ptrContactsTitle, searchWordsArray, iKeyMap );
    		}
		}

    CleanupStack::PopAndDestroy( searchWordsArray ); //searchWordsArray

	if( stopCount == itemCount )
		{
		SetAdaptiveGridCharsL( maxSpacesNumber, searchStringSpacesNumber );
		AddKeyMapToCacheL( *iSearchString, *iKeyMap );
		}
	else
		{
		//else continue
		SetActive();
		TRequestStatus* status = &iStatus;
		User::RequestComplete( status, KErrNone );
		}
	}

void CPbk2AdaptiveSearchGridFiller::DoCancel()
	{
	iView = NULL;
	}

TInt CPbk2AdaptiveSearchGridFiller::RunError( TInt /*aError*/ )
	{
	//ignore errors, nothing critical has happened, lets forget it
	return KErrNone;
	}

CPbk2AdaptiveGrid* CPbk2AdaptiveSearchGridFiller::KeyMapFromCache( const TDesC& aFindText )
	{
	const TInt count = iAdaptiveGridCache.Count();

	for( TInt i( 0 ); i < count; i++ )
		{
		if( iAdaptiveGridCache[i] != NULL
		    && !aFindText.Compare( iAdaptiveGridCache[i]->GetFindText() ) )
			{
			return iAdaptiveGridCache[i];
			}
		}

	return NULL;
	}

void CPbk2AdaptiveSearchGridFiller::AddKeyMapToCacheL( const TDesC& aFindText, const TDesC& aKeyMap )
	{
    CPbk2AdaptiveGrid* keyMap = new( ELeave )CPbk2AdaptiveGrid;
    CleanupStack::PushL( keyMap );
    keyMap->SetKeyMapL( aFindText, aKeyMap );

    // Keep always in the cache at position 0 the grid cache element for empty find box,
    // which is the one that requires more time to be built
    if ( aFindText.Length() == 0 )
        {
        if ( iAdaptiveGridCache.Count() > 0 )
            {
            delete iAdaptiveGridCache[0];
            iAdaptiveGridCache.Remove( 0 );
            }

        iAdaptiveGridCache.InsertL( keyMap, 0 );
        }
    else
        {
        if ( iAdaptiveGridCache.Count() == 0 )
        {
        iAdaptiveGridCache.InsertL( NULL, 0 );
        }

        iAdaptiveGridCache.InsertL( keyMap, 1 );
    
        // Delete oldest cache element
        if( iAdaptiveGridCache.Count() > KMaxAdaptiveGridCacheCount )
            {
            delete iAdaptiveGridCache[KMaxAdaptiveGridCacheCount];
            iAdaptiveGridCache.Remove( KMaxAdaptiveGridCacheCount );
            }
        }

    CleanupStack::Pop(); //keyMap
	}

void CPbk2AdaptiveSearchGridFiller::ClearCache()
	{
	iAdaptiveGridCache.ResetAndDestroy();
	if ( iCurrentGrid )
	    {
        delete iCurrentGrid;
        iCurrentGrid = NULL;
	    }
	}

void CPbk2AdaptiveSearchGridFiller::InvalidateAdaptiveSearchGrid()
	{
	iInvalidateAdaptiveSearchGrid = ETrue;
	}

void CPbk2AdaptiveSearchGridFiller::SetFocusToAdaptiveSearchGrid()
    {
    iSetFocusToSearchGrid = ETrue;
    }

void CPbk2AdaptiveSearchGridFiller::SetAdaptiveGridCharsL(
        const TInt aMaxSpacesNumber, const TInt aSearchStringSpacesNumber )
	{
	TPtr ptr = iKeyMap->Des();

	// Do upper case for all characters
	ptr.UpperCase();
	CDesCArray* array = new (ELeave) CDesCArrayFlat( KAdaptiveSearchKeyMapGranularity );
	CleanupStack::PushL( array );
	TInt length = ptr.Length();

	for( TInt ii = 0; ii < length; ii++ )
	    {
	    array->AppendL( ptr.Mid( ii, 1 ) );
	    }

	// Alphabetical sort
	array->Sort( ECmpCollated );
	ptr.Zero();

    // Add space character only if:
	// - user typed already some characters in the find pane,
	// - and more spaces can be found in contacts than in the current search string,
	// - and space is not the last character in the search string.
    if ( iSearchString->Length() > 0 
         && aMaxSpacesNumber > aSearchStringSpacesNumber
         && (*iSearchString)[iSearchString->Length() - 1] != TChar( ' ' ) )
        {
        ptr.Append( TChar( ' ' ) );
        }
     
	for( TInt ii = 0; ii < length; ii++ )
	    {
	    ptr.Append(array->MdcaPoint( ii ));
	    }
	CleanupStack::PopAndDestroy();//array

	if( iCurrentGrid )
		{
		if( !iCurrentGrid->Des().Compare( *iKeyMap ) )
			{
			//same grid again
			if( !iInvalidateAdaptiveSearchGrid )
				{
				//if grid hasn't been invalidated, we do not need to set it again
				return;
				}
			}
		}

	delete iCurrentGrid;
	iCurrentGrid = NULL;
	iCurrentGrid = iKeyMap->Des().AllocL();

	iSearchField.SetAdaptiveGridChars( *iKeyMap );
    
    iInvalidateAdaptiveSearchGrid = EFalse;
    
	if ( iSetFocusToSearchGrid )
	    {
        // set the focus to findbox
	    iSearchField.DrawDeferred();
	    iSetFocusToSearchGrid = EFalse;
	    }

	}


CDesC16Array* CPbk2AdaptiveSearchGridFiller::SplitContactFieldTextIntoArrayLC(
        const TDesC& aText )
    {
    // Attempt to minimize the allocations considering 3 words for the search fields:
    // FirstName, LastName, CompanyName.
    const TInt KGranularity = 2; // Attempt to minimize the allocations

    CDesCArrayFlat* array = new ( ELeave ) CDesCArrayFlat( KGranularity );
    CleanupStack::PushL( array );
    const TInt textLength = aText.Length();
    for ( TInt beg = 0; beg < textLength; beg++ )
        {
        // Skip separators before next word
        if ( iNameFormatter.IsFindSeparatorChar( aText[beg] ) )
            {
            continue;
            }
        
        // Scan till the end of the word
        TInt end;
        for ( end = beg + 1;
              end < textLength && !iNameFormatter.IsFindSeparatorChar( aText[end] );
              ++end )
            {
            }

        // Append found word to the array
        array->AppendL( aText.Mid( beg, end - beg) );

        // Scan for next word
        beg = end;
        }

    return array;
    }

CDesC16Array* CPbk2AdaptiveSearchGridFiller::SplitSearchFieldTextIntoArrayLC(
        const TDesC& aText )
    {
    CDesC16Array* searchWordsArray = SplitContactFieldTextIntoArrayLC( aText );

    // In searchWordsArray, the last word is the only one which generates new keymap characters
    // for the grid; the other words are used only for matching the contact words.
    //
    // KNullDesC fake word as last word in search string will match all contact words so that all
    // initials of contact words will be put into the grid.
    // We do this in case the search string is empty or the last character is a space separator.
    
    if( searchWordsArray->MdcaCount() == 0 ||
        ( aText.Length() > 0 && aText[aText.Length() - 1] == TChar(' ') ) )
        {
        searchWordsArray->AppendL( KNullDesC );
        }

    return searchWordsArray;
    }

void CPbk2AdaptiveSearchGridFiller::BuildGridL( const TDesC& aContactString, const CDesC16Array* aSearchWords, HBufC*& aKeyMap )
	{
    CDesC16Array* contactWords = SplitContactFieldTextIntoArrayLC( aContactString );
	
    const TInt contactWordCount = contactWords->MdcaCount();
	const TInt searchWordCount = aSearchWords->MdcaCount();

    TPtrC searchWord;
    TPtrC contactWord;

    // Try to make as fast algorithm as possible if there is only one search word,
    // which is the most common case    
    if ( searchWordCount == 1 )
        {
        searchWord.Set( aSearchWords->MdcaPoint( 0 ) ); // Search word

        for( TInt j = 0; j < contactWordCount; j++ )
            {
            contactWord.Set( contactWords->MdcaPoint( j ) );
    
            iFindUtil->Interface()->MatchAdaptiveRefineL( contactWord, searchWord, aKeyMap );
            }
        }
    
    // The clients of this method will provide at least one search word, so 0 is unexpected
    else if ( searchWordCount > 1 )
        {
        RArray<TBool> contactWordsMatchedArray;
        contactWordsMatchedArray.ReserveL( contactWordCount );
        for ( TInt i = 0; i < contactWordCount; ++i )
            {
            contactWordsMatchedArray.AppendL( EFalse );
            }

        TBool matched = ETrue;

        // Scan search words except for the last one
        for ( TInt i = 0; matched && i < searchWordCount - 1; i++ )
            {
            searchWord.Set( aSearchWords->MdcaPoint( i ) );

            matched = EFalse; // Search word not matched yet
            
            // Check if the search word is matched in the contact
            for( TInt j = 0; !matched && j < contactWordCount; j++ )
                {
                contactWord.Set( contactWords->MdcaPoint( j ) );
    
                // Partially or fully matched
                if ( iFindUtil->Interface()->MatchRefineL( contactWord, searchWord ) )
                    {
                    // Allow one search word to match only one contact word.
                    // This could be done better if both search and grid creation would
                    // work in the same way for contacts matching...
                    // Example: Contact: "Dim Din Dit"
                    //          Search:  "DIN DI"
                    //          - DIN is matched fully
                    //          - DI is matched partially and assigned to "Dim"
                    //          - The grid will show "_T" instead of "_MT"
                    contactWordsMatchedArray[j] = ETrue;    
                    matched = ETrue;
                    }
                }
            }

        // If all search words before the last one matched (fully or partially),
        // add characters to the grid using last search word.
        if ( matched )
            {
            searchWord.Set( aSearchWords->MdcaPoint( searchWordCount - 1 ) ); // Last search word
    
            for( TInt j = 0; j < contactWordCount; j++ )
                {
                // skip Contact words matched by previous search words
                if (contactWordsMatchedArray[j])
                    {
                    continue;
                    }
    
                contactWord.Set( contactWords->MdcaPoint( j ) );
    
                iFindUtil->Interface()->MatchAdaptiveRefineL( contactWord, searchWord, aKeyMap );
                }
            }
    
        contactWordsMatchedArray.Close();
        }
        
	CleanupStack::PopAndDestroy( contactWords );
	}

TInt CPbk2AdaptiveSearchGridFiller::NumberOfSpacesInString(
    const TDesC& aSearchString )
    {
    TInt numberOfSpaces = 0;
	TInt searchResult = 0;
	TPtrC ptr = aSearchString;
	while ( searchResult != KErrNotFound )
	    {
		searchResult = ptr.Locate( TChar( ' ' ) );
		if ( searchResult != KErrNotFound )
		    {
		    numberOfSpaces++;
		    ptr.Set( ptr.Right( ptr.Length() - searchResult - 1 ) );
		    }
		}
    return numberOfSpaces;
    }

TBool CPbk2AdaptiveSearchGridFiller::IsDigraphContactsTitleL(const TDesC& aContactTitle)
	{
	TBool isDigraphic( EFalse );
	// Go through the contactTitles one-by-one and check if they
	// include digraphs
	const TInt KDigraphLength(2);
	if ( aContactTitle.Length()>= KDigraphLength )
		{
		TPtrC substring = aContactTitle.Left(1);
		if( !iFindUtil->Interface()->MatchRefineL( aContactTitle, substring ) )
			{
			// The substring did not match the characters of the contactTitles
			// For example with Croatian locale the contactTitles "nj" does
			// not include the substring "n" because "nj" is a digraph
			isDigraphic = ETrue;
			}
		}
	return isDigraphic;
	}

void CPbk2AdaptiveSearchGridFiller::HandlePsResultsUpdate(
        RPointerArray<CPsClientData>& /*searchResults*/,
        RPointerArray<CPsPattern>& /*searchSeqs*/ )
    {
    
    }

void CPbk2AdaptiveSearchGridFiller::HandlePsError( TInt /*aErrorCode*/ )
    {
    
    }

void CPbk2AdaptiveSearchGridFiller::CachingStatus( TCachingStatus& aStatus, TInt& /*aError*/ )
    {
    TRAP_IGNORE(
    MVPbkContactViewBase* allContactsView = Phonebook2::Pbk2AppUi()->ApplicationServices().ViewSupplier().AllContactsViewL();
    
    const MPbk2FilteredViewStack* filteredView = dynamic_cast<const MPbk2FilteredViewStack*> ( iView );
    
    if ( aStatus >= ECachingComplete && filteredView && filteredView->Level() == 0 && &filteredView->BaseView() == allContactsView )
        {
        HBufC* string = iSearchString->AllocL();
        CleanupStack::PushL( string );
        StartFillingL( *iView, *string, ETrue );
        CleanupStack::PopAndDestroy( string  );
        }
        );
    }

TBool CPbk2AdaptiveSearchGridFiller::GridFromPsEngineL( const MVPbkContactViewBase& aView )
    {
    if ( iPsHandler == NULL )
        {
        return EFalse;
        }
    MPbk2ApplicationServices& appServices = Phonebook2::Pbk2AppUi()->ApplicationServices();
    MVPbkContactViewBase* allContactsView = appServices.ViewSupplier().AllContactsViewL();
    const MPbk2FilteredViewStack* filteredView = dynamic_cast<const MPbk2FilteredViewStack*> ( &aView );
        
    if ( filteredView && filteredView->Level() == 0 && &filteredView->BaseView() == allContactsView )
        {
        CPbk2StoreConfiguration& config = appServices.StoreConfiguration();
        CVPbkContactStoreUriArray* stores = NULL;
        stores = config.CurrentConfigurationL();
        if ( !stores || stores->Count() == 0 )
            {
            delete stores;
            return EFalse;
            }
        
        TInt count = stores->Count();
        CleanupStack::PushL(stores);
        
        CDesCArrayFlat* array = new ( ELeave ) CDesCArrayFlat( count );
        CleanupStack::PushL( array );
        
        for ( TInt i = 0; i < count; ++i)
            {
            TVPbkContactStoreUriPtr uriPtr = stores->operator[](i);
            array->AppendL( uriPtr.UriDes() );
            }

        TBool companyName = EFalse;
        TBuf<KPsAdaptiveGridStringMaxLen> gridChars;
        if( FeatureManager::FeatureSupported( KFeatureIdFfContactsCompanyNames ) )
            {
            companyName = ETrue;
            }
        iPsHandler->GetAdaptiveGridCharactersL( *array, *iSearchString, companyName, gridChars );
        
        CleanupStack::PopAndDestroy( array );
        CleanupStack::PopAndDestroy( stores );
        
        if ( !gridChars.Length() && iViewItemCount > 0 )
            {
            // grid should be created on standard way
            return EFalse;
            }
        if ( iKeyMap->Des().MaxLength() < gridChars.Length() )
            {
            iKeyMap = iKeyMap->ReAllocL( gridChars.Length() );
            }
        iKeyMap->Des().Copy( gridChars );
        
        delete iCurrentGrid;
        iCurrentGrid = NULL;
        iCurrentGrid = iKeyMap->Des().AllocL();

        iSearchField.SetAdaptiveGridChars( *iKeyMap );
        
        iInvalidateAdaptiveSearchGrid = EFalse;
        
        if ( iSetFocusToSearchGrid )
            {
            // set the focus to findbox
            iSearchField.DrawDeferred();
            iSetFocusToSearchGrid = EFalse;
            }
        AddKeyMapToCacheL( *iSearchString, *iKeyMap );
        return ETrue;
        }
    else
        {
        return EFalse;
        }
    }

TBool CPbk2AdaptiveSearchGridFiller::IsActualTitleEmpty( const MVPbkViewContact& aContact )
    {
    TBool result = ETrue;
    const TInt fieldCount = aContact.Fields().FieldCount();
    if ( fieldCount > ENameCompanyPart )
        {
        const MVPbkBaseContactField& field = aContact.Fields().FieldAt( ENameCompanyPart );
        if ( iNameFormatter.IsTitleField( field ) )
            {
            return EFalse;
            }
        }
   
    if ( fieldCount > ENameFirstPart ) 
        {
        const MVPbkBaseContactField& field = aContact.Fields().FieldAt( ENameFirstPart );
        if ( iNameFormatter.IsTitleField( field ) )
            {
            const MVPbkContactFieldData& fieldData = field.FieldData();
            if ( fieldData.DataType() == EVPbkFieldStorageTypeText )
                {
                const TDesC& fieldText = MVPbkContactFieldTextData::Cast( fieldData ).Text();
                TInt length = fieldText.Length();
                    
                if ( length > 0 )
                    {
                    TInt firstNonSpaceChar = 0;
                    while ( firstNonSpaceChar < length 
                        && TChar( fieldText[firstNonSpaceChar] ).IsSpace() )
                        {
                        ++firstNonSpaceChar;
                        }
                    if ( firstNonSpaceChar != length )
                        {
                        result = EFalse;
                        }
                    }   
                }
            }
        }
    return result;
    }
// End of File