phonebookui/Phonebook2/UIControls/src/CPbk2AdaptiveSearchGridFiller.cpp
branchRCL_3
changeset 20 f4a778e096c2
child 21 9da50d567e3c
equal deleted inserted replaced
19:5b6f26637ad3 20:f4a778e096c2
       
     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 #include "MPbk2FilteredViewStack.h"
       
    25 
       
    26 #include <MVPbkBaseContactFieldCollection.h>
       
    27 #include <MVPbkContactFieldTextData.h>
       
    28 #include <MVPbkBaseContactField.h>
       
    29 
       
    30 
       
    31 #include <MPbk2ContactViewSupplier.h>
       
    32 #include <MPbk2ApplicationServices.h>
       
    33 #include <MPbk2AppUi.h>
       
    34 #include <CPbk2StoreConfiguration.h>
       
    35 #include <MPbk2ContactNameFormatter2.h>
       
    36 #include <FindUtil.h>
       
    37 #include <badesca.h>
       
    38 #include <featmgr.h>
       
    39 
       
    40 #include <CPsRequestHandler.h>
       
    41 
       
    42 const TInt KMaxAdaptiveGridCacheCount = 10;
       
    43 const TInt KAdaptiveSearchKeyMapGranularity = 100;
       
    44 const TInt KAdaptiveSearchRefineStep = 25;
       
    45 const TInt KContactFormattingFlags = MPbk2ContactNameFormatter::EPreserveLeadingSpaces |
       
    46             MPbk2ContactNameFormatter::EReplaceNonGraphicChars |
       
    47             MPbk2ContactNameFormatter::EDisableCompanyNameSeparator;
       
    48 
       
    49 namespace {
       
    50 enum TNameOrder
       
    51     {
       
    52     ETopContactOrderNumber = 0,     //TC control data, not shown
       
    53     ENameFirstPart,                 //Contact name data
       
    54     ENameSecondPart,                //Contact name data
       
    55     ENameCompanyPart                //to support Company name
       
    56     };
       
    57 } // namespace
       
    58 
       
    59 NONSHARABLE_CLASS(CPbk2AdaptiveGrid) : public CBase
       
    60 	{
       
    61 	HBufC* iFindText;
       
    62 	HBufC* iKeyMap;
       
    63 
       
    64 	public:
       
    65 
       
    66 		CPbk2AdaptiveGrid()
       
    67 			{
       
    68 			}
       
    69 
       
    70 		~CPbk2AdaptiveGrid()
       
    71 			{
       
    72 			delete iFindText;
       
    73 			delete iKeyMap;
       
    74 			}
       
    75 
       
    76 		void SetKeyMapL( const TDesC& aFindText, const TDesC& aKeyMap )
       
    77 			{
       
    78 			delete iFindText;
       
    79 			delete iKeyMap;
       
    80 
       
    81 			iFindText = iKeyMap = NULL;
       
    82 
       
    83 			iFindText = aFindText.AllocL();
       
    84 			iKeyMap = aKeyMap.AllocL();
       
    85 			}
       
    86 
       
    87 		const TDesC& GetFindText() const
       
    88 			{
       
    89 			return *iFindText;
       
    90 			}
       
    91 
       
    92 		const TDesC& GetKeyMap() const
       
    93 			{
       
    94 			return *iKeyMap;
       
    95 			}
       
    96 	};
       
    97 
       
    98 // --------------------------------------------------------------------------
       
    99 // CPbk2ContactEditorDlg::CPbk2ContactEditorDlg
       
   100 // --------------------------------------------------------------------------
       
   101 //
       
   102 CPbk2AdaptiveSearchGridFiller::CPbk2AdaptiveSearchGridFiller( CAknSearchField& aField, MPbk2ContactNameFormatter& aNameFormatter )
       
   103 	: CActive( CActive::EPriorityIdle ), iSearchField( aField ), iNameFormatter( aNameFormatter ),
       
   104 	iInvalidateAdaptiveSearchGrid( EFalse ),iSetFocusToSearchGrid( ETrue )
       
   105     {
       
   106 	CActiveScheduler::Add( this );
       
   107     }
       
   108 
       
   109 // --------------------------------------------------------------------------
       
   110 // CPbk2ContactEditorDlg::~CPbk2ContactEditorDlg
       
   111 // --------------------------------------------------------------------------
       
   112 //
       
   113 CPbk2AdaptiveSearchGridFiller::~CPbk2AdaptiveSearchGridFiller()
       
   114     {
       
   115     Cancel();
       
   116     if ( iPsHandler )
       
   117         {
       
   118         iPsHandler->RemoveObserver( this );
       
   119         delete iPsHandler;
       
   120         }
       
   121 	delete iKeyMap;
       
   122 	delete iCurrentGrid;
       
   123 	iAdaptiveGridCache.ResetAndDestroy();
       
   124 	delete iSearchString;
       
   125 	delete iFindUtil;
       
   126 	iDigraphContactsTitleArray.ResetAndDestroy();
       
   127     }
       
   128 
       
   129 
       
   130 // --------------------------------------------------------------------------
       
   131 // CPbk2ContactEditorDlg::NewL
       
   132 // --------------------------------------------------------------------------
       
   133 //
       
   134 CPbk2AdaptiveSearchGridFiller* CPbk2AdaptiveSearchGridFiller::NewL(  CAknSearchField& aField, MPbk2ContactNameFormatter& aNameFormatter )
       
   135     {
       
   136     CPbk2AdaptiveSearchGridFiller* self =
       
   137         new(ELeave) CPbk2AdaptiveSearchGridFiller( aField, aNameFormatter );
       
   138     CleanupStack::PushL(self);
       
   139     self->ConstructL();
       
   140     CleanupStack::Pop(self);
       
   141     return self;
       
   142     }
       
   143 
       
   144 
       
   145 // --------------------------------------------------------------------------
       
   146 // CPbk2ContactEditorDlg::ConstructL
       
   147 // --------------------------------------------------------------------------
       
   148 //
       
   149 void CPbk2AdaptiveSearchGridFiller::ConstructL()
       
   150     {
       
   151 	iKeyMap = HBufC::NewL( KAdaptiveSearchKeyMapGranularity );
       
   152 	iFindUtil = CFindUtil::NewL();
       
   153     // UI Language
       
   154 	TLanguage uiLanguage = User::Language();
       
   155 	if ( uiLanguage != ELangJapanese && uiLanguage != ELangPrcChinese && 
       
   156 	        uiLanguage != ELangHongKongChinese && uiLanguage != ELangTaiwanChinese &&
       
   157 	        uiLanguage != ELangKorean )
       
   158 	    {  
       
   159         iPsHandler = CPSRequestHandler::NewL();
       
   160         iPsHandler->AddObserverL( this );
       
   161 	    }
       
   162     }
       
   163 
       
   164 void CPbk2AdaptiveSearchGridFiller::StartFillingL( const MVPbkContactViewBase& aView,
       
   165         const TDesC& aSearchString, TBool aClearCache )
       
   166 	{
       
   167 	
       
   168     if( aClearCache )
       
   169         {
       
   170         ClearCache();
       
   171         }
       
   172     
       
   173 	if ( IsActive() && iView == &aView && iViewItemCount == aView.ContactCountL() 
       
   174 	        && iSearchString && !iSearchString->Compare( aSearchString ) )
       
   175 	    {
       
   176 	    return;
       
   177 	    }
       
   178 	else
       
   179 	    {
       
   180 	    StopFilling();
       
   181 	    }
       
   182 
       
   183     CPbk2AdaptiveGrid* keyMap = KeyMapFromCache( aSearchString );
       
   184     
       
   185     if( keyMap )
       
   186         {
       
   187         iSearchField.SetAdaptiveGridChars( keyMap->GetKeyMap() );
       
   188         return;
       
   189         }
       
   190     
       
   191 	iViewItemCount = aView.ContactCountL();
       
   192 	delete iSearchString;
       
   193 	iSearchString = NULL;
       
   194 
       
   195 	iSearchString = aSearchString.AllocL();
       
   196 
       
   197 	// If there is no search word, the user is not searching any contacts
       
   198 	// so we should reset the array to prepare for the next searching.
       
   199     if ( iSearchString->Length() == 0 )
       
   200     	{
       
   201     	iDigraphContactsTitleArray.ResetAndDestroy();
       
   202     	}
       
   203 	iView = &aView;
       
   204 
       
   205 	iKeyMap->Des().Zero();
       
   206 
       
   207 	iCounter = 0;
       
   208 
       
   209 	if ( iSearchString->Length() <= KPsAdaptiveGridSupportedMaxLen && GridFromPsEngineL( aView ) )
       
   210 	    {
       
   211 	    return;
       
   212 	    }
       
   213 	
       
   214 	SetActive();
       
   215 	TRequestStatus* status = &iStatus;
       
   216 	User::RequestComplete( status, KErrNone );
       
   217 	}
       
   218 
       
   219 void CPbk2AdaptiveSearchGridFiller::StopFilling()
       
   220 	{
       
   221 	Cancel();
       
   222 	iView = NULL;
       
   223 	}
       
   224 
       
   225 void CPbk2AdaptiveSearchGridFiller::RunL()
       
   226 	{
       
   227 	if( !iView )
       
   228 		{
       
   229 		return;
       
   230 		}
       
   231 
       
   232 	TInt stopCount = iCounter + KAdaptiveSearchRefineStep;
       
   233 	const TInt itemCount = iView->ContactCountL();
       
   234     
       
   235 	if( stopCount > itemCount )
       
   236 		{
       
   237 		stopCount = itemCount;
       
   238 		}
       
   239 
       
   240 	TInt maxSpacesNumber = 0;
       
   241 
       
   242     CDesC16Array* searchWordsArray = SplitSearchFieldTextIntoArrayLC( *iSearchString );
       
   243     TInt searchStringSpacesNumber = searchWordsArray->MdcaCount() - 1;
       
   244     
       
   245 	for( ; iCounter < stopCount; iCounter++ )
       
   246 		{
       
   247 		const MVPbkViewContact& contact = iView->ContactAtL( iCounter );
       
   248 		const TInt titleLength = iNameFormatter.MaxTitleLength( contact.Fields(), KContactFormattingFlags );
       
   249 		HBufC* title = NULL;
       
   250 		
       
   251 		if( FeatureManager::FeatureSupported( KFeatureIdFfContactsCompanyNames ) )
       
   252             {
       
   253             MPbk2ContactNameFormatter2* nameformatterExtension =
       
   254                     reinterpret_cast<MPbk2ContactNameFormatter2*>( iNameFormatter.
       
   255                     ContactNameFormatterExtension( MPbk2ContactNameFormatterExtension2Uid ) );
       
   256             if ( nameformatterExtension && titleLength )
       
   257                 {
       
   258                 title = nameformatterExtension->GetContactTitleWithCompanyNameL( contact.Fields(),
       
   259                     KContactFormattingFlags );
       
   260                 
       
   261                 }
       
   262             }
       
   263         else if ( titleLength )
       
   264             {
       
   265             title = iNameFormatter.GetContactTitleL( contact.Fields(), KContactFormattingFlags );
       
   266             
       
   267             // In FDN, the number will be displayed in the list if the contact is no name.
       
   268             // If it is, set the search string as NULL.
       
   269             if ( IsActualTitleEmpty( contact ) )    
       
   270                 {
       
   271                 delete title;
       
   272                 title = NULL;
       
   273             	}
       
   274             }
       
   275 		
       
   276 		if ( !title )
       
   277 		    {
       
   278 		    title = HBufC::NewL( titleLength );
       
   279 		    }
       
   280 
       
   281         CleanupStack::PushL( title );
       
   282 		BuildGridL( *title, searchWordsArray, iKeyMap );
       
   283 		
       
   284 		// check number of spaces in the contact title
       
   285 		TInt numberOfSpaces = NumberOfSpacesInString( *title );
       
   286 		if ( numberOfSpaces > maxSpacesNumber )
       
   287 		    {
       
   288 		    maxSpacesNumber = numberOfSpaces;
       
   289 		    }
       
   290 		// Check if the contact's title include drgraphs,
       
   291 		// if it is, add it to array to save.
       
   292 		if ( IsDigraphContactsTitleL( *title ) )
       
   293 			{			
       
   294 			iDigraphContactsTitleArray.AppendL( title );
       
   295 			CleanupStack::Pop(); //title
       
   296 			}
       
   297 		else
       
   298 			{
       
   299 			CleanupStack::PopAndDestroy( title );
       
   300 			}
       
   301 		}
       
   302 	// If there are titles in array, we should add them to build grids again,
       
   303 	// because the contacts include drgraphs will be filtered 
       
   304 	// when the application builds grids again.
       
   305     if ( iDigraphContactsTitleArray.Count()!= 0 )
       
   306     	{
       
   307     	for( TInt i(0); i < iDigraphContactsTitleArray.Count() ; i++ )
       
   308     		{
       
   309     		TPtr ptrContactsTitle = iDigraphContactsTitleArray[i]->Des();
       
   310         	BuildGridL( ptrContactsTitle, searchWordsArray, iKeyMap );
       
   311     		}
       
   312 		}
       
   313 
       
   314     CleanupStack::PopAndDestroy( searchWordsArray ); //searchWordsArray
       
   315 
       
   316 	if( stopCount == itemCount )
       
   317 		{
       
   318 		SetAdaptiveGridCharsL( maxSpacesNumber, searchStringSpacesNumber );
       
   319 		AddKeyMapToCacheL( *iSearchString, *iKeyMap );
       
   320 		}
       
   321 	else
       
   322 		{
       
   323 		//else continue
       
   324 		SetActive();
       
   325 		TRequestStatus* status = &iStatus;
       
   326 		User::RequestComplete( status, KErrNone );
       
   327 		}
       
   328 	}
       
   329 
       
   330 void CPbk2AdaptiveSearchGridFiller::DoCancel()
       
   331 	{
       
   332 	iView = NULL;
       
   333 	}
       
   334 
       
   335 TInt CPbk2AdaptiveSearchGridFiller::RunError( TInt /*aError*/ )
       
   336 	{
       
   337 	//ignore errors, nothing critical has happened, lets forget it
       
   338 	return KErrNone;
       
   339 	}
       
   340 
       
   341 CPbk2AdaptiveGrid* CPbk2AdaptiveSearchGridFiller::KeyMapFromCache( const TDesC& aFindText )
       
   342 	{
       
   343 	const TInt count = iAdaptiveGridCache.Count();
       
   344 
       
   345 	for( TInt i( 0 ); i < count; i++ )
       
   346 		{
       
   347 		if( iAdaptiveGridCache[i] != NULL
       
   348 		    && !aFindText.Compare( iAdaptiveGridCache[i]->GetFindText() ) )
       
   349 			{
       
   350 			return iAdaptiveGridCache[i];
       
   351 			}
       
   352 		}
       
   353 
       
   354 	return NULL;
       
   355 	}
       
   356 
       
   357 void CPbk2AdaptiveSearchGridFiller::AddKeyMapToCacheL( const TDesC& aFindText, const TDesC& aKeyMap )
       
   358 	{
       
   359     CPbk2AdaptiveGrid* keyMap = new( ELeave )CPbk2AdaptiveGrid;
       
   360     CleanupStack::PushL( keyMap );
       
   361     keyMap->SetKeyMapL( aFindText, aKeyMap );
       
   362 
       
   363     // Keep always in the cache at position 0 the grid cache element for empty find box,
       
   364     // which is the one that requires more time to be built
       
   365     if ( aFindText.Length() == 0 )
       
   366         {
       
   367         if ( iAdaptiveGridCache.Count() > 0 )
       
   368             {
       
   369             delete iAdaptiveGridCache[0];
       
   370             iAdaptiveGridCache.Remove( 0 );
       
   371             }
       
   372 
       
   373         iAdaptiveGridCache.InsertL( keyMap, 0 );
       
   374         }
       
   375     else
       
   376         {
       
   377         if ( iAdaptiveGridCache.Count() == 0 )
       
   378         {
       
   379         iAdaptiveGridCache.InsertL( NULL, 0 );
       
   380         }
       
   381 
       
   382         iAdaptiveGridCache.InsertL( keyMap, 1 );
       
   383     
       
   384         // Delete oldest cache element
       
   385         if( iAdaptiveGridCache.Count() > KMaxAdaptiveGridCacheCount )
       
   386             {
       
   387             delete iAdaptiveGridCache[KMaxAdaptiveGridCacheCount];
       
   388             iAdaptiveGridCache.Remove( KMaxAdaptiveGridCacheCount );
       
   389             }
       
   390         }
       
   391 
       
   392     CleanupStack::Pop(); //keyMap
       
   393 	}
       
   394 
       
   395 void CPbk2AdaptiveSearchGridFiller::ClearCache()
       
   396 	{
       
   397 	iAdaptiveGridCache.ResetAndDestroy();
       
   398 	if ( iCurrentGrid )
       
   399 	    {
       
   400         delete iCurrentGrid;
       
   401         iCurrentGrid = NULL;
       
   402 	    }
       
   403 	}
       
   404 
       
   405 void CPbk2AdaptiveSearchGridFiller::InvalidateAdaptiveSearchGrid()
       
   406 	{
       
   407 	iInvalidateAdaptiveSearchGrid = ETrue;
       
   408 	}
       
   409 
       
   410 void CPbk2AdaptiveSearchGridFiller::SetFocusToAdaptiveSearchGrid()
       
   411     {
       
   412     iSetFocusToSearchGrid = ETrue;
       
   413     }
       
   414 
       
   415 void CPbk2AdaptiveSearchGridFiller::SetAdaptiveGridCharsL(
       
   416         const TInt aMaxSpacesNumber, const TInt aSearchStringSpacesNumber )
       
   417 	{
       
   418 	TPtr ptr = iKeyMap->Des();
       
   419 
       
   420 	// Do upper case for all characters
       
   421 	ptr.UpperCase();
       
   422 	CDesCArray* array = new (ELeave) CDesCArrayFlat( KAdaptiveSearchKeyMapGranularity );
       
   423 	CleanupStack::PushL( array );
       
   424 	TInt length = ptr.Length();
       
   425 
       
   426 	for( TInt ii = 0; ii < length; ii++ )
       
   427 	    {
       
   428 	    array->AppendL( ptr.Mid( ii, 1 ) );
       
   429 	    }
       
   430 
       
   431 	// Alphabetical sort
       
   432 	array->Sort( ECmpCollated );
       
   433 	ptr.Zero();
       
   434 
       
   435     // Add space character only if:
       
   436 	// - user typed already some characters in the find pane,
       
   437 	// - and more spaces can be found in contacts than in the current search string,
       
   438 	// - and space is not the last character in the search string.
       
   439     if ( iSearchString->Length() > 0 
       
   440          && aMaxSpacesNumber > aSearchStringSpacesNumber
       
   441          && (*iSearchString)[iSearchString->Length() - 1] != TChar( ' ' ) )
       
   442         {
       
   443         ptr.Append( TChar( ' ' ) );
       
   444         }
       
   445      
       
   446 	for( TInt ii = 0; ii < length; ii++ )
       
   447 	    {
       
   448 	    ptr.Append(array->MdcaPoint( ii ));
       
   449 	    }
       
   450 	CleanupStack::PopAndDestroy();//array
       
   451 
       
   452 	if( iCurrentGrid )
       
   453 		{
       
   454 		if( !iCurrentGrid->Des().Compare( *iKeyMap ) )
       
   455 			{
       
   456 			//same grid again
       
   457 			if( !iInvalidateAdaptiveSearchGrid )
       
   458 				{
       
   459 				//if grid hasn't been invalidated, we do not need to set it again
       
   460 				return;
       
   461 				}
       
   462 			}
       
   463 		}
       
   464 
       
   465 	delete iCurrentGrid;
       
   466 	iCurrentGrid = NULL;
       
   467 	iCurrentGrid = iKeyMap->Des().AllocL();
       
   468 
       
   469 	iSearchField.SetAdaptiveGridChars( *iKeyMap );
       
   470     
       
   471     iInvalidateAdaptiveSearchGrid = EFalse;
       
   472     
       
   473 	if ( iSetFocusToSearchGrid )
       
   474 	    {
       
   475         // set the focus to findbox
       
   476 	    iSearchField.DrawDeferred();
       
   477 	    iSetFocusToSearchGrid = EFalse;
       
   478 	    }
       
   479 
       
   480 	}
       
   481 
       
   482 
       
   483 CDesC16Array* CPbk2AdaptiveSearchGridFiller::SplitContactFieldTextIntoArrayLC(
       
   484         const TDesC& aText )
       
   485     {
       
   486     // Attempt to minimize the allocations considering 3 words for the search fields:
       
   487     // FirstName, LastName, CompanyName.
       
   488     const TInt KGranularity = 2; // Attempt to minimize the allocations
       
   489 
       
   490     CDesCArrayFlat* array = new ( ELeave ) CDesCArrayFlat( KGranularity );
       
   491     CleanupStack::PushL( array );
       
   492     const TInt textLength = aText.Length();
       
   493     for ( TInt beg = 0; beg < textLength; beg++ )
       
   494         {
       
   495         // Skip separators before next word
       
   496         if ( iNameFormatter.IsFindSeparatorChar( aText[beg] ) )
       
   497             {
       
   498             continue;
       
   499             }
       
   500         
       
   501         // Scan till the end of the word
       
   502         TInt end;
       
   503         for ( end = beg + 1;
       
   504               end < textLength && !iNameFormatter.IsFindSeparatorChar( aText[end] );
       
   505               ++end )
       
   506             {
       
   507             }
       
   508 
       
   509         // Append found word to the array
       
   510         array->AppendL( aText.Mid( beg, end - beg) );
       
   511 
       
   512         // Scan for next word
       
   513         beg = end;
       
   514         }
       
   515 
       
   516     return array;
       
   517     }
       
   518 
       
   519 CDesC16Array* CPbk2AdaptiveSearchGridFiller::SplitSearchFieldTextIntoArrayLC(
       
   520         const TDesC& aText )
       
   521     {
       
   522     CDesC16Array* searchWordsArray = SplitContactFieldTextIntoArrayLC( aText );
       
   523 
       
   524     // In searchWordsArray, the last word is the only one which generates new keymap characters
       
   525     // for the grid; the other words are used only for matching the contact words.
       
   526     //
       
   527     // KNullDesC fake word as last word in search string will match all contact words so that all
       
   528     // initials of contact words will be put into the grid.
       
   529     // We do this in case the search string is empty or the last character is a space separator.
       
   530     
       
   531     if( searchWordsArray->MdcaCount() == 0 ||
       
   532         ( aText.Length() > 0 && aText[aText.Length() - 1] == TChar(' ') ) )
       
   533         {
       
   534         searchWordsArray->AppendL( KNullDesC );
       
   535         }
       
   536 
       
   537     return searchWordsArray;
       
   538     }
       
   539 
       
   540 void CPbk2AdaptiveSearchGridFiller::BuildGridL( const TDesC& aContactString, const CDesC16Array* aSearchWords, HBufC*& aKeyMap )
       
   541 	{
       
   542     CDesC16Array* contactWords = SplitContactFieldTextIntoArrayLC( aContactString );
       
   543 	
       
   544     const TInt contactWordCount = contactWords->MdcaCount();
       
   545 	const TInt searchWordCount = aSearchWords->MdcaCount();
       
   546 
       
   547     TPtrC searchWord;
       
   548     TPtrC contactWord;
       
   549 
       
   550     // Try to make as fast algorithm as possible if there is only one search word,
       
   551     // which is the most common case    
       
   552     if ( searchWordCount == 1 )
       
   553         {
       
   554         searchWord.Set( aSearchWords->MdcaPoint( 0 ) ); // Search word
       
   555 
       
   556         for( TInt j = 0; j < contactWordCount; j++ )
       
   557             {
       
   558             contactWord.Set( contactWords->MdcaPoint( j ) );
       
   559     
       
   560             iFindUtil->Interface()->MatchAdaptiveRefineL( contactWord, searchWord, aKeyMap );
       
   561             }
       
   562         }
       
   563     
       
   564     // The clients of this method will provide at least one search word, so 0 is unexpected
       
   565     else if ( searchWordCount > 1 )
       
   566         {
       
   567         RArray<TBool> contactWordsMatchedArray;
       
   568         contactWordsMatchedArray.ReserveL( contactWordCount );
       
   569         for ( TInt i = 0; i < contactWordCount; ++i )
       
   570             {
       
   571             contactWordsMatchedArray.AppendL( EFalse );
       
   572             }
       
   573 
       
   574         TBool matched = ETrue;
       
   575 
       
   576         // Scan search words except for the last one
       
   577         for ( TInt i = 0; matched && i < searchWordCount - 1; i++ )
       
   578             {
       
   579             searchWord.Set( aSearchWords->MdcaPoint( i ) );
       
   580 
       
   581             matched = EFalse; // Search word not matched yet
       
   582             
       
   583             // Check if the search word is matched in the contact
       
   584             for( TInt j = 0; !matched && j < contactWordCount; j++ )
       
   585                 {
       
   586                 contactWord.Set( contactWords->MdcaPoint( j ) );
       
   587     
       
   588                 // Partially or fully matched
       
   589                 if ( iFindUtil->Interface()->MatchRefineL( contactWord, searchWord ) )
       
   590                     {
       
   591                     // Allow one search word to match only one contact word.
       
   592                     // This could be done better if both search and grid creation would
       
   593                     // work in the same way for contacts matching...
       
   594                     // Example: Contact: "Dim Din Dit"
       
   595                     //          Search:  "DIN DI"
       
   596                     //          - DIN is matched fully
       
   597                     //          - DI is matched partially and assigned to "Dim"
       
   598                     //          - The grid will show "_T" instead of "_MT"
       
   599                     contactWordsMatchedArray[j] = ETrue;    
       
   600                     matched = ETrue;
       
   601                     }
       
   602                 }
       
   603             }
       
   604 
       
   605         // If all search words before the last one matched (fully or partially),
       
   606         // add characters to the grid using last search word.
       
   607         if ( matched )
       
   608             {
       
   609             searchWord.Set( aSearchWords->MdcaPoint( searchWordCount - 1 ) ); // Last search word
       
   610     
       
   611             for( TInt j = 0; j < contactWordCount; j++ )
       
   612                 {
       
   613                 // skip Contact words matched by previous search words
       
   614                 if (contactWordsMatchedArray[j])
       
   615                     {
       
   616                     continue;
       
   617                     }
       
   618     
       
   619                 contactWord.Set( contactWords->MdcaPoint( j ) );
       
   620     
       
   621                 iFindUtil->Interface()->MatchAdaptiveRefineL( contactWord, searchWord, aKeyMap );
       
   622                 }
       
   623             }
       
   624     
       
   625         contactWordsMatchedArray.Close();
       
   626         }
       
   627         
       
   628 	CleanupStack::PopAndDestroy( contactWords );
       
   629 	}
       
   630 
       
   631 TInt CPbk2AdaptiveSearchGridFiller::NumberOfSpacesInString(
       
   632     const TDesC& aSearchString )
       
   633     {
       
   634     TInt numberOfSpaces = 0;
       
   635 	TInt searchResult = 0;
       
   636 	TPtrC ptr = aSearchString;
       
   637 	while ( searchResult != KErrNotFound )
       
   638 	    {
       
   639 		searchResult = ptr.Locate( TChar( ' ' ) );
       
   640 		if ( searchResult != KErrNotFound )
       
   641 		    {
       
   642 		    numberOfSpaces++;
       
   643 		    ptr.Set( ptr.Right( ptr.Length() - searchResult - 1 ) );
       
   644 		    }
       
   645 		}
       
   646     return numberOfSpaces;
       
   647     }
       
   648 
       
   649 TBool CPbk2AdaptiveSearchGridFiller::IsDigraphContactsTitleL(const TDesC& aContactTitle)
       
   650 	{
       
   651 	TBool isDigraphic( EFalse );
       
   652 	// Go through the contactTitles one-by-one and check if they
       
   653 	// include digraphs
       
   654 	const TInt KDigraphLength(2);
       
   655 	if ( aContactTitle.Length()>= KDigraphLength )
       
   656 		{
       
   657 		TPtrC substring = aContactTitle.Left(1);
       
   658 		if( !iFindUtil->Interface()->MatchRefineL( aContactTitle, substring ) )
       
   659 			{
       
   660 			// The substring did not match the characters of the contactTitles
       
   661 			// For example with Croatian locale the contactTitles "nj" does
       
   662 			// not include the substring "n" because "nj" is a digraph
       
   663 			isDigraphic = ETrue;
       
   664 			}
       
   665 		}
       
   666 	return isDigraphic;
       
   667 	}
       
   668 
       
   669 void CPbk2AdaptiveSearchGridFiller::HandlePsResultsUpdate(
       
   670         RPointerArray<CPsClientData>& /*searchResults*/,
       
   671         RPointerArray<CPsPattern>& /*searchSeqs*/ )
       
   672     {
       
   673     
       
   674     }
       
   675 
       
   676 void CPbk2AdaptiveSearchGridFiller::HandlePsError( TInt /*aErrorCode*/ )
       
   677     {
       
   678     
       
   679     }
       
   680 
       
   681 void CPbk2AdaptiveSearchGridFiller::CachingStatus( TCachingStatus& aStatus, TInt& /*aError*/ )
       
   682     {
       
   683     TRAP_IGNORE(
       
   684     MVPbkContactViewBase* allContactsView = Phonebook2::Pbk2AppUi()->ApplicationServices().ViewSupplier().AllContactsViewL();
       
   685     
       
   686     const MPbk2FilteredViewStack* filteredView = dynamic_cast<const MPbk2FilteredViewStack*> ( iView );
       
   687     
       
   688     if ( aStatus >= ECachingComplete && filteredView && filteredView->Level() == 0 && &filteredView->BaseView() == allContactsView )
       
   689         {
       
   690         HBufC* string = iSearchString->AllocL();
       
   691         CleanupStack::PushL( string );
       
   692         StartFillingL( *iView, *string, ETrue );
       
   693         CleanupStack::PopAndDestroy( string  );
       
   694         }
       
   695         );
       
   696     }
       
   697 
       
   698 TBool CPbk2AdaptiveSearchGridFiller::GridFromPsEngineL( const MVPbkContactViewBase& aView )
       
   699     {
       
   700     if ( iPsHandler == NULL )
       
   701         {
       
   702         return EFalse;
       
   703         }
       
   704     MPbk2ApplicationServices& appServices = Phonebook2::Pbk2AppUi()->ApplicationServices();
       
   705     MVPbkContactViewBase* allContactsView = appServices.ViewSupplier().AllContactsViewL();
       
   706     const MPbk2FilteredViewStack* filteredView = dynamic_cast<const MPbk2FilteredViewStack*> ( &aView );
       
   707         
       
   708     if ( filteredView && filteredView->Level() == 0 && &filteredView->BaseView() == allContactsView )
       
   709         {
       
   710         CPbk2StoreConfiguration& config = appServices.StoreConfiguration();
       
   711         CVPbkContactStoreUriArray* stores = NULL;
       
   712         stores = config.CurrentConfigurationL();
       
   713         if ( !stores || stores->Count() == 0 )
       
   714             {
       
   715             delete stores;
       
   716             return EFalse;
       
   717             }
       
   718         
       
   719         TInt count = stores->Count();
       
   720         CleanupStack::PushL(stores);
       
   721         
       
   722         CDesCArrayFlat* array = new ( ELeave ) CDesCArrayFlat( count );
       
   723         CleanupStack::PushL( array );
       
   724         
       
   725         for ( TInt i = 0; i < count; ++i)
       
   726             {
       
   727             TVPbkContactStoreUriPtr uriPtr = stores->operator[](i);
       
   728             array->AppendL( uriPtr.UriDes() );
       
   729             }
       
   730 
       
   731         TBool companyName = EFalse;
       
   732         TBuf<KPsAdaptiveGridStringMaxLen> gridChars;
       
   733         if( FeatureManager::FeatureSupported( KFeatureIdFfContactsCompanyNames ) )
       
   734             {
       
   735             companyName = ETrue;
       
   736             }
       
   737         iPsHandler->GetAdaptiveGridCharactersL( *array, *iSearchString, companyName, gridChars );
       
   738         
       
   739         CleanupStack::PopAndDestroy( array );
       
   740         CleanupStack::PopAndDestroy( stores );
       
   741         
       
   742         if ( !gridChars.Length() && iViewItemCount > 0 )
       
   743             {
       
   744             // grid should be created on standard way
       
   745             return EFalse;
       
   746             }
       
   747         if ( iKeyMap->Des().MaxLength() < gridChars.Length() )
       
   748             {
       
   749             iKeyMap = iKeyMap->ReAllocL( gridChars.Length() );
       
   750             }
       
   751         iKeyMap->Des().Copy( gridChars );
       
   752         
       
   753         delete iCurrentGrid;
       
   754         iCurrentGrid = NULL;
       
   755         iCurrentGrid = iKeyMap->Des().AllocL();
       
   756 
       
   757         iSearchField.SetAdaptiveGridChars( *iKeyMap );
       
   758         
       
   759         iInvalidateAdaptiveSearchGrid = EFalse;
       
   760         
       
   761         if ( iSetFocusToSearchGrid )
       
   762             {
       
   763             // set the focus to findbox
       
   764             iSearchField.DrawDeferred();
       
   765             iSetFocusToSearchGrid = EFalse;
       
   766             }
       
   767         AddKeyMapToCacheL( *iSearchString, *iKeyMap );
       
   768         return ETrue;
       
   769         }
       
   770     else
       
   771         {
       
   772         return EFalse;
       
   773         }
       
   774     }
       
   775 
       
   776 TBool CPbk2AdaptiveSearchGridFiller::IsActualTitleEmpty( const MVPbkViewContact& aContact )
       
   777     {
       
   778     TBool result = ETrue;
       
   779     const TInt fieldCount = aContact.Fields().FieldCount();
       
   780     if ( fieldCount > ENameCompanyPart )
       
   781         {
       
   782         const MVPbkBaseContactField& field = aContact.Fields().FieldAt( ENameCompanyPart );
       
   783         if ( iNameFormatter.IsTitleField( field ) )
       
   784             {
       
   785             return EFalse;
       
   786             }
       
   787         }
       
   788    
       
   789     if ( fieldCount > ENameFirstPart ) 
       
   790         {
       
   791         const MVPbkBaseContactField& field = aContact.Fields().FieldAt( ENameFirstPart );
       
   792         if ( iNameFormatter.IsTitleField( field ) )
       
   793             {
       
   794             const MVPbkContactFieldData& fieldData = field.FieldData();
       
   795             if ( fieldData.DataType() == EVPbkFieldStorageTypeText )
       
   796                 {
       
   797                 const TDesC& fieldText = MVPbkContactFieldTextData::Cast( fieldData ).Text();
       
   798                 TInt length = fieldText.Length();
       
   799                     
       
   800                 if ( length > 0 )
       
   801                     {
       
   802                     TInt firstNonSpaceChar = 0;
       
   803                     while ( firstNonSpaceChar < length 
       
   804                         && TChar( fieldText[firstNonSpaceChar] ).IsSpace() )
       
   805                         {
       
   806                         ++firstNonSpaceChar;
       
   807                         }
       
   808                     if ( firstNonSpaceChar != length )
       
   809                         {
       
   810                         result = EFalse;
       
   811                         }
       
   812                     }   
       
   813                 }
       
   814             }
       
   815         }
       
   816     return result;
       
   817     }
       
   818 // End of File