phonebookui/Phonebook2/UIControls/src/CPbk2AdaptiveSearchGridFiller.cpp
changeset 0 e686773b3f54
child 3 04ab22b956c2
--- /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