qtcontactsmobility/examples/incomingcalls/qcontactlistmodel.cpp
changeset 25 76a2435edfd4
parent 24 0ba2181d7c28
child 27 de1630741fbe
equal deleted inserted replaced
24:0ba2181d7c28 25:76a2435edfd4
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qcontactlistmodel.h"
       
    43 #include "qcontactlistmodel_p.h"
       
    44 
       
    45 #include "qcontact.h"
       
    46 #include "qcontactmanager.h"
       
    47 #include "qcontactdetails.h"
       
    48 #include "qcontactrequests.h"
       
    49 #include "qcontactfilters.h"
       
    50 
       
    51 /*!
       
    52  * Constructs a new QContactListModel which will request data from the given \a manager
       
    53  * and cache approximately \a cacheSize contacts.
       
    54  *
       
    55  * \sa setManager(), setCacheSize()
       
    56  */
       
    57 QContactListModel::QContactListModel(QContactManager* manager, int cacheSize)
       
    58         : QAbstractListModel(),
       
    59         d(new QContactListModelPrivate)
       
    60 {
       
    61     setCacheSize(cacheSize);
       
    62     setManager(manager);
       
    63 }
       
    64 
       
    65 /*!
       
    66  * Constructs a new copy of the \a other model
       
    67  */
       
    68 QContactListModel::QContactListModel(const QContactListModel& other)
       
    69         : QAbstractListModel(), d(other.d)
       
    70 {
       
    71 }
       
    72 
       
    73 /*!
       
    74  * Assigns this model to be equal to \a other
       
    75  */
       
    76 QContactListModel& QContactListModel::operator=(const QContactListModel& other)
       
    77 {
       
    78     d = other.d;
       
    79     return *this;
       
    80 }
       
    81 
       
    82 
       
    83 /*!
       
    84  * Cleans up any memory in use by the model
       
    85  */
       
    86 QContactListModel::~QContactListModel()
       
    87 {
       
    88 }
       
    89 
       
    90 /*!
       
    91  * Returns a pointer to the manager from which this model requests contact data
       
    92  */
       
    93 QContactManager* QContactListModel::manager() const
       
    94 {
       
    95     return d->m_manager;
       
    96 }
       
    97 
       
    98 /*!
       
    99  * Sets the manager from which this model requests contact data to \a manager.
       
   100  * Any requests made of the old manager will be cancelled and deleted.
       
   101  *
       
   102  * \sa backendChanged()
       
   103  */
       
   104 void QContactListModel::setManager(QContactManager* manager)
       
   105 {
       
   106     // first, cancel and delete any requests made of the old manager
       
   107     QMap<QContactAbstractRequest*, int> updatedRequestCentreRows;
       
   108     QList<QContactAbstractRequest*> requests = d->m_requestCentreRows.keys();
       
   109     for (int i = 0; i < requests.size(); i++) {
       
   110         QContactAbstractRequest* current = requests.at(i);
       
   111         if (current->manager() == d->m_manager) {
       
   112             current->cancel();
       
   113             delete current;
       
   114         } else {
       
   115             updatedRequestCentreRows.insert(current, d->m_requestCentreRows.value(current));
       
   116         }
       
   117     }
       
   118     d->m_requestCentreRows = updatedRequestCentreRows;
       
   119 
       
   120     // secondly, disconnect the signals from the old manager
       
   121     if (d->m_manager)
       
   122         d->m_manager->disconnect(this);
       
   123 
       
   124     // then set up the new manager.
       
   125     d->m_manager = manager;
       
   126     delete d->m_idRequest;
       
   127     d->m_idRequest = new QContactLocalIdFetchRequest;
       
   128     connect(d->m_idRequest, SIGNAL(progress(QContactLocalIdFetchRequest*,bool)), this, SLOT(contactIdFetchRequestProgress(QContactLocalIdFetchRequest*,bool)));
       
   129     d->m_idRequest->setManager(manager);
       
   130     if (manager) {
       
   131         connect(manager, SIGNAL(contactsAdded(QList<QContactLocalId>)), this, SLOT(backendChanged()));
       
   132         connect(manager, SIGNAL(contactsChanged(QList<QContactLocalId>)), this, SLOT(backendChanged()));
       
   133         connect(manager, SIGNAL(contactsRemoved(QList<QContactLocalId>)), this, SLOT(backendChanged()));
       
   134     }
       
   135 
       
   136     // and kick of a request for the ids.
       
   137     backendChanged();
       
   138 }
       
   139 
       
   140 /*!
       
   141  * Returns the number of contacts that this model will cache
       
   142  */
       
   143 int QContactListModel::cacheSize() const
       
   144 {
       
   145     return (d->m_halfCacheSize * 2);
       
   146 }
       
   147 
       
   148 /*!
       
   149  * Sets the number of contacts that this model will cache to be approximately \a size contacts.
       
   150  * The exact size of the cache will be the next higher size which is divisible by 4, or
       
   151  * \a size if \a size is divisible by 4, unless the next higher size would cause integer overflow.
       
   152  * Returns true if the cache size was set successfully, and false if a non-positive \a size was
       
   153  * specified.
       
   154  *
       
   155  * \sa cacheSize()
       
   156  */
       
   157 bool QContactListModel::setCacheSize(int size)
       
   158 {
       
   159     // size will be rounded up to nearest where modulo 4 == 0,
       
   160     // except where doing so would result in integer overflow
       
   161     // (where it will be rounded down)
       
   162     if (size > 0) {
       
   163         // if the cache size is odd, round up to nearest even then test modulo 4
       
   164         // this allows us to cache m_halfCacheSize rows either side of currentRow
       
   165         int modulo4 = size % 4;
       
   166         if (modulo4 == 0) {
       
   167             d->m_halfCacheSize = size / 2;
       
   168             d->m_quarterCacheSize = size / 4;
       
   169         } else {
       
   170             int test = size + (4 - modulo4); // avoid integer overflow.
       
   171             d->m_halfCacheSize = (test < 0 ? (size - modulo4) : (size + 4 - modulo4));
       
   172             d->m_halfCacheSize = d->m_halfCacheSize / 2;
       
   173             d->m_quarterCacheSize = d->m_halfCacheSize / 2;
       
   174         }
       
   175         return true;
       
   176     }
       
   177 
       
   178     return false;
       
   179 }
       
   180 
       
   181 /*!
       
   182  * Returns the policy that the model uses to determine when asynchronous requests should be cleaned up.
       
   183  *
       
   184  * \sa setRequestPolicy()
       
   185  */
       
   186 QContactListModel::AsynchronousRequestPolicy QContactListModel::requestPolicy() const
       
   187 {
       
   188     return d->m_requestPolicy;
       
   189 }
       
   190 
       
   191 /*!
       
   192  * Sets the policy that the model uses to determine when to clean up asynchronous requests to \a policy.
       
   193  *
       
   194  * \sa requestPolicy()
       
   195  */
       
   196 void QContactListModel::setRequestPolicy(QContactListModel::AsynchronousRequestPolicy policy)
       
   197 {
       
   198     d->m_requestPolicy = policy;
       
   199 }
       
   200 
       
   201 /*!
       
   202  * Returns the definition name of the relevant data detail which is cached by the model
       
   203  *
       
   204  * \sa setRelevantDetailDefinitionAndFieldNames()
       
   205  */
       
   206 QString QContactListModel::relevantDefinitionName() const
       
   207 {
       
   208     return d->m_relevantDefinitionName;
       
   209 }
       
   210 
       
   211 /*!
       
   212  * Returns the name of the field of the relevant data detail which is cached by the model
       
   213  *
       
   214  * \sa setRelevantDetailDefinitionAndFieldNames()
       
   215  */
       
   216 QString QContactListModel::relevantFieldName() const
       
   217 {
       
   218     return d->m_relevantFieldName;
       
   219 }
       
   220 
       
   221 /*!
       
   222  * Sets the definition name of the relevant detail which is cached by the model to \a definitionName,
       
   223  * and the name of the field of such details which is cached to \a fieldName.
       
   224  *
       
   225  * \sa relevantDefinitionName(), relevantFieldName()
       
   226  */
       
   227 bool QContactListModel::setRelevantDetailDefinitionAndFieldNames(const QString& definitionName, const QString& fieldName)
       
   228 {
       
   229     if (definitionName.isEmpty() || fieldName.isEmpty())
       
   230         return false;
       
   231 
       
   232     d->m_relevantDefinitionName = definitionName;
       
   233     d->m_relevantFieldName = fieldName;
       
   234     return true;
       
   235 }
       
   236 
       
   237 /*!
       
   238  * \reimp
       
   239  */
       
   240 int QContactListModel::rowCount(const QModelIndex& parent) const
       
   241 {
       
   242     Q_UNUSED(parent);
       
   243     return d->m_rowsToIds.count();
       
   244 }
       
   245 
       
   246 /*!
       
   247  * \reimp
       
   248  */
       
   249 QVariant QContactListModel::data(const QModelIndex& index, int role) const
       
   250 {
       
   251     if (index.row() == -1)
       
   252         return QVariant();
       
   253 
       
   254     d->m_currentRow = index.row();
       
   255     QContact currentContact = d->m_cache.value(d->m_currentRow);
       
   256 
       
   257     // check to see if we need to update our cache
       
   258     bool cacheUpdateRequired = d->m_cache.isEmpty();
       
   259     if ((d->m_halfCacheSize * 2) < d->m_rowsToIds.count()) {
       
   260         // we cannot fit the entire dataset into our cache; calculate window size.
       
   261         int maxActiveCacheRow = d->m_currentRow + d->m_quarterCacheSize;
       
   262         int minActiveCacheRow = d->m_currentRow - d->m_quarterCacheSize;
       
   263         if (maxActiveCacheRow <= d->m_lastCacheCentreRow) {
       
   264             cacheUpdateRequired = true;
       
   265         } else if (minActiveCacheRow >= d->m_lastCacheCentreRow) {
       
   266             cacheUpdateRequired = true;
       
   267         }
       
   268     }
       
   269 
       
   270     if (cacheUpdateRequired) {
       
   271         // the current row will be our new cache centre point.
       
   272         d->m_lastCacheCentreRow = d->m_currentRow;
       
   273 
       
   274         // update our cache - first calculate the new cache window.
       
   275         int lowerBound = d->m_lastCacheCentreRow - d->m_halfCacheSize;
       
   276         int upperBound = d->m_lastCacheCentreRow + d->m_halfCacheSize;
       
   277 
       
   278         // create a list of rows we need to cache
       
   279         QList<int> newCacheRows;
       
   280         if ((upperBound - lowerBound) >= d->m_rowsToIds.count()) {
       
   281             // we can cache the entire dataset.
       
   282             newCacheRows = d->m_rowsToIds.keys();
       
   283         } else {
       
   284             // we can only cache a window of the entire dataset.
       
   285             for (int i = lowerBound; i <= upperBound; i++) {
       
   286                 // wrap-around at top and bottom of cache.
       
   287                 int rowNumber = i;
       
   288                 if (i < 0)
       
   289                     rowNumber += d->m_rowsToIds.count();
       
   290                 if (i >= d->m_rowsToIds.count())
       
   291                     rowNumber -= d->m_rowsToIds.count();
       
   292                 newCacheRows.append(rowNumber);
       
   293             }
       
   294         }
       
   295 
       
   296         // clean up any old requests depending on policy
       
   297         // please note that this branching is _slow_; might be best to remove it
       
   298         // and just always do the default (cancel on cache centrepoint miss)...
       
   299         if (d->m_requestPolicy != QContactListModel::NeverCancelPolicy) {
       
   300             QList<QContactAbstractRequest*> oldRequests = d->m_requestCentreRows.keys();
       
   301             bool cancelRequest = false;
       
   302 
       
   303             // we could pull the conditionals outside the loop for better performance...
       
   304             for (int i = 0; i < oldRequests.size(); i++) {
       
   305                 QContactAbstractRequest* current = oldRequests.at(i);
       
   306                 if (d->m_requestPolicy == QContactListModel::CancelOnCacheUpdatePolicy) {
       
   307                     // immediately cancel since update is required.
       
   308                     cancelRequest = true;
       
   309                 } else if (d->m_requestPolicy == QContactListModel::CancelOnCacheMissPolicy) {
       
   310                     // slow solution... should probably do bounds checking instead of .contains().
       
   311                     if (!newCacheRows.contains(d->m_requestCentreRows.value(current))) {
       
   312                         cancelRequest = true;
       
   313                     }
       
   314                 } else {
       
   315                     int cacheSize = d->m_halfCacheSize * 2;
       
   316                     int requestCentre = d->m_requestCentreRows.value(current);
       
   317                     int requestWindowMax = (requestCentre + d->m_halfCacheSize) % cacheSize;
       
   318                     int requestWindowMin = (requestCentre - d->m_halfCacheSize) % cacheSize;
       
   319                     // slow solution... should probably do bounds checking instead of .contains().
       
   320                     if (!newCacheRows.contains(requestWindowMax) && !newCacheRows.contains(requestWindowMin) && !newCacheRows.contains(requestCentre)) {
       
   321                         cancelRequest = true;
       
   322                     }
       
   323                 }
       
   324 
       
   325                 // cancel (and clean up) the request if required by the policy.
       
   326                 if (cancelRequest) {
       
   327                     current->cancel();
       
   328                     d->m_requestCentreRows.remove(current);
       
   329                     delete current;
       
   330                 }
       
   331 
       
   332                 // reset the control variable.
       
   333                 cancelRequest = false;
       
   334             }
       
   335         }
       
   336 
       
   337         // create "spots" for the cache entries in our cache map
       
   338         QList<int> oldCacheRows = d->m_cache.keys();
       
   339         foreach (int row, newCacheRows) {
       
   340             if (!d->m_cache.contains(row)) {
       
   341                 QContact temp;
       
   342                 QContactName loadingName;
       
   343                 loadingName.setCustomLabel(QString(tr("Loading...")));
       
   344                 temp.saveDetail(&loadingName);
       
   345                 d->m_cache.insert(row, temp);
       
   346             }
       
   347         }
       
   348 
       
   349         // remove any out-of-cache-window contacts we have stored
       
   350         // and remove from the newCacheRows any rows we already have cached
       
   351         foreach (int row, oldCacheRows) {
       
   352             if (row < lowerBound || row > upperBound) {
       
   353                 // don't want to cache this row.
       
   354                 d->m_cache.remove(row);
       
   355             }
       
   356 
       
   357             if (newCacheRows.contains(row)) {
       
   358                 // already have this row in cache.
       
   359                 newCacheRows.removeOne(row);
       
   360             }
       
   361         }
       
   362 
       
   363         // ensure that the current row's contact is cached; if not create a placeholder.
       
   364         if (!d->m_cache.contains(d->m_currentRow)) {
       
   365             QContactName loadingName;
       
   366             loadingName.setCustomLabel(QString(tr("Loading...")));
       
   367             currentContact.saveDetail(&loadingName);
       
   368         }
       
   369 
       
   370         // now fire off an asynchronous request to update our cache
       
   371         QContactFetchRequest* req = new QContactFetchRequest;
       
   372         d->m_requestCentreRows.insert(req, d->m_lastCacheCentreRow);
       
   373         QContactLocalIdFilter idFil;
       
   374         QList<QContactLocalId> newCacheIds;
       
   375         for (int i = 0; i < newCacheRows.size(); i++) {
       
   376             newCacheIds.append(d->m_rowsToIds.value(newCacheRows.at(i)));
       
   377         }
       
   378         idFil.setIds(newCacheIds);
       
   379         req->setFilter(idFil);
       
   380         req->setManager(d->m_manager);
       
   381         connect(req, SIGNAL(progress(QContactFetchRequest*, bool)), this, SLOT(contactFetchRequestProgress(QContactFetchRequest*,bool)));
       
   382         req->start();
       
   383     }
       
   384 
       
   385     // now return the data being requested.
       
   386     QVariant ret;
       
   387     switch (role) {
       
   388         case QContactListModel::DisplayLabelRole:
       
   389         {
       
   390             ret = currentContact.displayLabel();
       
   391         }
       
   392         break;
       
   393 
       
   394         case QContactListModel::IdRole:
       
   395         {
       
   396             ret = currentContact.id().localId();
       
   397         }
       
   398         break;
       
   399 
       
   400         case QContactListModel::AvatarRole:
       
   401         {
       
   402             ret = currentContact.detail(QContactAvatar::DefinitionName).value(QContactAvatar::FieldAvatar);
       
   403         }
       
   404         break;
       
   405 
       
   406         case QContactListModel::PresenceRole:
       
   407         {
       
   408             if (d->m_manager == 0) {
       
   409                 // the manager has not been initialised.
       
   410                 break;
       
   411             }
       
   412 
       
   413             // grab the possible presence values; they should be in order from (unknown) to least present to most present.
       
   414             QContactDetailDefinition presenceDef = d->m_manager->detailDefinition(QContactOnlineAccount::DefinitionName);
       
   415             QList<QVariant> allowablePresenceValues = presenceDef.fields().value(QContactOnlineAccount::FieldPresence).allowableValues();
       
   416             if (presenceDef.isEmpty() || allowablePresenceValues.isEmpty()) {
       
   417                 // the manager does not support presence details.
       
   418                 break;
       
   419             }
       
   420 
       
   421             // calculate the "global presence" of the contact in a naive way.
       
   422             int bestPresenceSoFar = 0; // unknown
       
   423             QList<QContactDetail> presenceDetails = currentContact.details(QContactOnlineAccount::DefinitionName);
       
   424             foreach (const QContactOnlineAccount& pres, presenceDetails) {
       
   425                 int index = allowablePresenceValues.indexOf(pres.presence());
       
   426                 if (index > bestPresenceSoFar) {
       
   427                     bestPresenceSoFar = index;
       
   428                 }
       
   429             }
       
   430 
       
   431             ret = QVariant(allowablePresenceValues.at(bestPresenceSoFar));
       
   432         }
       
   433         break;
       
   434 
       
   435         case QContactListModel::RelevantDataRole:
       
   436         {
       
   437             ret = currentContact.detail(d->m_relevantDefinitionName).value(d->m_relevantFieldName);
       
   438         }
       
   439         break;
       
   440 
       
   441         default:
       
   442         break;
       
   443     }
       
   444 
       
   445     // return the requested data, or a default-constructed QVariant if not available.
       
   446     return ret;
       
   447 }
       
   448 
       
   449 /*!
       
   450  * \reimp
       
   451  */
       
   452 QVariant QContactListModel::headerData(int section, Qt::Orientation orientation, int role) const
       
   453 {
       
   454     Q_UNUSED(section);
       
   455     Q_UNUSED(orientation);
       
   456 
       
   457     QVariant ret;
       
   458     switch (role) {
       
   459         case QContactListModel::DisplayLabelRole:
       
   460         {
       
   461             ret = QString(tr("Name"));
       
   462         }
       
   463         break;
       
   464 
       
   465         case QContactListModel::IdRole:
       
   466         {
       
   467             ret = QString(tr("Id"));
       
   468         }
       
   469         break;
       
   470 
       
   471         case QContactListModel::AvatarRole:
       
   472         {
       
   473             ret = QString(tr("Avatar"));
       
   474         }
       
   475         break;
       
   476 
       
   477         case QContactListModel::PresenceRole:
       
   478         {
       
   479             ret = QString(tr("Presence"));
       
   480         }
       
   481         break;
       
   482 
       
   483         case QContactListModel::RelevantDataRole:
       
   484         {
       
   485             // todo: take this as an argument in setRelevant()
       
   486             ret = QString(tr("Details"));
       
   487         }
       
   488         break;
       
   489 
       
   490         default:
       
   491         break;
       
   492     }
       
   493 
       
   494     return ret;
       
   495 }
       
   496 
       
   497 /*!
       
   498  * \reimp
       
   499  */
       
   500 bool QContactListModel::insertRows(int row, int count, const QModelIndex& parent)
       
   501 {
       
   502     beginInsertRows(parent, row, count);
       
   503 
       
   504     // insertion code here.
       
   505 
       
   506     endInsertRows();
       
   507 
       
   508     return false;
       
   509 }
       
   510 
       
   511 /*!
       
   512  * \reimp
       
   513  */
       
   514 bool QContactListModel::removeRows(int row, int count, const QModelIndex& parent)
       
   515 {
       
   516     beginRemoveRows(parent, row, count);
       
   517 
       
   518     // removal code here.
       
   519 
       
   520     endRemoveRows();
       
   521 
       
   522     return false;
       
   523 }
       
   524 
       
   525 /*!
       
   526  * Returns the row number at which the data of the contact with the given \a contactId is stored, or
       
   527  * -1 if no such contact exists in the model
       
   528  */
       
   529 int QContactListModel::contactRow(const QContactLocalId& contactId) const
       
   530 {
       
   531     return d->m_idsToRows.value(contactId, -1);
       
   532 }
       
   533 
       
   534 /*!
       
   535  * Returns the entire contact which exists in the model at the specified \a index
       
   536  */
       
   537 QContact QContactListModel::contact(const QModelIndex& index) const
       
   538 {
       
   539     if (d->m_manager)
       
   540         return d->m_manager->contact(d->m_rowsToIds.value(index.row()));
       
   541     return QContact();
       
   542 }
       
   543 
       
   544 /*!
       
   545  * Processes the progress of the \a request.
       
   546  * If the request is still valid, the results are placed in the cache at the required positions.
       
   547  * If the cache is updated, the dataChanged() signal is emitted.
       
   548  * This implementation ignores the \a appendOnly flag.
       
   549  */
       
   550 void QContactListModel::contactFetchRequestProgress(QContactFetchRequest* request, bool appendOnly)
       
   551 {
       
   552     Q_UNUSED(appendOnly);
       
   553 
       
   554     // first, check to make sure that the request is still valid.
       
   555     if (d->m_manager != request->manager() || request->status() == QContactAbstractRequest::Cancelled) {
       
   556         d->m_requestCentreRows.remove(request);
       
   557         delete request;
       
   558         return; // ignore these results.
       
   559     }
       
   560 
       
   561     QMap<int, int> rowMap; // sorted list of rows changed.
       
   562     QList<QContact> fetched = request->contacts();
       
   563     foreach (const QContact& c, fetched) {
       
   564         int fetchedRow = d->m_idsToRows.value(c.id().localId(), -1);
       
   565 
       
   566         // see if this row should be cached
       
   567         if (!d->m_cache.contains(fetchedRow))
       
   568             break; // shouldn't cache this row (or already cached); ignore the contact.
       
   569 
       
   570         // we need to cache this contact.
       
   571         d->m_cache.insert(fetchedRow, c);
       
   572         rowMap.insert(fetchedRow, fetchedRow);
       
   573     }
       
   574 
       
   575     // check to see if the request status is "finished" - clean up.
       
   576     if (request->status() == QContactAbstractRequest::Finished) {
       
   577         d->m_requestCentreRows.remove(request);
       
   578         delete request;
       
   579     }
       
   580 
       
   581     // emit data changed for those that have changed.
       
   582     QList<int> rows = rowMap.keys();
       
   583     while (rows.size() > 0) {
       
   584         // we want to emit the dataChanged signal as few times as possible
       
   585         // so, we coalesce the changes into lumps of contiguous changes.
       
   586         int lowestIndex = rows.at(0);
       
   587         int highestIndex = rows.at(0);
       
   588         int nbrAccountedFor = 1;
       
   589         int nbrRows = rows.size();
       
   590         while (nbrAccountedFor < nbrRows) {
       
   591             int temp = highestIndex;
       
   592             highestIndex = rows.at(nbrAccountedFor);
       
   593             if ((highestIndex - temp) > 1) {
       
   594                 highestIndex = temp;
       
   595                 break;
       
   596             }
       
   597             nbrAccountedFor += 1;
       
   598         }
       
   599 
       
   600         while (nbrAccountedFor > 0) {
       
   601             rows.removeFirst();
       
   602             nbrAccountedFor -= 1;
       
   603         }
       
   604 
       
   605         // calculate the indices of the boundaries, and emit the signal.
       
   606         QModelIndex lowerBound = QAbstractItemModel::createIndex(lowestIndex, 0);
       
   607         QModelIndex upperBound = QAbstractItemModel::createIndex(highestIndex, 0);
       
   608         emit dataChanged(lowerBound, upperBound);
       
   609     }
       
   610 }
       
   611 
       
   612 /*!
       
   613  * Processes the results of a contact id fetch request.
       
   614  * If the \a appendOnly flag is set, the new data is appended to the existing data
       
   615  * and the dataChanged() signal is emitted; otherwise, the model emits the reset() signal
       
   616  * once the new data has been loaded.
       
   617  */
       
   618 void QContactListModel::contactIdFetchRequestProgress(QContactLocalIdFetchRequest* request, bool appendOnly)
       
   619 {
       
   620     // first, if it's not append only, we need to rebuild the entire list + cache.
       
   621     if (!appendOnly) {
       
   622         d->m_cache.clear();
       
   623         d->m_rowsToIds.clear();
       
   624         d->m_idsToRows.clear();
       
   625     }
       
   626 
       
   627     // then get the results, calculate the start and end indices, and fill our data structures.
       
   628     QList<QContactLocalId> ids = request->ids();
       
   629     int startIndex = d->m_idsToRows.count();
       
   630     int endIndex = ids.size();
       
   631     for (int i = startIndex; i < endIndex; i++) {
       
   632         d->m_rowsToIds.insert(i, ids.at(i));
       
   633         d->m_idsToRows.insert(ids.at(i), i);
       
   634     }
       
   635 
       
   636     // and if we need to, emit the reset signals.
       
   637     if (!appendOnly)
       
   638         reset();
       
   639     else
       
   640         emit dataChanged(QAbstractItemModel::createIndex(startIndex,0), QAbstractItemModel::createIndex(endIndex,0));
       
   641 }
       
   642 
       
   643 /*!
       
   644  * Requests data from the new backend.
       
   645  */
       
   646 void QContactListModel::backendChanged()
       
   647 {
       
   648     d->m_idRequest->start();
       
   649     d->m_idRequest->waitForFinished();
       
   650 }