phonebookengines/cntlistmodel/src/cntcache.cpp
changeset 72 6abfb1094884
parent 66 554fe4dbbb59
equal deleted inserted replaced
67:59984e68247d 72:6abfb1094884
    17 */
    17 */
    18 
    18 
    19 #include <hbapplication.h>
    19 #include <hbapplication.h>
    20 #include <qtcontacts.h>
    20 #include <qtcontacts.h>
    21 #include <qcontactmanager.h>
    21 #include <qcontactmanager.h>
       
    22 #include <QTimer>
       
    23 
    22 #include <cntdebug.h>
    24 #include <cntdebug.h>
    23 #include "cntcache.h"
    25 #include "cntcache.h"
       
    26 #include "cntnamefetcher.h"
    24 #include "cntcache_p.h"
    27 #include "cntcache_p.h"
    25 #include "cntinfoprovider.h"
       
    26 
    28 
    27 // set the singleton instance pointer to NULL
    29 // set the singleton instance pointer to NULL
    28 CntCache* CntCache::mInstance = NULL;
    30 CntCache* CntCache::mInstance = NULL;
    29 
    31 
    30 // value for first cache order to be assigned
    32 // value for first cache order to be assigned
    31 static const int CacheOrderStartValue = 1;
    33 static const int CacheOrderStartValue = 1;
    32 // for avoiding wrap around with cache orders
    34 // for avoiding wrap around with cache orders
    33 static const int MaxCacheOrderValue = 10000000;
    35 static const int MaxCacheOrderValue = 10000000;
    34 // number of items to read quickly when a new instance is requested or cache is cleared
    36 // number of items to read quickly when a new instance is requested or cache is cleared
    35 static const int ItemsToReadUrgently = 12;
    37 static const int ItemsToReadUrgently = 13;
    36 // number of items to read ahead into cache; this number is for one direction
    38 // number of items to read ahead into cache; this number is for one direction
    37 static const int ItemsToCacheAhead = 24;
    39 static const int ItemsToCacheAhead = 24;
    38 // cache size for info items (name, text, icon1name, icon2name)
    40 // cache size for info items (name, text, icon1name, icon2name)
    39 static const int InfoCacheSize = 128;
    41 static const int InfoCacheSize = 128;
    40 // cache size for icon items (iconName and HbIcon)
    42 // cache size for icon items (iconName and HbIcon)
    44 // default empty text info field for a contact; it cannot be empty
    46 // default empty text info field for a contact; it cannot be empty
    45 // as the listview will then ignore it, causing rendering problems
    47 // as the listview will then ignore it, causing rendering problems
    46 static const QString EmptyTextField = " ";
    48 static const QString EmptyTextField = " ";
    47 
    49 
    48 /*!
    50 /*!
    49     Creates the CntCache singleton instance.
       
    50  */
       
    51 CntCache::CntCache()
       
    52     : mContactManager(new QContactManager()),
       
    53       mWorker(new CntCacheThread()),
       
    54       mNextInfoCacheOrder(CacheOrderStartValue),
       
    55       mNextIconCacheOrder(CacheOrderStartValue),
       
    56       mEmittedContactId(-1),
       
    57       mUrgentContacts(0)
       
    58 {
       
    59     CNT_ENTRY
       
    60 
       
    61     // listen to worker updates
       
    62     connect(mWorker, SIGNAL(infoFieldUpdated(int, const ContactInfoField&, const QString&)),
       
    63             this, SLOT(onNewInfo(int, const ContactInfoField&, const QString&)));
       
    64     connect(mWorker, SIGNAL(iconUpdated(const QString&, const HbIcon&)),
       
    65             this, SLOT(onNewIcon(const QString&, const HbIcon&)));
       
    66     connect(mWorker, SIGNAL(infoCancelled(int)), this, SLOT(onInfoCancelled(int)));
       
    67     connect(mWorker, SIGNAL(iconCancelled(const QString&)), this, SLOT(onIconCancelled(const QString&)));
       
    68     connect(mWorker, SIGNAL(allJobsDone()), this, SLOT(scheduleOneReadAheadItem()));
       
    69 
       
    70     // listen to the database for changes to contacts
       
    71     connect(mContactManager, SIGNAL(contactsChanged(const QList<QContactLocalId>&)), this, SLOT(updateContactsInCache(const QList<QContactLocalId>&)));
       
    72     connect(mContactManager, SIGNAL(contactsRemoved(const QList<QContactLocalId>&)), this, SLOT(removeContactsFromCache(const QList<QContactLocalId>&)));
       
    73 
       
    74     // shutdown only when the whole application shuts down
       
    75     connect(HbApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(onShutdown()));
       
    76 
       
    77     CNT_EXIT
       
    78 }
       
    79 
       
    80 /*!
       
    81     Destructs the CntCache singleton instance.
       
    82  */
       
    83 CntCache::~CntCache()
       
    84 {
       
    85     CNT_ENTRY
       
    86 
       
    87     delete mWorker;
       
    88     delete mContactManager;
       
    89     
       
    90     qDeleteAll(mInfoCache);
       
    91     mInfoCache.clear();
       
    92     qDeleteAll(mIconCache);
       
    93     mIconCache.clear();
       
    94 
       
    95     CNT_EXIT
       
    96 }
       
    97 
       
    98 /*!
       
    99     Provides a pointer to the CntCache singleton instance.
    51     Provides a pointer to the CntCache singleton instance.
   100  */
    52  */
   101 CntCache* CntCache::instance()
    53 CntCache* CntCache::instance(QContactManager *manager)
   102 {
    54 {
   103     if (mInstance == NULL) {
    55     if (!mInstance) {
   104         mInstance = new CntCache();
    56         mInstance = new CntCache(manager);
   105     }
    57     }
   106 
    58 
   107     // whenever a client requests an instance the client will want to get all info
    59     // whenever a client requests an instance the client will want to get all info
   108     // for the first couple of contacts (~a screenfull) as fast as possible
    60     // for the first couple of contacts (~a screenfull) as fast as possible
   109     mInstance->mUrgentContacts = ItemsToReadUrgently;
    61     mInstance->mUrgentContacts = ItemsToReadUrgently;
   144         // have set urgencymode on, but in the latter case:
    96         // have set urgencymode on, but in the latter case:
   145         // 1) postpone all jobs so the UI can use as much of the CPU as possible
    97         // 1) postpone all jobs so the UI can use as much of the CPU as possible
   146         // 2) update read ahead cache to contain all IDs of all items near this item
    98         // 2) update read ahead cache to contain all IDs of all items near this item
   147         if (mUrgentContacts > 0) {
    99         if (mUrgentContacts > 0) {
   148             --mUrgentContacts;
   100             --mUrgentContacts;
   149         }
   101         } else {
   150         else {
   102             mWorker->postponeJobs(150);
   151             mWorker->postponeJobs();
       
   152         }
   103         }
   153         updateReadAheadCache(row, idList);
   104         updateReadAheadCache(row, idList);
   154     }
   105     }
   155 
   106 
   156     // fetch contact
   107     // fetch contact
   170                         // notified when the icon is received
   121                         // notified when the icon is received
   171                         iconItem->contactIds.insert(contactId);
   122                         iconItem->contactIds.insert(contactId);
   172                         // also reschedule it
   123                         // also reschedule it
   173                         mWorker->scheduleIconJob(iconName, row);
   124                         mWorker->scheduleIconJob(iconName, row);
   174                     }
   125                     }
   175                 }
   126                 } else {
   176                 else {
       
   177                     // needed icon is not in cache, so schedule it for retrieval
   127                     // needed icon is not in cache, so schedule it for retrieval
   178                     CntIconCacheItem* iconItem = createIconCacheItem(iconName);
   128                     CntIconCacheItem* iconItem = createIconCacheItem(iconName);
   179                     iconItem->contactIds.insert(contactId);
   129                     iconItem->contactIds.insert(contactId);
   180                     mWorker->scheduleIconJob(iconName, row);
   130                     mWorker->scheduleIconJob(iconName, row);
   181                 }
   131                 }
   182             }
   132             }
   183         }
   133         }
   184 
   134 
       
   135         // set return text
       
   136         text = infoItem->text;
       
   137 
   185         // update cache order
   138         // update cache order
   186         infoItem->cacheOrder = mNextInfoCacheOrder++;
   139         infoItem->cacheOrder = mNextInfoCacheOrder++;
   187         infoItem->latestRow = row;
   140         infoItem->latestRow = row;
   188 
   141     } else {
   189         name = infoItem->name;
   142         // the contact info is not in cache, schedule it for retrieval
   190         text = infoItem->text;
   143         if (contactExists(contactId)) {
   191     }
       
   192     else {
       
   193         // the item is not in cache, so fetch the name and schedule the rest
       
   194         // of the info for retrieval
       
   195         if (fetchContactName(contactId, name)) {
       
   196             // contact found, so add new entry to cache
   144             // contact found, so add new entry to cache
   197             CntInfoCacheItem* item = createInfoCacheItem(contactId);
   145             CntInfoCacheItem* item = createInfoCacheItem(contactId);
   198             item->name = name;
       
   199             item->text = text;
   146             item->text = text;
   200             item->latestRow = row;
   147             item->latestRow = row;
   201 
   148 
   202             // ask the worker thread to fetch the information asynchronously
   149             // ask the worker thread to fetch the information asynchronously
   203             mWorker->scheduleInfoJob(contactId, row);
   150             mWorker->scheduleInfoJob(contactId, row);
   204         }
   151         }
   205     }
   152     }
   206 
   153 
       
   154     name = contactName(contactId);
   207     CNT_EXIT_ARGS("name:" << name << "sec:" << text)
   155     CNT_EXIT_ARGS("name:" << name << "sec:" << text)
   208 
   156 
   209     return CntContactInfo(contactId, name, text, icons[0], icons[1]);
   157     return CntContactInfo(contactId, name, text, icons[0], icons[1]);
   210 }
   158 }
   211 
   159 
   212 /*! 
   160 /*! 
   213     Clears the cache of names (not icons). This function can be useful
   161     Creates a list of contact ids sorted according the corresponding contact names.
   214     for example when the format of contact names changes.
   162 
   215  */
   163     \param idFilter the IDs to be returned; if NULL, all contact IDs are returned
   216 void CntCache::clearCache()
   164     \return the list of ids, sorted according the contact name
       
   165  */
       
   166 QList<QContactLocalId> CntCache::sortIdsByName(const QSet<QContactLocalId>* idFilter) const
   217 {
   167 {
   218     CNT_ENTRY
   168     CNT_ENTRY
   219 
   169 
   220     // clear info cache
   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     
   221     qDeleteAll(mInfoCache);
   327     qDeleteAll(mInfoCache);
   222     mInfoCache.clear();
   328     mInfoCache.clear();
   223     mNextInfoCacheOrder = CacheOrderStartValue;
   329 
   224     mUrgentContacts = ItemsToReadUrgently;
   330     qDeleteAll(mIconCache);
       
   331     mIconCache.clear();
       
   332 
       
   333     qDeleteAll(mNameCache);
       
   334     mNameCache.clear();
       
   335     mSortedNames.clear();
   225 
   336 
   226     CNT_EXIT
   337     CNT_EXIT
   227 }
   338 }
   228 
   339 
   229 /*! 
   340 /*! 
   371     }
   482     }
   372 
   483 
   373     CNT_EXIT
   484     CNT_EXIT
   374 }
   485 }
   375 
   486 
   376 /*! 
   487 /*!
   377     Update contacts in cache.
   488     Fetch the names of all contacts.
   378     
   489  */
   379     /param contactIds ids of the contact that will be updated
   490 void CntCache::loadNames()
   380  */
       
   381 void CntCache::updateContactsInCache(const QList<QContactLocalId>& contactIds)
       
   382 {
   491 {
   383     CNT_ENTRY
   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
   384 
   528 
   385     QString name;
   529     QString name;
   386 
   530 
   387     foreach (QContactLocalId contactId, contactIds) {
   531     QHash<QContactLocalId, CntNameCacheItem*>::const_iterator i = mNameCache.find(contactId);
   388         if (mInfoCache.contains(contactId) && fetchContactName(contactId, name)) {
   532     if (i != mNameCache.end()) {
   389             CntInfoCacheItem* infoItem = mInfoCache.value(contactId);
   533         name = i.value()->name();
   390             infoItem->name = name;
   534     }
   391             mWorker->scheduleInfoJob(contactId, infoItem->latestRow);
   535 
   392         }
   536     CNT_EXIT
   393     }
   537 
   394 
   538     return name;
   395     foreach (QContactLocalId contactId, contactIds) {
       
   396         emitContactInfoUpdated(contactId);
       
   397     }
       
   398 
       
   399     CNT_EXIT
       
   400 }
       
   401 
       
   402 /*! 
       
   403     Removes contacts from cache.
       
   404     
       
   405     /param contactIds ids of the contact that will be removed
       
   406  */
       
   407 void CntCache::removeContactsFromCache(const QList<QContactLocalId>& contactIds)
       
   408 {
       
   409     CNT_ENTRY
       
   410 
       
   411     foreach (QContactLocalId contactId, contactIds) {
       
   412         if (mInfoCache.contains(contactId)) {
       
   413             CntInfoCacheItem* item = mInfoCache.take(contactId);
       
   414             delete item;
       
   415         }
       
   416     }
       
   417 
       
   418     foreach (QContactLocalId contactId, contactIds) {
       
   419         emitContactInfoUpdated(contactId);
       
   420     }
       
   421 
       
   422     CNT_EXIT
       
   423 }
       
   424 
       
   425 /*! 
       
   426     Uses an optimized function to fetch the name of a contact from
       
   427     the database.
       
   428 
       
   429     /param contactId the id of the contact to fetch
       
   430     /param contactName the name will be stored here if the function is successful
       
   431     /return true if the name was fetched successfully
       
   432  */
       
   433 bool CntCache::fetchContactName(int contactId, QString& contactName)
       
   434 {
       
   435     CNT_ENTRY_ARGS( contactId )
       
   436 
       
   437     bool foundContact = false;
       
   438 
       
   439     QContactFetchHint nameOnlyFetchHint;
       
   440     /*QStringList details;
       
   441     details << QContactDisplayLabel::DefinitionName;
       
   442     nameOnlyFetchHint.setDetailDefinitionsHint(details);*/
       
   443     nameOnlyFetchHint.setOptimizationHints(QContactFetchHint::NoRelationships);
       
   444     QContact contact = mContactManager->contact(contactId, nameOnlyFetchHint);
       
   445     
       
   446     if (mContactManager->error() == QContactManager::NoError) {
       
   447         contactName = contact.displayLabel();
       
   448         foundContact = true;
       
   449     }
       
   450     
       
   451     CNT_EXIT_ARGS( foundContact )
       
   452     
       
   453     return foundContact;
       
   454 }
   539 }
   455 
   540 
   456 /*! 
   541 /*! 
   457     Collects all contact IDs near the latest fetch from the UI. These will be fetched and
   542     Collects all contact IDs near the latest fetch from the UI. These will be fetched and
   458     precached when UI activity slows down.
   543     precached when UI activity slows down.
   507 {
   592 {
   508     CNT_ENTRY
   593     CNT_ENTRY
   509 
   594 
   510     QString name;
   595     QString name;
   511 
   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 
   512     // pick the first contact from the read ahead cache and schedule it
   604     // pick the first contact from the read ahead cache and schedule it
   513     while (mReadAheadCache.count() > 0) {
   605     while (mReadAheadCache.count() > 0) {
   514         int contactId = mReadAheadCache.first().first;
   606         int contactId = mReadAheadCache.first().first;
   515         int contactRow = mReadAheadCache.takeFirst().second;
   607         int contactRow = mReadAheadCache.takeFirst().second;
   516         if (!mInfoCache.contains(contactId)) {
   608         if (!mInfoCache.contains(contactId)) {
   517             // contact is not in cache, so schedule it for retreival
   609             // contact is not in cache, so schedule it for retreival
   518             if (fetchContactName(contactId, name)) {
   610             if (contactExists(contactId)) {
   519                 // contact found, so add new entry to cache
   611                 // contact found, so add new entry to cache
   520                 CntInfoCacheItem* item = createInfoCacheItem(contactId);
   612                 CntInfoCacheItem* item = createInfoCacheItem(contactId);
   521                 item->name = name;
       
   522                 item->text = EmptyTextField;
   613                 item->text = EmptyTextField;
   523                 item->latestRow = contactRow;
   614                 item->latestRow = contactRow;
   524     
   615     
   525                 // schedule the info for retrieval
   616                 // schedule the info
   526                 mWorker->scheduleInfoJob(contactId, contactRow);
   617                 mWorker->scheduleInfoJob(contactId, contactRow);
   527                 break;
   618                 break;
   528             }
   619             }
   529         }
   620         }
   530     }
   621     }
   648 void CntCache::onShutdown()
   739 void CntCache::onShutdown()
   649 {
   740 {
   650 	CNT_ENTRY
   741 	CNT_ENTRY
   651 
   742 
   652     mInstance = NULL;
   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 
   653     deleteLater();
   749     deleteLater();
   654 
   750 
   655 	CNT_EXIT
   751 	CNT_EXIT
   656 }
   752 }
   657 
   753 
   658 
   754 /*! 
   659 /*! 
   755     Updates the names in cache according to newFormat.
   660     Creates an empty object.
   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.
   661  */
   926  */
   662 CntContactInfo::CntContactInfo()
   927 CntContactInfo::CntContactInfo()
   663     : d(new CntContactInfoData())
   928     : d(new CntContactInfoData())
   664 {
   929 {
   665 }
   930 }
   666 
   931 
   667 /*! 
   932 /*! 
   668     Creates an object with all info fields set.
   933     Creates a CntContactInfo object with all info fields set.
   669  */
   934  */
   670 CntContactInfo::CntContactInfo(int id, const QString& name, const QString& text, const HbIcon& icon1, const HbIcon& icon2)
   935 CntContactInfo::CntContactInfo(int id, const QString& name, const QString& text, const HbIcon& icon1, const HbIcon& icon2)
   671     : d(new CntContactInfoData())
   936     : d(new CntContactInfoData())
   672 {
   937 {
   673       d->id = id;
   938       d->id = id;
   738  */
  1003  */
   739 HbIcon CntContactInfo::icon2() const
  1004 HbIcon CntContactInfo::icon2() const
   740 {
  1005 {
   741     return d->icon2;
  1006     return d->icon2;
   742 }
  1007 }
   743