|
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 |