--- /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 <FindUtil.h>
+#include <badesca.h>
+
+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