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