phonebookengines/cntlistmodel/src/cntcache.cpp
changeset 81 640d30f4fb64
parent 77 c18f9fa7f42e
child 84 63017c97b1d6
equal deleted inserted replaced
77:c18f9fa7f42e 81:640d30f4fb64
     1 /*
       
     2 * Copyright (c) 2010 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: Class for asynchronously fetching and caching basic
       
    15 *              contact info (see CntContactInfo) for list views.
       
    16 *
       
    17 */
       
    18 
       
    19 #include <hbapplication.h>
       
    20 #include <qtcontacts.h>
       
    21 #include <qcontactmanager.h>
       
    22 #include <QTimer>
       
    23 
       
    24 #include <cntdebug.h>
       
    25 #include "cntcache.h"
       
    26 #include "cntnamefetcher.h"
       
    27 #include "cntcache_p.h"
       
    28 
       
    29 // set the singleton instance pointer to NULL
       
    30 CntCache* CntCache::mInstance = NULL;
       
    31 
       
    32 // value for first cache order to be assigned
       
    33 static const int CacheOrderStartValue = 1;
       
    34 // for avoiding wrap around with cache orders
       
    35 static const int MaxCacheOrderValue = 10000000;
       
    36 // number of items to read quickly when a new instance is requested or cache is cleared
       
    37 static const int ItemsToReadUrgently = 13;
       
    38 // number of items to read ahead into cache; this number is for one direction
       
    39 static const int ItemsToCacheAhead = 24;
       
    40 // cache size for info items (name, text, icon1name, icon2name)
       
    41 static const int InfoCacheSize = 128;
       
    42 // cache size for icon items (iconName and HbIcon)
       
    43 static const int IconCacheSize = 50;
       
    44 // number of icons in a CntContactInfo object
       
    45 static const int IconsInCntContactInfo = 2;
       
    46 // default empty text info field for a contact; it cannot be empty
       
    47 // as the listview will then ignore it, causing rendering problems
       
    48 static const QString EmptyTextField = " ";
       
    49 
       
    50 /*!
       
    51     Provides a pointer to the CntCache singleton instance.
       
    52  */
       
    53 CntCache* CntCache::instance(QContactManager *manager)
       
    54 {
       
    55     if (!mInstance) {
       
    56         mInstance = new CntCache(manager);
       
    57     }
       
    58 
       
    59     // whenever a client requests an instance the client will want to get all info
       
    60     // for the first couple of contacts (~a screenfull) as fast as possible
       
    61     mInstance->mUrgentContacts = ItemsToReadUrgently;
       
    62 
       
    63     return mInstance;
       
    64 }
       
    65 
       
    66 /*! 
       
    67     Fetches information about a contact: name, text (e.g. phone number or
       
    68     social status) and two icons (e.g. avatar, presence). Previously cached
       
    69     content - at the very least the name - will be returned immediately.
       
    70     Availability of more information will be checked asynchronously and
       
    71     sent to clients via contactInfoUpdated() signals.
       
    72       
       
    73     The function takes a row and a list rather than just a contact id because
       
    74     of read ahead caching - contacts near the requested contacts are expected
       
    75     to be needed soon and are therefore also scheduled for caching.
       
    76 
       
    77     \param row the row of the contact to fetch
       
    78     \param idList a list with all the IDs in the list
       
    79     \return a contact with some details filled in
       
    80  */
       
    81 CntContactInfo CntCache::fetchContactInfo(int row, const QList<QContactLocalId>& idList)
       
    82 {
       
    83     CNT_ENTRY_ARGS(row << "/" << idList.count())
       
    84 
       
    85     Q_ASSERT(row >= 0 && row < idList.count());
       
    86 
       
    87     QString name;
       
    88     QString text = EmptyTextField;
       
    89     HbIcon icons[IconsInCntContactInfo];
       
    90 
       
    91     int contactId = idList.at(row);
       
    92 
       
    93     if (contactId != mEmittedContactId) {
       
    94         // this request comes from the UI when a new view is created or in response to
       
    95         // some scrolling activity; in the former case, the client should
       
    96         // have set urgencymode on, but in the latter case:
       
    97         // 1) postpone all jobs so the UI can use as much of the CPU as possible
       
    98         // 2) update read ahead cache to contain all IDs of all items near this item
       
    99         if (mUrgentContacts > 0) {
       
   100             --mUrgentContacts;
       
   101         } else {
       
   102             mWorker->postponeJobs(150);
       
   103         }
       
   104         updateReadAheadCache(row, idList);
       
   105     }
       
   106 
       
   107     // fetch contact
       
   108     if (mInfoCache.contains(contactId)) {
       
   109         // the item is in the cache
       
   110         CntInfoCacheItem* infoItem = mInfoCache.value(contactId);
       
   111         for (int i = 0; i < IconsInCntContactInfo; ++i) {
       
   112             QString iconName = infoItem->icons[i];
       
   113             if (!iconName.isEmpty()) {
       
   114                 if (mIconCache.contains(iconName)) {
       
   115                     CntIconCacheItem* iconItem = mIconCache.value(iconName);
       
   116                     iconItem->cacheOrder = mNextIconCacheOrder++;
       
   117                     icons[i] = iconItem->icon;
       
   118                     if (!iconItem->isFetched) {
       
   119                         // if icon has not yet been received from backend, add
       
   120                         // this id to the list of contacts that want to be
       
   121                         // notified when the icon is received
       
   122                         iconItem->contactIds.insert(contactId);
       
   123                         // also reschedule it
       
   124                         mWorker->scheduleIconJob(iconName, row);
       
   125                     }
       
   126                 } else {
       
   127                     // needed icon is not in cache, so schedule it for retrieval
       
   128                     CntIconCacheItem* iconItem = createIconCacheItem(iconName);
       
   129                     iconItem->contactIds.insert(contactId);
       
   130                     mWorker->scheduleIconJob(iconName, row);
       
   131                 }
       
   132             }
       
   133         }
       
   134 
       
   135         // set return text
       
   136         text = infoItem->text;
       
   137 
       
   138         // update cache order
       
   139         infoItem->cacheOrder = mNextInfoCacheOrder++;
       
   140         infoItem->latestRow = row;
       
   141     } else {
       
   142         // the contact info is not in cache, schedule it for retrieval
       
   143         if (contactExists(contactId)) {
       
   144             // contact found, so add new entry to cache
       
   145             CntInfoCacheItem* item = createInfoCacheItem(contactId);
       
   146             item->text = text;
       
   147             item->latestRow = row;
       
   148 
       
   149             // ask the worker thread to fetch the information asynchronously
       
   150             mWorker->scheduleInfoJob(contactId, row);
       
   151         }
       
   152     }
       
   153 
       
   154     name = contactName(contactId);
       
   155     CNT_EXIT_ARGS("name:" << name << "sec:" << text)
       
   156 
       
   157     return CntContactInfo(contactId, name, text, icons[0], icons[1]);
       
   158 }
       
   159 
       
   160 /*! 
       
   161     Creates a list of contact ids sorted according the corresponding contact names.
       
   162 
       
   163     \param idFilter the IDs to be returned; if NULL, all contact IDs are returned
       
   164     \return the list of ids, sorted according the contact name
       
   165  */
       
   166 QList<QContactLocalId> CntCache::sortIdsByName(const QSet<QContactLocalId>* idFilter) const
       
   167 {
       
   168     CNT_ENTRY
       
   169 
       
   170     QList<QContactLocalId> sortedIds;
       
   171     
       
   172     // allocate memory in advance to avoid repeated reallocation during population
       
   173     // an extra 16 items are allocated to leave room for a few more contacts
       
   174     // before reallocation is needed
       
   175     if (!idFilter) {
       
   176         sortedIds.reserve(mSortedNames.count() + 16);
       
   177     } else {
       
   178         sortedIds.reserve(idFilter->count() + 16);
       
   179     }
       
   180 
       
   181     // the entries in mSortedNames are already sorted, so just pick
       
   182     // out the ids from that list in the order that they appear
       
   183     if (!idFilter) {
       
   184         foreach (CntNameCacheItem* item, mSortedNames) {
       
   185             sortedIds.append(item->contactId());
       
   186         }
       
   187     } else {
       
   188         foreach (CntNameCacheItem* item, mSortedNames) {
       
   189             if (idFilter->contains(item->contactId())) {
       
   190                 sortedIds.append(item->contactId());
       
   191             }
       
   192         }
       
   193     }
       
   194 
       
   195     CNT_EXIT
       
   196 
       
   197     return sortedIds;
       
   198 }
       
   199 
       
   200 /*!
       
   201     Overloaded version of the function for string based searching of contact names.
       
   202     Currently for multi part names only space and dash variations are used for filtering,
       
   203     e.g. "Axx Bxx" or "Axx-Bxx" are the only possible matches along with the original string. 
       
   204     
       
   205     \param searchList list of strings which are used for search
       
   206     \return the list of ids, sorted according the contact name
       
   207  */
       
   208 QList<QContactLocalId> CntCache::sortIdsByName(const QStringList searchList) const
       
   209 {
       
   210     CNT_ENTRY
       
   211     
       
   212     QList<QContactLocalId> sortedIds;
       
   213     int iterNames = 0;
       
   214     int iterList = 0;
       
   215     QString firstName = 0;
       
   216     QString lastName = 0;
       
   217     QString tempString = 0;
       
   218     QString tempDash = 0;
       
   219     QString tempSpace = 0;
       
   220     int matchesFound = 0;
       
   221     const QChar dash = '-';
       
   222     const QChar space = ' ';
       
   223     QStringList searchVariations;
       
   224     
       
   225     for (iterList = 0; iterList < searchList.size(); iterList++)
       
   226     {
       
   227         tempString = searchList.at(iterList);
       
   228         tempDash = tempString;
       
   229         tempSpace = tempString;
       
   230         tempDash.insert(0, dash);
       
   231         tempSpace.insert(0, space);
       
   232         
       
   233         searchVariations.append(tempString);
       
   234         searchVariations.append(tempDash);
       
   235         searchVariations.append(tempSpace);
       
   236     }
       
   237     
       
   238     for (iterNames = 0; iterNames < mSortedNames.size(); iterNames++)
       
   239     {
       
   240         matchesFound = 0;
       
   241         firstName = (mSortedNames.at(iterNames))->firstName();
       
   242         lastName = (mSortedNames.at(iterNames))->lastName();
       
   243         for (iterList = 0; iterList < searchVariations.size(); iterList += 3)
       
   244         {
       
   245             // if the current name doesn't contain any of the possible variations then it can be skipped
       
   246             if ( !( firstName.startsWith(searchVariations.at(iterList), Qt::CaseInsensitive) ||
       
   247                     lastName.startsWith(searchVariations.at(iterList), Qt::CaseInsensitive) ||
       
   248                     firstName.contains(searchVariations.at(iterList+1), Qt::CaseInsensitive) ||
       
   249                     lastName.contains(searchVariations.at(iterList+1), Qt::CaseInsensitive) ||
       
   250                     firstName.contains(searchVariations.at(iterList+2), Qt::CaseInsensitive) ||
       
   251                     lastName.contains(searchVariations.at(iterList+2), Qt::CaseInsensitive) ) )
       
   252             {
       
   253                 break;
       
   254             }
       
   255         }
       
   256         if (iterList == searchVariations.size())
       
   257         {
       
   258             sortedIds.append(mSortedNames.at(iterNames)->contactId());
       
   259         }
       
   260     }
       
   261     
       
   262     CNT_EXIT
       
   263 
       
   264     return sortedIds;
       
   265 }
       
   266 
       
   267 /*!
       
   268     Creates the CntCache singleton instance.
       
   269  */
       
   270 CntCache::CntCache(QContactManager *manager)
       
   271     : mContactManager(manager),
       
   272       mWorker(new CntCacheThread()),
       
   273       mNameFetcher(new CntNameFetcher()),
       
   274       mNextInfoCacheOrder(CacheOrderStartValue),
       
   275       mNextIconCacheOrder(CacheOrderStartValue),
       
   276       mEmittedContactId(-1),
       
   277       mUrgentContacts(0),
       
   278       mHasModifiedNames(false),
       
   279       mAllNamesFetchStarted(false)
       
   280 {
       
   281     CNT_ENTRY
       
   282 
       
   283     // listen to name fetcher
       
   284     connect(mNameFetcher, SIGNAL(nameFormatChanged(CntNameOrder)), this, SLOT(reformatNames(CntNameOrder)));
       
   285     connect(mNameFetcher, SIGNAL(databaseAccessComplete()), mWorker, SLOT(resumeJobs()));
       
   286     connect(mNameFetcher, SIGNAL(namesAvailable(QList<CntNameCacheItem *>)), this, SLOT(setNameList(QList<CntNameCacheItem *>)));
       
   287 
       
   288     // listen to info fetcher
       
   289     connect(mWorker, SIGNAL(infoFieldUpdated(int, const ContactInfoField&, const QString&)),
       
   290             this, SLOT(onNewInfo(int, const ContactInfoField&, const QString&)));
       
   291     connect(mWorker, SIGNAL(infoCancelled(int)), this, SLOT(onInfoCancelled(int)));
       
   292 
       
   293     // listen to icon fetcher
       
   294     connect(mWorker, SIGNAL(iconUpdated(const QString&, const HbIcon&)),
       
   295             this, SLOT(onNewIcon(const QString&, const HbIcon&)));
       
   296     connect(mWorker, SIGNAL(iconCancelled(const QString&)), this, SLOT(onIconCancelled(const QString&)));
       
   297     connect(mWorker, SIGNAL(allJobsDone()), this, SLOT(scheduleOneReadAheadItem()));
       
   298 
       
   299     // listen to contact manager
       
   300     connect(mContactManager, SIGNAL(contactsChanged(const QList<QContactLocalId>&)), this, SLOT(updateContacts(const QList<QContactLocalId>&)));
       
   301     connect(mContactManager, SIGNAL(contactsRemoved(const QList<QContactLocalId>&)), this, SLOT(removeContacts(const QList<QContactLocalId>&)));
       
   302     connect(mContactManager, SIGNAL(contactsAdded(const QList<QContactLocalId>&)), this, SLOT(addContacts(const QList<QContactLocalId>&)));
       
   303 
       
   304     // listen to application -- shut down cache only when the whole application quits
       
   305     connect(HbApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(onShutdown()));
       
   306 
       
   307     // load all names to RAM
       
   308     loadNames();
       
   309 
       
   310     CNT_EXIT
       
   311 }
       
   312 
       
   313 /*!
       
   314     Destructs the CntCache singleton instance.
       
   315  */
       
   316 CntCache::~CntCache()
       
   317 {
       
   318     CNT_ENTRY
       
   319 
       
   320     if (mHasModifiedNames) {
       
   321         mNameFetcher->writeNamesToCache(mSortedNames);
       
   322     }
       
   323 
       
   324     delete mWorker;
       
   325     delete mNameFetcher;
       
   326     
       
   327     qDeleteAll(mInfoCache);
       
   328     mInfoCache.clear();
       
   329 
       
   330     qDeleteAll(mIconCache);
       
   331     mIconCache.clear();
       
   332 
       
   333     qDeleteAll(mNameCache);
       
   334     mNameCache.clear();
       
   335     mSortedNames.clear();
       
   336 
       
   337     CNT_EXIT
       
   338 }
       
   339 
       
   340 /*! 
       
   341     Processes a new info field that has arrived from the worker thread.
       
   342     If the contact is in the info cache, then the info cache is updated
       
   343     accordingly.
       
   344     
       
   345     A contactInfoUpdated() signal is usually also emitted. The exception
       
   346     is if the info is the name of an icon and that icon is not in the icon
       
   347     cache. In this case the icon is fetched before a signal is emitted.
       
   348  */
       
   349 void CntCache::onNewInfo(int contactId, const ContactInfoField& infoField, const QString& infoValue)
       
   350 {
       
   351     CNT_ENTRY_ARGS( "id:" << contactId   << "infotype:" << infoField   << "infovalue:" << infoValue )
       
   352 
       
   353     Q_ASSERT(infoField == ContactInfoTextField || infoField == ContactInfoIcon1Field || infoField == ContactInfoIcon2Field);
       
   354 
       
   355     bool hasNewInfo;
       
   356 
       
   357     if (!mInfoCache.contains(contactId)) {
       
   358         // contact is not in cache, so nothing needs to be done
       
   359         // except notify clients that this contact has (possibly)
       
   360         // been changed
       
   361         hasNewInfo = true;
       
   362     }
       
   363     else if (infoField == ContactInfoTextField) {
       
   364         // update cache with new text for contact
       
   365         mInfoCache.value(contactId)->text = infoValue;
       
   366         hasNewInfo = true;
       
   367     }
       
   368     else {
       
   369         // update cache with new icon name for contact
       
   370         int iconIndex = (infoField == ContactInfoIcon1Field ? 0 : 1);
       
   371 
       
   372         CntInfoCacheItem* item = mInfoCache.value(contactId);
       
   373         QString iconName = infoValue;
       
   374         if (item->icons[iconIndex] != iconName) {
       
   375             item->icons[iconIndex] = iconName;
       
   376             if (iconName.isEmpty()) {
       
   377                 hasNewInfo = true;
       
   378             }
       
   379             else if (mIconCache.contains(iconName)) {
       
   380                 CntIconCacheItem* iconItem = mIconCache.value(iconName);
       
   381                 if (!iconItem->isFetched) {
       
   382                     iconItem->contactIds.insert(contactId);
       
   383                     hasNewInfo = false;
       
   384                 }
       
   385                 else {
       
   386                     hasNewInfo = true;
       
   387                 }
       
   388             }
       
   389             else if (iconName.startsWith("qtg_", Qt::CaseInsensitive)) {
       
   390                 createIconCacheItem(iconName);
       
   391                 onNewIcon(iconName, HbIcon(iconName)); 
       
   392                 hasNewInfo = true;
       
   393             }
       
   394             else {
       
   395                 CntIconCacheItem* iconItem = createIconCacheItem(iconName);
       
   396                 iconItem->contactIds.insert(contactId);
       
   397 				if (mInfoCache.contains(contactId)) {
       
   398                 	mWorker->scheduleIconJob(iconName, mInfoCache.value(contactId)->latestRow);
       
   399 				}
       
   400 				else {
       
   401 					// less important icon, since this contact is not in cache
       
   402                 	mWorker->scheduleIconJob(iconName, 100000);
       
   403 				}
       
   404                 hasNewInfo = false;
       
   405             }
       
   406         }
       
   407         else {
       
   408             hasNewInfo = false;
       
   409         }
       
   410     }
       
   411 
       
   412     if (hasNewInfo) {
       
   413         emitContactInfoUpdated(contactId);
       
   414     }
       
   415 
       
   416     CNT_EXIT
       
   417 }
       
   418 
       
   419 /*! 
       
   420     Handle the case where a request for contact info is cancelled by the
       
   421     worker because of too many subsequent requests.
       
   422  */
       
   423 void CntCache::onInfoCancelled(int contactId)
       
   424 {
       
   425     CNT_ENTRY_ARGS( "id:" << contactId )
       
   426 
       
   427     if (mInfoCache.contains(contactId)) {
       
   428         CntInfoCacheItem* item = mInfoCache.take(contactId);
       
   429         delete item;
       
   430     }
       
   431 
       
   432     emitContactInfoUpdated(contactId);
       
   433 
       
   434     CNT_EXIT
       
   435 }
       
   436 
       
   437 /*! 
       
   438     Processes a new icon that has arrived from the worker thread.
       
   439     The icon cache is updated and a contactInfoUpdated() signal is
       
   440     emitted for all contacts that use this icon.
       
   441  */
       
   442 void CntCache::onNewIcon(const QString& iconName, const HbIcon& icon)
       
   443 {
       
   444     CNT_ENTRY_ARGS( iconName )
       
   445 
       
   446     QSet<int> contactsToNotify;
       
   447 
       
   448     if (mIconCache.contains(iconName)) {
       
   449         CntIconCacheItem* item = mIconCache.value(iconName);
       
   450         item->icon = icon;
       
   451         item->isFetched = true;
       
   452         contactsToNotify = item->contactIds;
       
   453         item->contactIds.clear();
       
   454     }
       
   455 
       
   456     foreach (int contactId, contactsToNotify) {
       
   457         emitContactInfoUpdated(contactId);
       
   458     }
       
   459 
       
   460     CNT_EXIT
       
   461 }
       
   462 
       
   463 /*! 
       
   464     Handle the case where a request for an icon is cancelled by the worker because
       
   465     of too many subsequent requests.
       
   466  */
       
   467 void CntCache::onIconCancelled(const QString& iconName)
       
   468 {
       
   469     CNT_ENTRY_ARGS( iconName )
       
   470 
       
   471     QSet<int> contactsToNotify;
       
   472 
       
   473     if (mIconCache.contains(iconName)) {
       
   474         CntIconCacheItem* item = mIconCache.take(iconName);
       
   475         contactsToNotify = item->contactIds;
       
   476         item->contactIds.clear();
       
   477         delete item;
       
   478     }
       
   479 
       
   480     foreach (int contactId, contactsToNotify) {
       
   481         emitContactInfoUpdated(contactId);
       
   482     }
       
   483 
       
   484     CNT_EXIT
       
   485 }
       
   486 
       
   487 /*!
       
   488     Fetch the names of all contacts.
       
   489  */
       
   490 void CntCache::loadNames()
       
   491 {
       
   492     CNT_ENTRY
       
   493     
       
   494     // read names from file cache
       
   495     mNameFetcher->readNamesFromCache(mSortedNames);
       
   496 
       
   497     // insert the names into the id-to-name map
       
   498     foreach (CntNameCacheItem* item, mSortedNames) {
       
   499         mNameCache.insert(item->contactId(), item);
       
   500     }
       
   501 
       
   502     // if there are no names in file cache, start the asynch
       
   503     // read of all names immediately (normally it is done
       
   504     // after secondary info has been read)
       
   505     if (mSortedNames.count() == 0) {
       
   506         mWorker->postponeJobs();
       
   507         mAllNamesFetchStarted = true;
       
   508         mNameFetcher->readAllNamesAsynch();
       
   509     }
       
   510 
       
   511     CNT_EXIT
       
   512 }
       
   513 
       
   514 /*!
       
   515     Checks whether a contact exists.
       
   516  */
       
   517 bool CntCache::contactExists(QContactLocalId contactId) const
       
   518 {
       
   519     return mNameCache.contains(contactId);
       
   520 }
       
   521 
       
   522 /*!
       
   523     Fetch the name of one contact.
       
   524  */
       
   525 QString CntCache::contactName(QContactLocalId contactId) const
       
   526 {
       
   527     CNT_ENTRY
       
   528 
       
   529     QString name;
       
   530 
       
   531     QHash<QContactLocalId, CntNameCacheItem*>::const_iterator i = mNameCache.find(contactId);
       
   532     if (i != mNameCache.end()) {
       
   533         name = i.value()->name();
       
   534     }
       
   535 
       
   536     CNT_EXIT
       
   537 
       
   538     return name;
       
   539 }
       
   540 
       
   541 /*! 
       
   542     Collects all contact IDs near the latest fetch from the UI. These will be fetched and
       
   543     precached when UI activity slows down.
       
   544 
       
   545     \param mostRecentRow the row of the contact that was most recently fetched
       
   546     \param idList a list with all the IDs in the list
       
   547  */
       
   548 void CntCache::updateReadAheadCache(int mostRecentRow, const QList<QContactLocalId>& idList)
       
   549 {
       
   550     CNT_ENTRY_ARGS( mostRecentRow )
       
   551 
       
   552     int row;
       
   553 
       
   554     mReadAheadCache.clear();
       
   555     
       
   556     // step through the area near to last fetch item and make sure all
       
   557     // contacts in it are also in cache or in the read ahead list
       
   558     for (int i = 1; i <= ItemsToCacheAhead; ++i) {
       
   559         for (int j = 0; j < 2; ++j) {
       
   560             if (j == 0) {
       
   561                 row = mostRecentRow - i;
       
   562                 if (row <= 0) {
       
   563                     continue;
       
   564                 }
       
   565             }
       
   566             else {
       
   567                 row = mostRecentRow + i;
       
   568                 if (row >= idList.count()) {
       
   569                     continue;
       
   570                 }
       
   571             }
       
   572             
       
   573             int contactId = idList.at(row);
       
   574             if (!mInfoCache.contains(contactId)) {
       
   575                 // contact is not in cache, so put the id to items to read into cache
       
   576                 mReadAheadCache.append(QPair<int,int>(contactId,row));
       
   577             }
       
   578             else {
       
   579                 // contact is in cache; update cache order as we want to keep this item in cache
       
   580                 mInfoCache.value(contactId)->cacheOrder = mNextInfoCacheOrder++;
       
   581             }
       
   582         }
       
   583     }
       
   584 
       
   585    CNT_EXIT
       
   586 }
       
   587 
       
   588 /*! 
       
   589     Schedules one uncached item in the read-ahead list for retrieval.
       
   590  */
       
   591 void CntCache::scheduleOneReadAheadItem()
       
   592 {
       
   593     CNT_ENTRY
       
   594 
       
   595     QString name;
       
   596 
       
   597     // fetch all names from the database if it hasn't been done yet
       
   598     if (!mAllNamesFetchStarted) {
       
   599         mWorker->postponeJobs();
       
   600         mAllNamesFetchStarted = true;
       
   601         mNameFetcher->readAllNamesAsynch();
       
   602     }
       
   603 
       
   604     // pick the first contact from the read ahead cache and schedule it
       
   605     while (mReadAheadCache.count() > 0) {
       
   606         int contactId = mReadAheadCache.first().first;
       
   607         int contactRow = mReadAheadCache.takeFirst().second;
       
   608         if (!mInfoCache.contains(contactId)) {
       
   609             // contact is not in cache, so schedule it for retreival
       
   610             if (contactExists(contactId)) {
       
   611                 // contact found, so add new entry to cache
       
   612                 CntInfoCacheItem* item = createInfoCacheItem(contactId);
       
   613                 item->text = EmptyTextField;
       
   614                 item->latestRow = contactRow;
       
   615     
       
   616                 // schedule the info
       
   617                 mWorker->scheduleInfoJob(contactId, contactRow);
       
   618                 break;
       
   619             }
       
   620         }
       
   621     }
       
   622 
       
   623     CNT_EXIT
       
   624 }
       
   625 
       
   626 /*! 
       
   627     Creates a new item in the info cache. If the cache is full,
       
   628     then the least recently accessed item is removed from cache.
       
   629     
       
   630     /param contactId id of contact for which to create the new cache item
       
   631     /return the newly created cache item
       
   632  */
       
   633 CntInfoCacheItem* CntCache::createInfoCacheItem(int contactId)
       
   634 {
       
   635     CNT_ENTRY_ARGS( contactId )
       
   636 
       
   637     if (mInfoCache.count() >= InfoCacheSize) {
       
   638         // cache is full, so remove the oldest contact
       
   639         int minCacheOrder = mNextInfoCacheOrder;
       
   640         CntInfoCacheItem* oldestItem = NULL;
       
   641         foreach (CntInfoCacheItem* i, mInfoCache) {
       
   642             if (i->cacheOrder < minCacheOrder) {
       
   643                 minCacheOrder = i->cacheOrder;
       
   644                 oldestItem = i;
       
   645             }
       
   646         }
       
   647         
       
   648         if (oldestItem) {
       
   649             mInfoCache.remove(oldestItem->contactId);
       
   650             delete oldestItem;
       
   651         }
       
   652         
       
   653         // cache maintenance: if the cache ids become too large,
       
   654         // reduce all of them by MaxCacheOrderValue
       
   655         if (mNextInfoCacheOrder >= MaxCacheOrderValue) {
       
   656             mNextInfoCacheOrder -=  MaxCacheOrderValue;
       
   657             foreach (CntInfoCacheItem* i, mInfoCache) {
       
   658                 i->cacheOrder -= MaxCacheOrderValue;
       
   659             }
       
   660         }
       
   661     }
       
   662     
       
   663     // create and insert the new item
       
   664     CntInfoCacheItem* item = new CntInfoCacheItem();
       
   665     item->cacheOrder = mNextInfoCacheOrder++;
       
   666     item->contactId = contactId;
       
   667     mInfoCache.insert(contactId, item);
       
   668     
       
   669     CNT_EXIT
       
   670 
       
   671     return item;
       
   672 }
       
   673 
       
   674 /*! 
       
   675     Creates a new item in the icon cache. If the cache is full,
       
   676     then the least recently accessed item is removed from cache.
       
   677     
       
   678     /param iconName name of the icon for which to create the new cache item
       
   679     /return the newly created cache item
       
   680  */
       
   681 CntIconCacheItem* CntCache::createIconCacheItem(const QString& iconName)
       
   682 {
       
   683     CNT_ENTRY_ARGS( iconName )
       
   684 
       
   685     if (mIconCache.count() >= IconCacheSize) {
       
   686         // cache is full, so remove the oldest icon
       
   687         int minCacheOrder = mNextIconCacheOrder;
       
   688         CntIconCacheItem* oldestItem = NULL;
       
   689         foreach (CntIconCacheItem* i, mIconCache) {
       
   690             if (i->cacheOrder < minCacheOrder) {
       
   691                 minCacheOrder = i->cacheOrder;
       
   692                 oldestItem = i;
       
   693             }
       
   694         }
       
   695         mIconCache.remove(oldestItem->iconName);
       
   696         delete oldestItem;
       
   697 
       
   698         // cache maintenance: if the cache orders become too large,
       
   699         // reduce all of them by MaxCacheOrderValue
       
   700         if (mNextIconCacheOrder >= MaxCacheOrderValue) {
       
   701             mNextIconCacheOrder -=  MaxCacheOrderValue;
       
   702             foreach (CntIconCacheItem* i, mIconCache) {
       
   703                 i->cacheOrder -=  MaxCacheOrderValue;
       
   704             }
       
   705         }
       
   706     }
       
   707 
       
   708     // create and insert the new item
       
   709     CntIconCacheItem* item = new CntIconCacheItem();
       
   710     item->cacheOrder = mNextIconCacheOrder++;
       
   711     item->iconName = iconName;
       
   712     item->isFetched = false;
       
   713     mIconCache.insert(iconName, item);
       
   714 
       
   715     CNT_EXIT
       
   716 
       
   717     return item;
       
   718 }
       
   719 
       
   720 /*! 
       
   721     Notifies clients that a contact might have changed.
       
   722     Clients can then request the info via fetchContactInfo() 
       
   723     if they are interested.
       
   724  */
       
   725 void CntCache::emitContactInfoUpdated(int contactId)
       
   726 {
       
   727 	CNT_ENTRY_ARGS( contactId )
       
   728 
       
   729     mEmittedContactId = contactId;
       
   730     emit contactInfoUpdated(contactId);
       
   731     mEmittedContactId = -1;
       
   732 
       
   733 	CNT_EXIT
       
   734 }
       
   735 
       
   736 /*! 
       
   737     Deletes the cache.
       
   738  */
       
   739 void CntCache::onShutdown()
       
   740 {
       
   741 	CNT_ENTRY
       
   742 
       
   743     mInstance = NULL;
       
   744 
       
   745     disconnect(mContactManager, SIGNAL(contactsChanged(const QList<QContactLocalId>&)), this, SLOT(updateContacts(const QList<QContactLocalId>&)));
       
   746     disconnect(mContactManager, SIGNAL(contactsRemoved(const QList<QContactLocalId>&)), this, SLOT(removeContacts(const QList<QContactLocalId>&)));
       
   747     disconnect(mContactManager, SIGNAL(contactsAdded(const QList<QContactLocalId>&)), this, SLOT(addContacts(const QList<QContactLocalId>&)));
       
   748 
       
   749     deleteLater();
       
   750 
       
   751 	CNT_EXIT
       
   752 }
       
   753 
       
   754 /*! 
       
   755     Updates the names in cache according to newFormat.
       
   756 
       
   757     This slot is called when name fetcher signals that the format of
       
   758     names has been changed.
       
   759  */
       
   760 void CntCache::reformatNames(CntNameOrder newFormat)
       
   761 {
       
   762     foreach (CntNameCacheItem* item, mSortedNames) {
       
   763         item->setNameFormat(newFormat);
       
   764     }
       
   765 
       
   766     mNameFetcher->sortNames(mSortedNames);
       
   767 
       
   768     mNameFetcher->writeNamesToCache(mSortedNames);
       
   769     mHasModifiedNames = false;
       
   770 
       
   771     emit dataChanged();
       
   772 }
       
   773 
       
   774 /*! 
       
   775     Replaces the names in cache with the ones in this list.
       
   776     
       
   777     \param newSortedNames the sorted list with names; this list will be cleared and
       
   778                           ownership will be taken of the items in the list
       
   779  */
       
   780 void CntCache::setNameList(QList<CntNameCacheItem *> newSortedNames)
       
   781 {
       
   782     CNT_ENTRY
       
   783     
       
   784     bool hasModifiedContacts = false;
       
   785     int count = newSortedNames.count();
       
   786 
       
   787     // check if there have been any changes
       
   788     if (mSortedNames.count() != count) {
       
   789         hasModifiedContacts = true;
       
   790     } else {
       
   791         for (int i = 0; i < count; ++i) {
       
   792             CntNameCacheItem *oldItem = mSortedNames.at(i);
       
   793             CntNameCacheItem *newItem = newSortedNames.at(i);
       
   794             if (oldItem->contactId() != newItem->contactId() || oldItem->name() != newItem->name()) {
       
   795                 hasModifiedContacts = true;
       
   796                 break;
       
   797             }
       
   798         }
       
   799     }
       
   800 
       
   801     // the list has changed, so use the new list instead
       
   802     if (hasModifiedContacts) {
       
   803         qDeleteAll(mSortedNames);
       
   804         mNameCache.clear();
       
   805         mSortedNames.clear();
       
   806         
       
   807         foreach (CntNameCacheItem* item, newSortedNames) {
       
   808             mSortedNames.append(item);
       
   809             mNameCache.insert(item->contactId(), item);
       
   810         }
       
   811         
       
   812         // write names to file cache
       
   813         mNameFetcher->writeNamesToCache(mSortedNames);
       
   814         
       
   815         // notify clients that the list of names has changed
       
   816         emit dataChanged();
       
   817     } else {
       
   818         qDeleteAll(newSortedNames);
       
   819     }
       
   820     
       
   821     CNT_EXIT
       
   822 }
       
   823 
       
   824 /*! 
       
   825     Updates data in response to some contacts having changed and
       
   826     then notifies observers that these contacts have changed.
       
   827  */
       
   828 void CntCache::updateContacts(const QList<QContactLocalId> &changedContacts)
       
   829 {
       
   830     QString name;
       
   831     QList<CntNameCacheItem*> items;
       
   832 
       
   833     // reloads the names of the changed contacts and updates the
       
   834     // list of sorted names accordingly
       
   835     foreach (QContactLocalId contactId, changedContacts) {
       
   836         CntNameCacheItem *newItem = mNameFetcher->readOneName(contactId);
       
   837         if (newItem != NULL) {
       
   838             CntNameCacheItem *oldItem = mNameCache.value(contactId);
       
   839             if (oldItem->name() != newItem->name()) {
       
   840                 QList<CntNameCacheItem*>::iterator oldPos = qLowerBound(mSortedNames.begin(), mSortedNames.end(), oldItem, CntNameFetcher::compareNames);
       
   841                 while (*oldPos != oldItem && oldPos != mSortedNames.end()) {
       
   842                      ++oldPos;
       
   843                 }
       
   844                 QList<CntNameCacheItem*>::iterator newPos = qUpperBound(mSortedNames.begin(), mSortedNames.end(), newItem, CntNameFetcher::compareNames);
       
   845                 if (oldPos < newPos) {
       
   846                     mSortedNames.move(oldPos - mSortedNames.begin(), (newPos - mSortedNames.begin()) - 1);
       
   847                 } else {
       
   848                     mSortedNames.move(oldPos - mSortedNames.begin(), newPos - mSortedNames.begin());
       
   849                 }
       
   850                 *oldItem = *newItem;
       
   851                 mHasModifiedNames = true;
       
   852             }
       
   853         }
       
   854     }
       
   855 
       
   856     // if any of the changed items have cached info, the info
       
   857     // is scheduled for refreshing
       
   858     foreach (QContactLocalId contactId, changedContacts) {
       
   859         if (mInfoCache.contains(contactId)) {
       
   860             CntInfoCacheItem* infoItem = mInfoCache.value(contactId);
       
   861             mWorker->scheduleInfoJob(contactId, infoItem->latestRow);
       
   862         }
       
   863     }
       
   864 
       
   865     // inform clients about these changes
       
   866     emit contactsChanged(changedContacts);
       
   867 }
       
   868 
       
   869 /*! 
       
   870     Updates data in response to some contacts having been removed
       
   871     and then notifies observers that the contacts have been removed.
       
   872  */
       
   873 void CntCache::removeContacts(const QList<QContactLocalId> &removedContacts)
       
   874 {
       
   875     // removed the deleted contacts from the name cache and from the
       
   876     // list of sorted names
       
   877     foreach (QContactLocalId contactId, removedContacts) {
       
   878         CntNameCacheItem *item = mNameCache.take(contactId);
       
   879         if (item) {
       
   880             QList<CntNameCacheItem*>::iterator pos = qLowerBound(mSortedNames.begin(), mSortedNames.end(), item, CntNameFetcher::compareNames);
       
   881             while (*pos != item && pos != mSortedNames.end()) {
       
   882                 ++pos;
       
   883             }
       
   884             mSortedNames.erase(pos);
       
   885             delete item;
       
   886             mHasModifiedNames = true;
       
   887         }
       
   888     }
       
   889 
       
   890     // info for these deleted items should be removed from cache
       
   891     foreach (QContactLocalId contactId, removedContacts) {
       
   892         if (mInfoCache.contains(contactId)) {
       
   893             CntInfoCacheItem* item = mInfoCache.take(contactId);
       
   894             delete item;
       
   895         }
       
   896     }
       
   897 
       
   898     // inform clients about these deleted contacts
       
   899     emit contactsRemoved(removedContacts);
       
   900 }
       
   901 
       
   902 /*! 
       
   903     Updates data in response to some contacts having been added
       
   904     and then notifies observers that the contacts have been added.
       
   905  */
       
   906 void CntCache::addContacts(const QList<QContactLocalId> &addedContacts)
       
   907 {
       
   908     // add the new contacts to the name cache and to the
       
   909     // list of sorted names
       
   910     foreach (QContactLocalId contactId, addedContacts) {
       
   911         CntNameCacheItem *item = mNameFetcher->readOneName(contactId);
       
   912         if (item != NULL) {
       
   913             mNameCache.insert(contactId, item);
       
   914             QList<CntNameCacheItem*>::iterator i = qUpperBound(mSortedNames.begin(), mSortedNames.end(), item, CntNameFetcher::compareNames);
       
   915             mSortedNames.insert(i, item);
       
   916             mHasModifiedNames = true;
       
   917         }
       
   918     }
       
   919 
       
   920     // inform clients about the new contacts
       
   921     emit contactsAdded(addedContacts);
       
   922 }
       
   923 
       
   924 /*! 
       
   925     Creates an empty CntContactInfo object.
       
   926  */
       
   927 CntContactInfo::CntContactInfo()
       
   928     : d(new CntContactInfoData())
       
   929 {
       
   930 }
       
   931 
       
   932 /*! 
       
   933     Creates a CntContactInfo object with all info fields set.
       
   934  */
       
   935 CntContactInfo::CntContactInfo(int id, const QString& name, const QString& text, const HbIcon& icon1, const HbIcon& icon2)
       
   936     : d(new CntContactInfoData())
       
   937 {
       
   938       d->id = id;
       
   939       d->name = name;
       
   940       d->text = text;
       
   941       d->icon1 = icon1;
       
   942       d->icon2 = icon2;
       
   943 }
       
   944 
       
   945 /*! 
       
   946     Destroys the object.
       
   947  */
       
   948 CntContactInfo::~CntContactInfo()
       
   949 {
       
   950 }
       
   951 
       
   952 /*! 
       
   953     Copy constructor.
       
   954  */
       
   955 CntContactInfo::CntContactInfo(const CntContactInfo& other)
       
   956     : d(other.d)
       
   957 {
       
   958 }
       
   959 
       
   960 /*! 
       
   961     Assignment operator.
       
   962  */
       
   963 CntContactInfo& CntContactInfo::operator=(const CntContactInfo& other)
       
   964 {
       
   965     d = other.d;
       
   966     return *this;
       
   967 }
       
   968 
       
   969 /*! 
       
   970     Getter function for the id.
       
   971  */
       
   972 int CntContactInfo::id() const
       
   973 {
       
   974     return d->id;
       
   975 }
       
   976 
       
   977 /*! 
       
   978     Getter function for the name.
       
   979  */
       
   980 QString CntContactInfo::name() const
       
   981 {
       
   982     return d->name;
       
   983 }
       
   984 
       
   985 /*! 
       
   986     Getter function for the text.
       
   987  */
       
   988 QString CntContactInfo::text() const
       
   989 {
       
   990     return d->text;
       
   991 }
       
   992 
       
   993 /*! 
       
   994     Getter function for the first icon.
       
   995  */
       
   996 HbIcon CntContactInfo::icon1() const
       
   997 {
       
   998     return d->icon1;
       
   999 }
       
  1000 
       
  1001 /*! 
       
  1002     Getter function for the second icon.
       
  1003  */
       
  1004 HbIcon CntContactInfo::icon2() const
       
  1005 {
       
  1006     return d->icon2;
       
  1007 }