phonebookui/Phonebook2/UIControls/src/CPbk2AdaptiveSearchGridFiller.cpp
changeset 0 e686773b3f54
child 3 04ab22b956c2
equal deleted inserted replaced
-1:000000000000 0:e686773b3f54
       
     1 /*
       
     2 * Copyright (c) 2005-2007 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Phonebook2 contact editor dialog.
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 // INCLUDE FILES
       
    20 #include "CPbk2AdaptiveSearchGridFiller.h"
       
    21 #include "MVPbkViewContact.h"
       
    22 #include "MVPbkContactViewBase.h"
       
    23 #include "MPbk2ContactNameFormatter.h"
       
    24 
       
    25 #include <FindUtil.h>
       
    26 #include <badesca.h>
       
    27 
       
    28 const TInt KMaxAdaptiveGridCacheCount = 10;
       
    29 const TInt KAdaptiveSearchKeyMapGranularity = 100;
       
    30 const TInt KAdaptiveSearchRefineStep = 200;
       
    31 const TInt KContactFormattingFlags = MPbk2ContactNameFormatter::EPreserveLeadingSpaces | MPbk2ContactNameFormatter::EReplaceNonGraphicChars;
       
    32 
       
    33 
       
    34 NONSHARABLE_CLASS(CPbk2AdaptiveGrid) : public CBase
       
    35 	{
       
    36 	HBufC* iFindText;
       
    37 	HBufC* iKeyMap;
       
    38 
       
    39 	public:
       
    40 
       
    41 		CPbk2AdaptiveGrid()
       
    42 			{
       
    43 			}
       
    44 
       
    45 		~CPbk2AdaptiveGrid()
       
    46 			{
       
    47 			delete iFindText;
       
    48 			delete iKeyMap;
       
    49 			}
       
    50 
       
    51 		void SetKeyMapL( const TDesC& aFindText, const TDesC& aKeyMap )
       
    52 			{
       
    53 			delete iFindText;
       
    54 			delete iKeyMap;
       
    55 
       
    56 			iFindText = iKeyMap = NULL;
       
    57 
       
    58 			iFindText = aFindText.AllocL();
       
    59 			iKeyMap = aKeyMap.AllocL();
       
    60 			}
       
    61 
       
    62 		const TDesC& FindText() const
       
    63 			{
       
    64 			return *iFindText;
       
    65 			}
       
    66 
       
    67 		const TDesC& KeyMap() const
       
    68 			{
       
    69 			return *iKeyMap;
       
    70 			}
       
    71 	};
       
    72 
       
    73 // --------------------------------------------------------------------------
       
    74 // CPbk2ContactEditorDlg::CPbk2ContactEditorDlg
       
    75 // --------------------------------------------------------------------------
       
    76 //
       
    77 CPbk2AdaptiveSearchGridFiller::CPbk2AdaptiveSearchGridFiller( CAknSearchField& aField, MPbk2ContactNameFormatter& aNameFormatter )
       
    78 	: CActive( CActive::EPriorityStandard ), iSearchField( aField ), iNameFormatter( aNameFormatter ),
       
    79 	iInvalidateAdaptiveSearchGrid( EFalse ),iSetFocusToSearchGrid( ETrue )
       
    80     {
       
    81 	CActiveScheduler::Add( this );
       
    82     }
       
    83 
       
    84 // --------------------------------------------------------------------------
       
    85 // CPbk2ContactEditorDlg::~CPbk2ContactEditorDlg
       
    86 // --------------------------------------------------------------------------
       
    87 //
       
    88 CPbk2AdaptiveSearchGridFiller::~CPbk2AdaptiveSearchGridFiller()
       
    89     {
       
    90     Cancel();
       
    91 	delete iKeyMap;
       
    92 	delete iCurrentGrid;
       
    93 	iAdaptiveGridCache.ResetAndDestroy();
       
    94 	delete iSearchString;
       
    95 	delete iFindUtil;
       
    96 	iDigraphContactsTitleArray.ResetAndDestroy();
       
    97     }
       
    98 
       
    99 
       
   100 // --------------------------------------------------------------------------
       
   101 // CPbk2ContactEditorDlg::NewL
       
   102 // --------------------------------------------------------------------------
       
   103 //
       
   104 CPbk2AdaptiveSearchGridFiller* CPbk2AdaptiveSearchGridFiller::NewL(  CAknSearchField& aField, MPbk2ContactNameFormatter& aNameFormatter )
       
   105     {
       
   106     CPbk2AdaptiveSearchGridFiller* self =
       
   107         new(ELeave) CPbk2AdaptiveSearchGridFiller( aField, aNameFormatter );
       
   108     CleanupStack::PushL(self);
       
   109     self->ConstructL();
       
   110     CleanupStack::Pop(self);
       
   111     return self;
       
   112     }
       
   113 
       
   114 
       
   115 // --------------------------------------------------------------------------
       
   116 // CPbk2ContactEditorDlg::ConstructL
       
   117 // --------------------------------------------------------------------------
       
   118 //
       
   119 void CPbk2AdaptiveSearchGridFiller::ConstructL()
       
   120     {
       
   121 	iKeyMap = HBufC::NewL( KAdaptiveSearchKeyMapGranularity );
       
   122 	iFindUtil = CFindUtil::NewL();
       
   123     }
       
   124 
       
   125 void CPbk2AdaptiveSearchGridFiller::StartFilling( const MVPbkContactViewBase& aView, const TDesC& aSearchString )
       
   126 	{
       
   127 	CPbk2AdaptiveGrid* keyMap = KeyMapFromCache( aSearchString );
       
   128 
       
   129 	if( keyMap )
       
   130 		{
       
   131 		iSearchField.SetAdaptiveGridChars( keyMap->KeyMap() );
       
   132 		return;
       
   133 		}
       
   134 
       
   135 	delete iSearchString;
       
   136 	iSearchString = NULL;
       
   137 
       
   138 	iSearchString = aSearchString.AllocL();
       
   139 
       
   140 	// If there is no search word, the user is not searching any contacts
       
   141 	// so we should reset the array to prepare for the next searching.
       
   142     if ( iSearchString->Length()== 0 )
       
   143     	{
       
   144     	iDigraphContactsTitleArray.ResetAndDestroy();
       
   145     	}
       
   146 	iView = &aView;
       
   147 
       
   148 	iKeyMap->Des().Zero();
       
   149 
       
   150 
       
   151 	iCounter = 0;
       
   152 
       
   153 	SetActive();
       
   154 	TRequestStatus* status = &iStatus;
       
   155 	User::RequestComplete( status, KErrNone );
       
   156 	}
       
   157 
       
   158 void CPbk2AdaptiveSearchGridFiller::StopFilling()
       
   159 	{
       
   160 	Cancel();
       
   161 	iView = NULL;
       
   162 	}
       
   163 
       
   164 void CPbk2AdaptiveSearchGridFiller::RunL()
       
   165 	{
       
   166 	if( !iView )
       
   167 		{
       
   168 		return;
       
   169 		}
       
   170 
       
   171 	TInt stopCount = iCounter + KAdaptiveSearchRefineStep;
       
   172 	const TInt itemCount = iView->ContactCountL();
       
   173 	TInt maxSpacesNumber = 0;
       
   174 
       
   175 	if( stopCount > itemCount )
       
   176 		{
       
   177 		stopCount = itemCount;
       
   178 		}
       
   179 
       
   180 	for( ; iCounter < stopCount; iCounter++ )
       
   181 		{
       
   182 		const MVPbkViewContact& contact = iView->ContactAtL( iCounter );
       
   183 		const TInt titleLength = iNameFormatter.MaxTitleLength( contact.Fields(), KContactFormattingFlags );
       
   184 		HBufC* title = HBufC::NewLC( titleLength );
       
   185 		TPtr ptrTitle = title->Des();
       
   186 		iNameFormatter.GetContactTitle( contact.Fields(), ptrTitle, KContactFormattingFlags );
       
   187 		BuildGridL( ptrTitle, *iSearchString, iKeyMap );
       
   188 		
       
   189 		// check number of spaces in the contact title
       
   190 		TInt numberOfSpaces = NumberOfSpacesInString( ptrTitle );
       
   191 		if ( numberOfSpaces > maxSpacesNumber )
       
   192 		    {
       
   193 		    maxSpacesNumber = numberOfSpaces;
       
   194 		    }
       
   195 		// Check if the contact's title include drgraphs,
       
   196 		// if it is, add it to array to save.
       
   197 		if ( IsDigraphContactsTitleL( ptrTitle ) )
       
   198 			{			
       
   199 			iDigraphContactsTitleArray.AppendL( title );
       
   200 			CleanupStack::Pop(); //title
       
   201 			}
       
   202 		else
       
   203 			{
       
   204 			CleanupStack::PopAndDestroy(); //title
       
   205 			}
       
   206 		}
       
   207 	// If there are titles in array, we should add them to build grids again,
       
   208 	// because the contacts include drgraphs will be filtered 
       
   209 	// when the application builds grids again.
       
   210     if ( iDigraphContactsTitleArray.Count()!= 0 )
       
   211     	{
       
   212     	for( TInt i(0); i < iDigraphContactsTitleArray.Count() ; i++ )
       
   213     		{
       
   214     		TPtr ptrContactsTitle = iDigraphContactsTitleArray[i]->Des();
       
   215         	BuildGridL( ptrContactsTitle, *iSearchString, iKeyMap );
       
   216     		}
       
   217 		}
       
   218 
       
   219 
       
   220 	if( stopCount == itemCount )
       
   221 		{
       
   222 		SetAdaptiveGridCharsL( maxSpacesNumber );
       
   223 		AddKeyMapToCacheL( *iSearchString, *iKeyMap );
       
   224 		}
       
   225 	else
       
   226 		{
       
   227 		//else continue
       
   228 		SetActive();
       
   229 		TRequestStatus* status = &iStatus;
       
   230 		User::RequestComplete( status, KErrNone );
       
   231 		}
       
   232 	}
       
   233 
       
   234 void CPbk2AdaptiveSearchGridFiller::DoCancel()
       
   235 	{
       
   236 	iView = NULL;
       
   237 	}
       
   238 
       
   239 TInt CPbk2AdaptiveSearchGridFiller::RunError( TInt /*aError*/ )
       
   240 	{
       
   241 	//ignore errors, nothing critical has happened, lets forget it
       
   242 	return KErrNone;
       
   243 	}
       
   244 
       
   245 CPbk2AdaptiveGrid* CPbk2AdaptiveSearchGridFiller::KeyMapFromCache( const TDesC& aFindText )
       
   246 	{
       
   247 	const TInt count = iAdaptiveGridCache.Count();
       
   248 
       
   249 	for( TInt i( 0 ); i < count; i++ )
       
   250 		{
       
   251 		if( !aFindText.Compare( iAdaptiveGridCache[i]->FindText() ) )
       
   252 			{
       
   253 			return iAdaptiveGridCache[i];
       
   254 			}
       
   255 		}
       
   256 
       
   257 	return NULL;
       
   258 	}
       
   259 
       
   260 void CPbk2AdaptiveSearchGridFiller::AddKeyMapToCacheL( const TDesC& aFindText, const TDesC& aKeyMap )
       
   261 	{
       
   262 	CPbk2AdaptiveGrid* keyMap = new( ELeave )CPbk2AdaptiveGrid;
       
   263 	CleanupStack::PushL( keyMap );
       
   264 	keyMap->SetKeyMapL( aFindText, aKeyMap );
       
   265 	iAdaptiveGridCache.InsertL( keyMap, 0 );
       
   266 	CleanupStack::Pop();//keyMap
       
   267 
       
   268 	if( iAdaptiveGridCache.Count() > KMaxAdaptiveGridCacheCount )
       
   269 		{
       
   270 		delete iAdaptiveGridCache[0];
       
   271 		iAdaptiveGridCache.Remove( 0 );
       
   272 		}
       
   273 	}
       
   274 
       
   275 void CPbk2AdaptiveSearchGridFiller::ClearCache()
       
   276 	{
       
   277 	iAdaptiveGridCache.ResetAndDestroy();
       
   278 	if ( iCurrentGrid )
       
   279 	    {
       
   280         delete iCurrentGrid;
       
   281         iCurrentGrid = NULL;
       
   282 	    }
       
   283 	}
       
   284 
       
   285 void CPbk2AdaptiveSearchGridFiller::InvalidateAdaptiveSearchGrid()
       
   286 	{
       
   287 	iInvalidateAdaptiveSearchGrid = ETrue;
       
   288 	}
       
   289 
       
   290 void CPbk2AdaptiveSearchGridFiller::SetFocusToAdaptiveSearchGrid()
       
   291     {
       
   292     iSetFocusToSearchGrid = ETrue;
       
   293     }
       
   294 
       
   295 void CPbk2AdaptiveSearchGridFiller::SetAdaptiveGridCharsL(  const TInt aMaxSpacesNumber )
       
   296 	{
       
   297 	TPtr ptr = iKeyMap->Des();
       
   298 
       
   299 	//To do upper case for all characters
       
   300 	ptr.UpperCase();
       
   301 	CDesCArray* array = new (ELeave) CDesCArrayFlat( KAdaptiveSearchKeyMapGranularity );
       
   302 	CleanupStack::PushL( array );
       
   303 	TInt length = ptr.Length();
       
   304 
       
   305 	for( TInt ii = 0; ii < length; ii++ )
       
   306 	    {
       
   307 	    array->AppendL( ptr.Mid( ii, 1 ) );
       
   308 	    }
       
   309 
       
   310 	// Alphabetical sort
       
   311 	array->Sort( ECmpCollated );
       
   312 	ptr.Zero();
       
   313 
       
   314     // Add space character only if user typed already some characters
       
   315     // in the find pane and more spaces can be found in contacts than
       
   316     // in the current search string.
       
   317     TInt searchTextLength = iSearchField.TextLength();
       
   318     HBufC* searchText = HBufC::NewL( searchTextLength );
       
   319 	TPtr ptrSearchText = searchText->Des();
       
   320 	iSearchField.GetSearchText( ptrSearchText );
       
   321     if ( searchTextLength > 0 
       
   322         && NumberOfSpacesInString( ptrSearchText ) < aMaxSpacesNumber  )
       
   323         {
       
   324         ptr.Append( TChar( ' ' ) );
       
   325         }
       
   326     delete searchText;
       
   327     searchText = NULL;
       
   328     
       
   329 	for( TInt ii = 0; ii < length; ii++ )
       
   330 	    {
       
   331 	    ptr.Append(array->MdcaPoint( ii ));
       
   332 	    }
       
   333 	CleanupStack::PopAndDestroy();//array
       
   334 
       
   335 	if( iCurrentGrid )
       
   336 		{
       
   337 		if( !iCurrentGrid->Des().Compare( *iKeyMap ) )
       
   338 			{
       
   339 			//same grid again
       
   340 			if( !iInvalidateAdaptiveSearchGrid )
       
   341 				{
       
   342 				//if grid hasn't been invalidated, we do not need to set it again
       
   343 				return;
       
   344 				}
       
   345 
       
   346 			}
       
   347 		}
       
   348 
       
   349 	iInvalidateAdaptiveSearchGrid = EFalse;
       
   350 
       
   351 	delete iCurrentGrid;
       
   352 	iCurrentGrid = NULL;
       
   353 	iCurrentGrid = iKeyMap->Des().AllocL();
       
   354 
       
   355 	iSearchField.SetAdaptiveGridChars( *iKeyMap );
       
   356 	if ( iSetFocusToSearchGrid )
       
   357 	    {
       
   358         // set the focus to findbox
       
   359 	    iSearchField.DrawDeferred();
       
   360 	    iSetFocusToSearchGrid = EFalse;
       
   361 	    }
       
   362 
       
   363 	}
       
   364 
       
   365 
       
   366 CDesC16Array* CPbk2AdaptiveSearchGridFiller::SplitContactFieldTextIntoArrayLC(
       
   367         const TDesC& aText )
       
   368     {
       
   369     const TInt KGranularity = 2;
       
   370     CDesCArrayFlat* array = new ( ELeave ) CDesCArrayFlat( KGranularity );
       
   371     CleanupStack::PushL( array );
       
   372     const TInt textLength = aText.Length();
       
   373     for ( TInt beg = 0; beg < textLength; ++beg )
       
   374         {
       
   375         // Skip separators before next word
       
   376         if ( !iNameFormatter.IsFindSeparatorChar( aText[beg] ) )
       
   377             {
       
   378             // Scan the end of the word
       
   379             TInt end = beg;
       
   380             for (; end < textLength &&
       
   381                    !iNameFormatter.IsFindSeparatorChar( aText[end] );
       
   382                 ++end )
       
   383                 {
       
   384                 }
       
   385             const TInt len = end - beg;
       
   386             // Append found word to the array
       
   387           	array->AppendL( aText.Mid( beg,len ) );
       
   388             // Scan for next word
       
   389             beg = end;
       
   390             }
       
   391         }
       
   392 
       
   393     if ( array->MdcaCount() == 0 && textLength > 0 )
       
   394         {
       
   395         // aText is all word separator characters
       
   396         // -> make a "word" out of those
       
   397         array->AppendL( aText );
       
   398         }
       
   399     return array;
       
   400     }
       
   401 
       
   402 
       
   403 
       
   404 void CPbk2AdaptiveSearchGridFiller::BuildGridL( const TDesC& aContactTitle, const TDesC& aSearchString, HBufC*& aKeyMap )
       
   405 	{
       
   406 	CDesC16Array* contactTitles = SplitContactFieldTextIntoArrayLC( aContactTitle );
       
   407 	CDesC16Array* searchWords = SplitContactFieldTextIntoArrayLC( aSearchString );
       
   408 
       
   409 	//in searchWords list, the last term is the only one which generates new keymap characters
       
   410 	//other ones are used only for matching
       
   411 
       
   412 	if( searchWords->MdcaCount() == 0 )
       
   413 		{
       
   414 		searchWords->AppendL( KNullDesC );
       
   415 		}
       
   416 
       
   417 	if( aSearchString.Length() > 0 )
       
   418 		{
       
   419 		if( aSearchString[ aSearchString.Length() - 1 ] == TChar( ' ' ) )
       
   420 			{
       
   421 			//because we now start new search term, we must add KNullDesC so we
       
   422 			//can find the matching new words
       
   423 			searchWords->AppendL( KNullDesC );
       
   424 			}
       
   425 		}
       
   426 
       
   427 
       
   428 	const TInt searchWordCount = searchWords->MdcaCount();
       
   429 
       
   430 	TBool contactMatch( searchWordCount == 1 );
       
   431 
       
   432 	for( TInt i( 0 ); i < searchWordCount; i++ )
       
   433 		{
       
   434 		TInt contactTitleCount = contactTitles->MdcaCount();
       
   435 
       
   436 		TBool contactTitleMatch( EFalse );
       
   437 
       
   438 		for( TInt j( 0 ); j < contactTitleCount; j++ )
       
   439 			{
       
   440 			TPtrC searchWord = searchWords->MdcaPoint( i );
       
   441 			TPtrC contactTitle = contactTitles->MdcaPoint( j );
       
   442 
       
   443 			const TBool lastSearchWord = ( i == searchWordCount - 1 );
       
   444 
       
   445 			TBool match( EFalse );
       
   446 
       
   447 			if( lastSearchWord )
       
   448 				{
       
   449 				if( !contactMatch )
       
   450 					{
       
   451 					//none of the previous words didin't match, so why this is not filtered?
       
   452 					//marked contact!?
       
   453 					}
       
   454 				else
       
   455 					{
       
   456 					match = iFindUtil->Interface()->MatchAdaptiveRefineL( contactTitle, searchWord, aKeyMap );
       
   457 					}
       
   458 				}
       
   459 			else
       
   460 				{
       
   461 				match = iFindUtil->Interface()->MatchRefineL( contactTitle, searchWord );
       
   462 				}
       
   463 
       
   464 			if( match )
       
   465 				{
       
   466 
       
   467 				if( !contactTitleMatch )
       
   468 					{
       
   469 					contactTitles->Delete( j );
       
   470 					//allow one search word to take away only one contactTitle
       
   471 					contactTitleMatch = ETrue;
       
   472 					//for loop must go from first contact title to last
       
   473 					//to be consistent with match functionality of VPbk.
       
   474 					j--; contactTitleCount--;
       
   475 					}
       
   476 				contactMatch = ETrue;
       
   477 				}
       
   478 			}
       
   479 
       
   480 		}
       
   481 
       
   482 	CleanupStack::PopAndDestroy( 2 );//contactTitle, searchWords
       
   483 	}
       
   484 
       
   485 TInt CPbk2AdaptiveSearchGridFiller::NumberOfSpacesInString(
       
   486     const TDesC& aSearchString )
       
   487     {
       
   488     TInt numberOfSpaces = 0;
       
   489 	TInt searchResult = 0;
       
   490 	TPtrC ptr = aSearchString;
       
   491 	while ( searchResult != KErrNotFound )
       
   492 	    {
       
   493 		searchResult = ptr.Locate( TChar( ' ' ) );
       
   494 		if ( searchResult != KErrNotFound )
       
   495 		    {
       
   496 		    numberOfSpaces++;
       
   497 		    ptr.Set( ptr.Right( ptr.Length() - searchResult - 1 ) );
       
   498 		    }
       
   499 		}
       
   500     return numberOfSpaces;
       
   501     }
       
   502 
       
   503 TBool CPbk2AdaptiveSearchGridFiller::IsDigraphContactsTitleL(const TDesC& aContactTitle)
       
   504 	{
       
   505 	TBool isDigraphic( EFalse );
       
   506 	// Go through the contactTitles one-by-one and check if they
       
   507 	// include digraphs
       
   508 	const TInt KDigraphLength(2);
       
   509 	if ( aContactTitle.Length()>= KDigraphLength )
       
   510 		{
       
   511 		TPtrC substring = aContactTitle.Left(1);
       
   512 		if( !iFindUtil->Interface()->MatchRefineL( aContactTitle, substring ) )
       
   513 			{
       
   514 			// The substring did not match the characters of the contactTitles
       
   515 			// For example with Croatian locale the contactTitles "nj" does
       
   516 			// not include the substring "n" because "nj" is a digraph
       
   517 			isDigraphic = ETrue;
       
   518 			}
       
   519 		}
       
   520 	return isDigraphic;
       
   521 	}
       
   522 // End of File