diff -r 000000000000 -r e686773b3f54 phonebookui/Phonebook2/UIControls/src/CPbk2AdaptiveSearchGridFiller.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/phonebookui/Phonebook2/UIControls/src/CPbk2AdaptiveSearchGridFiller.cpp Tue Feb 02 10:12:17 2010 +0200 @@ -0,0 +1,522 @@ +/* +* 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 +#include + +const TInt KMaxAdaptiveGridCacheCount = 10; +const TInt KAdaptiveSearchKeyMapGranularity = 100; +const TInt KAdaptiveSearchRefineStep = 200; +const TInt KContactFormattingFlags = MPbk2ContactNameFormatter::EPreserveLeadingSpaces | MPbk2ContactNameFormatter::EReplaceNonGraphicChars; + + +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& FindText() const + { + return *iFindText; + } + + const TDesC& KeyMap() const + { + return *iKeyMap; + } + }; + +// -------------------------------------------------------------------------- +// CPbk2ContactEditorDlg::CPbk2ContactEditorDlg +// -------------------------------------------------------------------------- +// +CPbk2AdaptiveSearchGridFiller::CPbk2AdaptiveSearchGridFiller( CAknSearchField& aField, MPbk2ContactNameFormatter& aNameFormatter ) + : CActive( CActive::EPriorityStandard ), iSearchField( aField ), iNameFormatter( aNameFormatter ), + iInvalidateAdaptiveSearchGrid( EFalse ),iSetFocusToSearchGrid( ETrue ) + { + CActiveScheduler::Add( this ); + } + +// -------------------------------------------------------------------------- +// CPbk2ContactEditorDlg::~CPbk2ContactEditorDlg +// -------------------------------------------------------------------------- +// +CPbk2AdaptiveSearchGridFiller::~CPbk2AdaptiveSearchGridFiller() + { + Cancel(); + 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(); + } + +void CPbk2AdaptiveSearchGridFiller::StartFilling( const MVPbkContactViewBase& aView, const TDesC& aSearchString ) + { + CPbk2AdaptiveGrid* keyMap = KeyMapFromCache( aSearchString ); + + if( keyMap ) + { + iSearchField.SetAdaptiveGridChars( keyMap->KeyMap() ); + return; + } + + 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; + + 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(); + TInt maxSpacesNumber = 0; + + if( stopCount > itemCount ) + { + stopCount = itemCount; + } + + for( ; iCounter < stopCount; iCounter++ ) + { + const MVPbkViewContact& contact = iView->ContactAtL( iCounter ); + const TInt titleLength = iNameFormatter.MaxTitleLength( contact.Fields(), KContactFormattingFlags ); + HBufC* title = HBufC::NewLC( titleLength ); + TPtr ptrTitle = title->Des(); + iNameFormatter.GetContactTitle( contact.Fields(), ptrTitle, KContactFormattingFlags ); + BuildGridL( ptrTitle, *iSearchString, iKeyMap ); + + // check number of spaces in the contact title + TInt numberOfSpaces = NumberOfSpacesInString( ptrTitle ); + if ( numberOfSpaces > maxSpacesNumber ) + { + maxSpacesNumber = numberOfSpaces; + } + // Check if the contact's title include drgraphs, + // if it is, add it to array to save. + if ( IsDigraphContactsTitleL( ptrTitle ) ) + { + 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, *iSearchString, iKeyMap ); + } + } + + + if( stopCount == itemCount ) + { + SetAdaptiveGridCharsL( maxSpacesNumber ); + 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( !aFindText.Compare( iAdaptiveGridCache[i]->FindText() ) ) + { + 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 ); + iAdaptiveGridCache.InsertL( keyMap, 0 ); + CleanupStack::Pop();//keyMap + + if( iAdaptiveGridCache.Count() > KMaxAdaptiveGridCacheCount ) + { + delete iAdaptiveGridCache[0]; + iAdaptiveGridCache.Remove( 0 ); + } + } + +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 ) + { + TPtr ptr = iKeyMap->Des(); + + //To 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. + TInt searchTextLength = iSearchField.TextLength(); + HBufC* searchText = HBufC::NewL( searchTextLength ); + TPtr ptrSearchText = searchText->Des(); + iSearchField.GetSearchText( ptrSearchText ); + if ( searchTextLength > 0 + && NumberOfSpacesInString( ptrSearchText ) < aMaxSpacesNumber ) + { + ptr.Append( TChar( ' ' ) ); + } + delete searchText; + searchText = NULL; + + 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; + } + + } + } + + iInvalidateAdaptiveSearchGrid = EFalse; + + delete iCurrentGrid; + iCurrentGrid = NULL; + iCurrentGrid = iKeyMap->Des().AllocL(); + + iSearchField.SetAdaptiveGridChars( *iKeyMap ); + if ( iSetFocusToSearchGrid ) + { + // set the focus to findbox + iSearchField.DrawDeferred(); + iSetFocusToSearchGrid = EFalse; + } + + } + + +CDesC16Array* CPbk2AdaptiveSearchGridFiller::SplitContactFieldTextIntoArrayLC( + const TDesC& aText ) + { + const TInt KGranularity = 2; + 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] ) ) + { + // Scan the end of the word + TInt end = beg; + for (; end < textLength && + !iNameFormatter.IsFindSeparatorChar( aText[end] ); + ++end ) + { + } + const TInt len = end - beg; + // Append found word to the array + array->AppendL( aText.Mid( beg,len ) ); + // Scan for next word + beg = end; + } + } + + if ( array->MdcaCount() == 0 && textLength > 0 ) + { + // aText is all word separator characters + // -> make a "word" out of those + array->AppendL( aText ); + } + return array; + } + + + +void CPbk2AdaptiveSearchGridFiller::BuildGridL( const TDesC& aContactTitle, const TDesC& aSearchString, HBufC*& aKeyMap ) + { + CDesC16Array* contactTitles = SplitContactFieldTextIntoArrayLC( aContactTitle ); + CDesC16Array* searchWords = SplitContactFieldTextIntoArrayLC( aSearchString ); + + //in searchWords list, the last term is the only one which generates new keymap characters + //other ones are used only for matching + + if( searchWords->MdcaCount() == 0 ) + { + searchWords->AppendL( KNullDesC ); + } + + if( aSearchString.Length() > 0 ) + { + if( aSearchString[ aSearchString.Length() - 1 ] == TChar( ' ' ) ) + { + //because we now start new search term, we must add KNullDesC so we + //can find the matching new words + searchWords->AppendL( KNullDesC ); + } + } + + + const TInt searchWordCount = searchWords->MdcaCount(); + + TBool contactMatch( searchWordCount == 1 ); + + for( TInt i( 0 ); i < searchWordCount; i++ ) + { + TInt contactTitleCount = contactTitles->MdcaCount(); + + TBool contactTitleMatch( EFalse ); + + for( TInt j( 0 ); j < contactTitleCount; j++ ) + { + TPtrC searchWord = searchWords->MdcaPoint( i ); + TPtrC contactTitle = contactTitles->MdcaPoint( j ); + + const TBool lastSearchWord = ( i == searchWordCount - 1 ); + + TBool match( EFalse ); + + if( lastSearchWord ) + { + if( !contactMatch ) + { + //none of the previous words didin't match, so why this is not filtered? + //marked contact!? + } + else + { + match = iFindUtil->Interface()->MatchAdaptiveRefineL( contactTitle, searchWord, aKeyMap ); + } + } + else + { + match = iFindUtil->Interface()->MatchRefineL( contactTitle, searchWord ); + } + + if( match ) + { + + if( !contactTitleMatch ) + { + contactTitles->Delete( j ); + //allow one search word to take away only one contactTitle + contactTitleMatch = ETrue; + //for loop must go from first contact title to last + //to be consistent with match functionality of VPbk. + j--; contactTitleCount--; + } + contactMatch = ETrue; + } + } + + } + + CleanupStack::PopAndDestroy( 2 );//contactTitle, searchWords + } + +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; + } +// End of File