diff -r 5b6f26637ad3 -r f4a778e096c2 phonebookui/Phonebook2/UIControls/src/CPbk2AdaptiveSearchGridFiller.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/phonebookui/Phonebook2/UIControls/src/CPbk2AdaptiveSearchGridFiller.cpp Wed Sep 01 12:29:52 2010 +0100 @@ -0,0 +1,818 @@ +/* +* 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 +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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( 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 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& /*searchResults*/, + RPointerArray& /*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 ( 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 ( &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 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