phonebookengines/contactsmodel/cntview/FilteredView.cpp
changeset 0 e686773b3f54
child 24 0ba2181d7c28
equal deleted inserted replaced
-1:000000000000 0:e686773b3f54
       
     1 // Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include <cntview.h>
       
    17 #include "CNTSTD.H"
       
    18 
       
    19 //#define CNTVIEW_API_PROFILING
       
    20 // To see the diferences between class versions check the in source documentation of TContactViewEvent
       
    21 const TUint KClassVersion2 = 2;
       
    22 
       
    23 /*
       
    24  * Threshold to choose whether to use base class implementation of 
       
    25  * ContactsMatchingCriteria or use remote view implementation.
       
    26  * If the filter view has 59% or more of the parent view, it is 
       
    27  * usually quicker to use the parent (remote) view.
       
    28  */
       
    29 const TReal KFilterThreshold = 0.592437 ;
       
    30 
       
    31 EXPORT_C CContactFilteredView* CContactFilteredView::NewL(MContactViewObserver& aObserver,const CContactDatabase& aDb,CContactViewBase& aView,TInt aFilter)
       
    32 /** Allocates and constructs a CContactFilteredView version 1 object.
       
    33 
       
    34 When adding/deleting contacts in the view, MContactViewObserver observer will receive 
       
    35 TContactViewEvent events with iInt parameter set to index into the observed view of the added/deleted item
       
    36 
       
    37 @param aObserver An observer that receives notifications when this view is 
       
    38 ready for use and when changes take place in it. The observer receives a TContactViewEvent::EReady 
       
    39 event when the view is ready. An attempt to use the view before this notification 
       
    40 causes a panic.
       
    41 @param aDb The database containing the contact items.
       
    42 @param aView The underlying contact view over which this view provides a filter.
       
    43 @param aFilter The filter to use. For possible values, see CContactDatabase::TContactViewFilter.
       
    44 @return The newly constructed filtered view object. */
       
    45 	{
       
    46 #ifdef CNTVIEW_API_PROFILING
       
    47 	RDebug::Print(_L("[CNTMODEL] CContactFilteredView::NewL()\n"));
       
    48 #endif
       
    49 	CContactFilteredView* self=new(ELeave) CContactFilteredView(aDb,aFilter,aView);
       
    50 	CleanupStack::PushL(self);
       
    51 	self->ConstructL(aObserver);
       
    52 	CleanupStack::Pop(self); 
       
    53 	return self;
       
    54 	}
       
    55 
       
    56 EXPORT_C CContactFilteredView* CContactFilteredView::NewL(CContactViewBase& aView,const CContactDatabase& aDb,MContactViewObserver& aObserver,TInt aFilter)
       
    57 /** Allocates and constructs a CContactFilteredView version 2 object. 
       
    58 
       
    59 When adding/deleting contacts in the view, MContactViewObserver observer will receive 
       
    60 TContactViewEvent events with iInt parameter set to index into the observed view of the added/deleted item
       
    61 
       
    62 @param aObserver An observer that receives notifications when this view is 
       
    63 ready for use and when changes take place in it. The observer receives a TContactViewEvent::EReady 
       
    64 event when the view is ready. An attempt to use the view before this notification 
       
    65 causes a panic.
       
    66 @param aDb The database containing the contact items.
       
    67 @param aView The underlying contact view over which this view provides a filter.
       
    68 @param aFilter The filter to use. For possible values, see CContactDatabase::TContactViewFilter.
       
    69 @return The newly constructed filtered view object. */
       
    70 	{
       
    71 #ifdef CNTVIEW_API_PROFILING
       
    72 	RDebug::Print(_L("[CNTMODEL] CContactFilteredView::NewL()\n"));
       
    73 #endif
       
    74 	CContactFilteredView* self=new(ELeave) CContactFilteredView(aDb,aFilter,aView);
       
    75 	CleanupStack::PushL(self);
       
    76 	self->ConstructL(aObserver);
       
    77 	self->iClassVersion = KClassVersion2;
       
    78 	CleanupStack::Pop(self); 
       
    79 	return self;
       
    80 	}
       
    81 
       
    82 /*
       
    83  * This is a reserved virtual exported function that is used for BC proofing 
       
    84  * against present and future additions of new exported virtual functions.
       
    85  @return Any return values of the helper methods called from this function or NULL.
       
    86  **/
       
    87  TAny* CContactFilteredView::CContactViewBase_Reserved_1(TFunction aFunction,TAny* aParams)
       
    88 	{
       
    89 	return CContactViewBase::CContactViewBase_Reserved_1(aFunction,aParams);
       
    90 	}
       
    91 
       
    92 TContactItemId CContactFilteredView::AtL(TInt aIndex) const
       
    93 /** Gets the contact item ID at the specified index into the filtered view.
       
    94 
       
    95 @param aIndex Index of the contact item ID into the filtered view. 
       
    96 @leave KErrNotReady The view is not ready for use.
       
    97 @leave KErrNotFound aIndex is outside the bounds of the filtered view.
       
    98 @return The contact item ID. 
       
    99 */
       
   100 	{
       
   101 	if (iState != EReady)
       
   102 		{
       
   103 		User::Leave(KErrNotReady);
       
   104 		}
       
   105 	if (aIndex>=iFilteredIdArray.Count())
       
   106 		{
       
   107 		//Out of Bounds.
       
   108 		User::Leave(KErrNotFound);
       
   109 		}
       
   110 	return iFilteredIdArray[aIndex].iId;
       
   111 	}
       
   112 
       
   113 const CViewContact& CContactFilteredView::ContactAtL(TInt aIndex) const
       
   114 /** Gets the contact item at the specified index into the view.
       
   115 
       
   116 @param aIndex Index into the view of the required item.
       
   117 @leave KErrNotReady The view is not ready for use.
       
   118 @leave KErrNotFound aIndex is outside the bounds of the filtered ID array.
       
   119 @return The contact item. 
       
   120 */
       
   121 	{
       
   122 	if (iState != EReady)
       
   123 		{
       
   124 		User::Leave(KErrNotReady);
       
   125 		}
       
   126 	if (aIndex>=iFilteredIdArray.Count())
       
   127 		{
       
   128 		//Out of Bounds.
       
   129 		User::Leave(KErrNotFound);
       
   130 		}
       
   131 	TContactIdWithMapping idWithMapping = (iFilteredIdArray)[aIndex];
       
   132 	return iView.ContactAtL(idWithMapping.iMapping);
       
   133 	}
       
   134 
       
   135 
       
   136 TInt CContactFilteredView::CountL() const
       
   137 /** Gets the number of contact item IDs in the filtered view.
       
   138 @leave KErrNotReady The view is not ready for use. 
       
   139 @return The number of contact items in the filtered view. 
       
   140 */
       
   141 	{
       
   142 	if (iState != EReady)
       
   143 		{
       
   144 		User::Leave(KErrNotReady);
       
   145 		}
       
   146 	return iFilteredIdArray.Count();
       
   147 	}
       
   148 
       
   149 TInt CContactFilteredView::FindL(TContactItemId aId) const
       
   150 /** Finds the index into the filtered view of the specified contact item.
       
   151 
       
   152 @param aId The contact item ID to search for. 
       
   153 @leave KErrNotReady The view is not ready for use.
       
   154 @return The index of the first matching item in the array or KErrNotFound if 
       
   155 no matching object can be found. 
       
   156 */
       
   157 	{
       
   158 	if (iState != EReady)
       
   159 		{
       
   160 		User::Leave(KErrNotReady);
       
   161 		}
       
   162 	TContactIdWithMapping idWithDummyMapping;
       
   163 	idWithDummyMapping.iId=aId;
       
   164 	return iFilteredIdArray.Find(idWithDummyMapping,TIdentityRelation<TContactIdWithMapping>(IdsEqual));
       
   165 	}
       
   166 
       
   167 HBufC* CContactFilteredView::AllFieldsLC(TInt aIndex,const TDesC& aSeparator) const
       
   168 /** Returns a descriptor containing the contents of all fields for an item in the 
       
   169 view.
       
   170 
       
   171 The fields are separated by aSeparator.
       
   172 
       
   173 @param aIndex The index into the filtered view of the contact item.
       
   174 @param aSeparator The string to use to separate the fields.
       
   175 @return Pointer to the specified contact item descriptor. */
       
   176 	{
       
   177 	return iView.AllFieldsLC((iFilteredIdArray)[aIndex].iMapping,aSeparator);
       
   178 	}
       
   179 
       
   180 #ifdef _DEBUG
       
   181 void CContactFilteredView::HandleContactViewEvent(const CContactViewBase& aView,const TContactViewEvent& aEvent)
       
   182 #else
       
   183 void CContactFilteredView::HandleContactViewEvent(const CContactViewBase& /*aView*/,const TContactViewEvent& aEvent)
       
   184 #endif
       
   185 	{
       
   186 	ASSERT(&aView==&iView);
       
   187 	TBool doUpdate(EFalse);
       
   188 	TContactViewEvent event=aEvent;
       
   189 	switch (event.iEventType)
       
   190 		{
       
   191 		case TContactViewEvent::ESortError:
       
   192 		case TContactViewEvent::EServerError:
       
   193 		case TContactViewEvent::EUnavailable:
       
   194 			iState=ENotReady;
       
   195 			break;
       
   196 		case TContactViewEvent::EReady:
       
   197 		case TContactViewEvent::ESortOrderChanged:
       
   198 			{
       
   199 			//Underlying view is ready, create filtered view.
       
   200 			CreateFilteredIdArray();
       
   201 			return; // Notification is handled in CreateFilteredIdArray
       
   202 			}
       
   203 		case TContactViewEvent::EItemAdded:
       
   204 			{
       
   205 			TBool viewModified(EFalse);
       
   206 			TRAPD(err,UpdateFilteredIdArrayL(event,viewModified));
       
   207 			if(err!=KErrNone && err!=KErrNotFound)
       
   208 				{
       
   209 				NotifyObservers(TContactViewEvent(TContactViewEvent::ESortError,err));
       
   210 				return;
       
   211 				}
       
   212 			if (!viewModified)
       
   213 				return; //If view wasn't modified, don't notify the observers
       
   214 			}
       
   215 			break;
       
   216 		case TContactViewEvent::EItemRemoved:
       
   217 			{
       
   218 			TBool viewModified(EFalse);
       
   219 			doUpdate = ETrue;
       
   220 			TRAPD(err,UpdateFilteredIdArrayL(event,viewModified));
       
   221 			if(err!=KErrNone && err!=KErrNotFound)
       
   222 				{
       
   223 				NotifyObservers(TContactViewEvent(TContactViewEvent::ESortError,err));
       
   224 				return;
       
   225 				}
       
   226 			if (!viewModified)
       
   227 				{
       
   228 				UpdateMappingsL();
       
   229 				return; //If view wasn't modified, don't notify the observers
       
   230 				}
       
   231 			}
       
   232 			break;
       
   233 		case TContactViewEvent::EGroupChanged:
       
   234 			break;
       
   235 		default:
       
   236 			ASSERT(EFalse);
       
   237 		}
       
   238 
       
   239 	NotifyObservers(event);
       
   240 	if(doUpdate) 
       
   241 		{ 
       
   242 		UpdateMappingsL(); //Update mappings for multiple changes and notify observers. 
       
   243 		} 
       
   244 
       
   245 	}
       
   246 
       
   247 CContactFilteredView::~CContactFilteredView()
       
   248 	{
       
   249 	iFilteredIdArray.Close();
       
   250 	iView.Close(*this);
       
   251 	}
       
   252 
       
   253 CContactFilteredView::CContactFilteredView(const CContactDatabase& aDb,TInt aFilter,CContactViewBase& aView)
       
   254 	: CContactViewBase(aDb),iFilter(aFilter),iView(aView)
       
   255 	{
       
   256 	}
       
   257 	
       
   258 void CContactFilteredView::ConstructL(MContactViewObserver& aObserver)
       
   259 	{
       
   260 	CContactViewBase::ConstructL();
       
   261 	OpenL(aObserver);
       
   262 	iView.OpenL(*this);
       
   263 	}
       
   264 
       
   265 /* 
       
   266  * Construct the filtered view. 
       
   267  * This method is called when the underlying view sends an EReady or 
       
   268  * ESortOrderChanged event. It is implemented by asking the underlying
       
   269  * view which contacts match the filter (via CContactViewBase_Reserved_1)
       
   270  */
       
   271 void CContactFilteredView::CreateFilteredIdArray()
       
   272 	{
       
   273 	iFilteredIdArray.Reset();
       
   274 
       
   275 	CContactViewBase::TVirtualFunction2Params structOfParams(iFilter, iFilteredIdArray);
       
   276 	TRAPD(err, iView.CContactViewBase_Reserved_1(CContactViewBase::ECContactViewBaseVirtualFunction2, &structOfParams)); //leaving method
       
   277 
       
   278 	if(err==KErrNone) // View built OK, set to EReady or ESortOrderChanged
       
   279 		{
       
   280 		TState oldState = iState;
       
   281  		iState=EReady;
       
   282  
       
   283  		if (oldState == EInitializing)
       
   284 			{
       
   285 			NotifyObservers(TContactViewEvent(TContactViewEvent::EReady));
       
   286 			}
       
   287  		else
       
   288  			{
       
   289  			NotifyObservers(TContactViewEvent(TContactViewEvent::ESortOrderChanged));
       
   290  			}
       
   291 		}
       
   292 	else // View building had problems
       
   293 		{
       
   294 		NotifyObservers(TContactViewEvent(TContactViewEvent::ESortError,err));
       
   295 		}
       
   296 	}
       
   297 
       
   298 void CContactFilteredView::UpdateFilteredIdArrayL(TContactViewEvent& aEvent, TBool& aViewModified)
       
   299 	{
       
   300 	// Note, this method should update the index in aEvent - it will be passed on to observers of this view.
       
   301 	switch(aEvent.iEventType)
       
   302 		{
       
   303 		case TContactViewEvent::EItemAdded:
       
   304 			HandleAddEventL(aEvent, aViewModified);
       
   305 			break;
       
   306 		case TContactViewEvent::EItemRemoved:
       
   307 			HandleRemoveEventL(aEvent, aViewModified);
       
   308 			break;
       
   309 		default:
       
   310 			ASSERT(EFalse);
       
   311 		};
       
   312 	}
       
   313 
       
   314 void CContactFilteredView::HandleAddEventL(TContactViewEvent& aEvent, TBool& aViewModified)
       
   315 	{
       
   316 	// The id contained in aEvent can refer to a inexistent contact by now 
       
   317 	// so check that the contact with that id still exist first
       
   318 	TInt underlyingViewIndex = iView.FindL(aEvent.iContactId);
       
   319 	User::LeaveIfError(underlyingViewIndex);
       
   320 
       
   321 	// get contact data from view rather than re-read from disk (speeds up access time)
       
   322 	const CViewContact& contact = iView.ContactAtL(underlyingViewIndex);
       
   323 	TContactIdWithMapping findMap;
       
   324 	findMap.iId=contact.Id();
       
   325 	findMap.iMapping=underlyingViewIndex;
       
   326 	UpdateMappingsL();
       
   327 	if(contact.ContactMatchesFilter(iFilter))
       
   328 		{
       
   329 		aViewModified=ETrue;
       
   330 		//Contact Matches Filter, need to find the correct position to insert.
       
   331 		aEvent.iInt=BinaryInsertL(findMap);
       
   332 		}
       
   333 	}
       
   334 
       
   335 void CContactFilteredView::HandleRemoveEventL(TContactViewEvent& aEvent, TBool& aViewModified)
       
   336 	{
       
   337 	//Remove the contact from the filter array and alter the Event to local mapping
       
   338 	TContactIdWithMapping findMap;
       
   339 	findMap.iId = aEvent.iContactId;
       
   340 	findMap.iMapping=KErrNotFound;//not important
       
   341 	const TInt position = iFilteredIdArray.Find(findMap,TIdentityRelation<TContactIdWithMapping>(IdsEqual));
       
   342 	if (position!=KErrNotFound)
       
   343 		{
       
   344 		aViewModified=ETrue;
       
   345 		iFilteredIdArray.Remove(position);
       
   346 		}
       
   347 	aEvent.iInt=position;
       
   348 	}
       
   349 
       
   350 /* Update index mappings used in the filtered view. */
       
   351 void CContactFilteredView::UpdateMappingsL()
       
   352 	{
       
   353 	 // View mappings are now invalid Fix all mapped indexes
       
   354 	TInt updatedViewIndex=KErrNotFound;
       
   355 	for (TInt i=0; i < iFilteredIdArray.Count(); ++i)
       
   356 		{
       
   357 		TContactItemId idUpdate = iFilteredIdArray[i].iId;
       
   358 		updatedViewIndex=iView.FindL(idUpdate);
       
   359 		if(updatedViewIndex==KErrNotFound)
       
   360 			{
       
   361 			//local view has removed this contact, so we need to as well.
       
   362 			iFilteredIdArray.Remove(i);
       
   363 			NotifyObservers(TContactViewEvent(TContactViewEvent::EItemRemoved, i, idUpdate));
       
   364 			i--;
       
   365 			continue;
       
   366 			}
       
   367 		iFilteredIdArray[i].iMapping=updatedViewIndex;
       
   368 		}
       
   369 	}
       
   370 
       
   371 
       
   372 TInt CContactFilteredView::BinaryInsertL(TContactIdWithMapping aId)
       
   373 	{
       
   374 	TInt min=0;
       
   375 	TInt max=iFilteredIdArray.Count();
       
   376 	TInt match = aId.iMapping;
       
   377 	FOREVER
       
   378 		{
       
   379 		if (min==max)
       
   380 			break; // min is the new position
       
   381 		TInt index=(max-min)/2+min;
       
   382 		TInt compare = (iFilteredIdArray)[index].iMapping;
       
   383 		if (compare<=match)
       
   384 			{
       
   385 			if (min==index)
       
   386 				min++;
       
   387 			else
       
   388 				min=index;
       
   389 			}
       
   390 		else if (compare>match)
       
   391 			max=index;
       
   392 		}
       
   393 	TInt err = iFilteredIdArray.Insert(aId,min);
       
   394 	User::LeaveIfError(err);
       
   395 	return min;
       
   396 	}
       
   397 
       
   398 TContactViewPreferences CContactFilteredView::ContactViewPreferences()
       
   399 /** Gets the underlying view's view preferences.
       
   400 
       
   401 @return The view preferences. */
       
   402 	{
       
   403 	return iView.ContactViewPreferences();
       
   404 	}
       
   405 
       
   406 const RContactViewSortOrder& CContactFilteredView::SortOrderL() const
       
   407 /** Gets the underlying view's sort order.
       
   408 
       
   409 @return The sort order. */
       
   410 	{
       
   411 	return iView.SortOrderL();
       
   412 	}
       
   413 
       
   414 
       
   415 void CContactFilteredView::ContactsMatchingCriteriaL(const MDesCArray& aFindWords,RPointerArray<CViewContact>& aMatchedContacts)
       
   416 /** Searches all contact items in the filtered view for fields that contain all 
       
   417 of the search strings specified.
       
   418 
       
   419 The search uses wildcard matching so that the search strings can occur anywhere 
       
   420 in an item's fields. For a match to occur, all of the search strings must 
       
   421 be found in the contact item.
       
   422 
       
   423 @param aFindWords A descriptor array containing one or more search strings.
       
   424 @param aMatchedContacts On return, an array of matching contact items. */
       
   425 	{
       
   426 	const TReal parentViewCount = static_cast<TReal>(iView.CountL());
       
   427 	const TReal filterViewCount = static_cast<TReal>(iFilteredIdArray.Count());
       
   428 	
       
   429 	if (parentViewCount!=0 && (filterViewCount/parentViewCount) < KFilterThreshold)
       
   430 		{
       
   431 		CContactViewBase::ContactsMatchingCriteriaL(aFindWords, aMatchedContacts);
       
   432 		}
       
   433 	else
       
   434 		{
       
   435 		iView.ContactsMatchingCriteriaL(aFindWords, aMatchedContacts);
       
   436 		FilterResultsArray(aMatchedContacts);
       
   437 		} 
       
   438 	}
       
   439 
       
   440 void CContactFilteredView::ContactsMatchingPrefixL(const MDesCArray& aFindWords, RPointerArray<CViewContact>& aMatchedContacts)
       
   441 /** Searches all contact items in the filtered view for fields that contain all 
       
   442 of the search strings specified.
       
   443 
       
   444 Unlike ContactsMatchingCriteriaL(), the search term can only occur at the 
       
   445 beginning of a field.
       
   446 
       
   447 @param aFindWords A descriptor array containing one or more search strings.
       
   448 @param aMatchedContacts On return, an array of matching contact items. */
       
   449 	{
       
   450 	iView.ContactsMatchingPrefixL(aFindWords, aMatchedContacts);
       
   451 	FilterResultsArray(aMatchedContacts);
       
   452 	}
       
   453 
       
   454 /* 
       
   455  * Filter the results from ContactsMatchingCriteria/ContactsMatchingPrefix
       
   456  * removing contacts which are not in the current filtered view
       
   457  */
       
   458 void CContactFilteredView::FilterResultsArray(RPointerArray<CViewContact>& aMatchedContacts)
       
   459 	{
       
   460 	TInt counter = 0;
       
   461 	CViewContact* contactPtr;
       
   462 	TContactIdWithMapping contactIdWithMapping;
       
   463 	TInt max = aMatchedContacts.Count();
       
   464 	// for each returned CContactFilteredView, check to
       
   465 	// make sure that it is a contact which exists in
       
   466 	// our list.
       
   467 
       
   468 	// our list						: iFilteredIdArray
       
   469 	// list to export is 			: aMatchedContacts
       
   470 	while (counter < max)
       
   471 		{
       
   472 		contactPtr = aMatchedContacts[counter];
       
   473 		contactIdWithMapping.iId = contactPtr->Id();
       
   474 
       
   475 		// if the contact does not exist in our filtered list, then
       
   476 		if ( iFilteredIdArray.Find(contactIdWithMapping, CompareMappings) == KErrNotFound )
       
   477 			{
       
   478 			// remove it from our list
       
   479 			aMatchedContacts.Remove(counter); // does not delete pointer
       
   480 			delete contactPtr;
       
   481 			// we've removed an item from the array
       
   482 			// this means that the item above, will drop to fill the hole left by
       
   483 			// the item we've deleted.
       
   484 			// so there is no need to increment the counter, but we do need to
       
   485 			// decrement the max number of contacts we're parsing.
       
   486 			max--;
       
   487 			}
       
   488 		else
       
   489 			{
       
   490 			counter++;
       
   491 			}
       
   492 		} 
       
   493 	}
       
   494 
       
   495 //static
       
   496 TBool CContactFilteredView::CompareMappings(const TContactIdWithMapping& aFirst,const TContactIdWithMapping& aSecond)
       
   497 	{
       
   498 	return (aFirst.iId == aSecond.iId);
       
   499 	}