File indexing completed on 2024-12-15 04:54:40
0001 /****************************************************************************** 0002 * 0003 * SPDX-FileCopyrightText: 2008 Szymon Tomasz Stefanek <pragma@kvirc.net> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 * 0007 *******************************************************************************/ 0008 0009 #include "core/modelinvariantrowmapper.h" 0010 #include "core/modelinvariantindex_p.h" 0011 #include "core/modelinvariantrowmapper_p.h" 0012 0013 #include <QTime> 0014 #include <QTimer> 0015 0016 #include "messagelist_debug.h" 0017 0018 namespace MessageList 0019 { 0020 namespace Core 0021 { 0022 class RowShift 0023 { 0024 public: 0025 int mMinimumRowIndex; 0026 int mShift; 0027 QHash<int, ModelInvariantIndex *> *mInvariantHash; 0028 0029 public: 0030 RowShift(int minRowIndex, int shift, QHash<int, ModelInvariantIndex *> *invariantHash) 0031 : mMinimumRowIndex(minRowIndex) 0032 , mShift(shift) 0033 , mInvariantHash(invariantHash) 0034 { 0035 } 0036 0037 ~RowShift() 0038 { 0039 for (const auto idx : std::as_const(*mInvariantHash)) { 0040 idx->d->setRowMapper(nullptr); 0041 } 0042 delete mInvariantHash; 0043 } 0044 }; 0045 } // namespace Core 0046 } // namespace MessageList 0047 0048 using namespace MessageList::Core; 0049 0050 ModelInvariantRowMapper::ModelInvariantRowMapper() 0051 : d(new ModelInvariantRowMapperPrivate(this)) 0052 { 0053 d->mRowShiftList = new QList<RowShift *>(); 0054 d->mCurrentShiftSerial = 0; 0055 d->mCurrentInvariantHash = new QHash<int, ModelInvariantIndex *>(); 0056 d->mUpdateTimer = new QTimer(this); 0057 d->mUpdateTimer->setSingleShot(true); 0058 d->mLazyUpdateChunkInterval = 50; 0059 d->mLazyUpdateIdleInterval = 50; 0060 0061 connect(d->mUpdateTimer, &QTimer::timeout, this, [this]() { 0062 d->slotPerformLazyUpdate(); 0063 }); 0064 } 0065 0066 ModelInvariantRowMapper::~ModelInvariantRowMapper() 0067 { 0068 if (d->mUpdateTimer->isActive()) { 0069 d->mUpdateTimer->stop(); 0070 } 0071 0072 // FIXME: optimize this (it CAN be optimized) 0073 for (const auto idx : std::as_const(*d->mCurrentInvariantHash)) { 0074 idx->d->setRowMapper(nullptr); 0075 } 0076 delete d->mCurrentInvariantHash; 0077 0078 if (d->mRowShiftList) { 0079 while (!d->mRowShiftList->isEmpty()) { 0080 delete d->mRowShiftList->takeFirst(); 0081 } 0082 0083 delete d->mRowShiftList; 0084 } 0085 } 0086 0087 void ModelInvariantRowMapperPrivate::killFirstRowShift() 0088 { 0089 RowShift *shift = mRowShiftList->at(0); 0090 0091 Q_ASSERT(shift->mInvariantHash->isEmpty()); 0092 0093 delete shift; 0094 mRowShiftList->removeAt(0); 0095 mRemovedShiftCount++; 0096 if (mRowShiftList->isEmpty()) { 0097 delete mRowShiftList; 0098 mRowShiftList = nullptr; 0099 } 0100 } 0101 0102 void ModelInvariantRowMapperPrivate::indexDead(ModelInvariantIndex *invariant) 0103 { 0104 Q_ASSERT(invariant->d->rowMapper() == q); 0105 0106 if (invariant->d->rowMapperSerial() == mCurrentShiftSerial) { 0107 mCurrentInvariantHash->remove(invariant->d->modelIndexRow()); 0108 return; 0109 } 0110 0111 Q_ASSERT(invariant->d->rowMapperSerial() < mCurrentShiftSerial); 0112 0113 if (!mRowShiftList) { 0114 return; // not found (not requested yet or invalid index at all) 0115 } 0116 0117 uint invariantShiftIndex = invariant->d->rowMapperSerial() - mRemovedShiftCount; 0118 0119 Q_ASSERT(invariantShiftIndex < static_cast<uint>(mRowShiftList->count())); 0120 0121 RowShift *shift = mRowShiftList->at(invariantShiftIndex); 0122 0123 Q_ASSERT(shift); 0124 0125 shift->mInvariantHash->remove(invariant->d->modelIndexRow()); 0126 0127 if ((shift->mInvariantHash->isEmpty()) && (invariantShiftIndex == 0)) { 0128 // no more invariants with serial <= invariant->d->rowMapperSerial() 0129 killFirstRowShift(); 0130 } 0131 } 0132 0133 void ModelInvariantRowMapperPrivate::updateModelInvariantIndex(int modelIndexRow, ModelInvariantIndex *invariantToFill) 0134 { 0135 // Here the invariant already belongs to this mapper. We ASSUME that it's somewhere 0136 // in the history and not in the hash belonging to the current serial. 0137 // modelIndexRow is the CURRENT model index row. 0138 Q_ASSERT(invariantToFill->d->rowMapper() == q); 0139 0140 uint invariantShiftIndex = invariantToFill->d->rowMapperSerial() - mRemovedShiftCount; 0141 0142 Q_ASSERT(invariantShiftIndex < static_cast<uint>(mRowShiftList->count())); 0143 0144 RowShift *shift = mRowShiftList->at(invariantShiftIndex); 0145 0146 int count = shift->mInvariantHash->remove(invariantToFill->d->modelIndexRow()); 0147 0148 Q_ASSERT(count > 0); 0149 Q_UNUSED(count) 0150 0151 // update and make it belong to the current serial 0152 invariantToFill->d->setModelIndexRowAndRowMapperSerial(modelIndexRow, mCurrentShiftSerial); 0153 0154 Q_ASSERT(!mCurrentInvariantHash->contains(invariantToFill->d->modelIndexRow())); 0155 0156 mCurrentInvariantHash->insert(invariantToFill->d->modelIndexRow(), invariantToFill); 0157 0158 if ((shift->mInvariantHash->isEmpty()) && (invariantShiftIndex == 0)) { 0159 // no more invariants with serial <= invariantToFill->rowMapperSerial() 0160 killFirstRowShift(); 0161 } 0162 } 0163 0164 ModelInvariantIndex *ModelInvariantRowMapperPrivate::modelIndexRowToModelInvariantIndexInternal(int modelIndexRow, bool updateIfNeeded) 0165 { 0166 // First of all look it up in the current hash 0167 ModelInvariantIndex *invariant = mCurrentInvariantHash->value(modelIndexRow, nullptr); 0168 if (invariant) { 0169 return invariant; // found: was up to date 0170 } 0171 0172 // Go backward in history by unapplying changes 0173 if (!mRowShiftList) { 0174 return nullptr; // not found (not requested yet or invalid index at all) 0175 } 0176 0177 int idx = mRowShiftList->count(); 0178 if (idx == 0) { 0179 Q_ASSERT(false); 0180 return nullptr; // should never happen (mRowShiftList should have been 0), but well... 0181 } 0182 idx--; 0183 0184 int previousIndexRow = modelIndexRow; 0185 0186 while (idx >= 0) { 0187 RowShift *shift = mRowShiftList->at(idx); 0188 0189 // this shift has taken "previousModelIndexRow" in the historic state 0190 // and has executed: 0191 // 0192 // if ( previousIndexRow >= shift->mMinimumRowIndex ) 0193 // previousIndexRow += shift->mShift; 0194 // 0195 // so inverting it 0196 // 0197 // int potentialPreviousModelIndexRow = modelIndexRow - shift->mShift; 0198 // if ( potentialPreviousModelIndexRow >= shift->mMinimumRowIndex ) 0199 // previousIndexRow = potentialPreviousModelIndexRow; 0200 // 0201 // or by simplifying... 0202 0203 int potentialPreviousModelIndexRow = previousIndexRow - shift->mShift; 0204 if (potentialPreviousModelIndexRow >= shift->mMinimumRowIndex) { 0205 previousIndexRow = potentialPreviousModelIndexRow; 0206 } 0207 0208 invariant = shift->mInvariantHash->value(previousIndexRow, nullptr); 0209 if (invariant) { 0210 // found at this level in history 0211 if (updateIfNeeded) { // update it too 0212 updateModelInvariantIndex(modelIndexRow, invariant); 0213 } 0214 return invariant; 0215 } 0216 0217 idx--; 0218 } 0219 0220 qCWarning(MESSAGELIST_LOG) << "Requested invariant for storage row index " << modelIndexRow << " not found in history"; 0221 return nullptr; // not found in history 0222 } 0223 0224 void ModelInvariantRowMapper::setLazyUpdateChunkInterval(int chunkInterval) 0225 { 0226 d->mLazyUpdateChunkInterval = chunkInterval; 0227 } 0228 0229 void ModelInvariantRowMapper::setLazyUpdateIdleInterval(int idleInterval) 0230 { 0231 d->mLazyUpdateIdleInterval = idleInterval; 0232 } 0233 0234 int ModelInvariantRowMapper::modelInvariantIndexToModelIndexRow(ModelInvariantIndex *invariant) 0235 { 0236 // the invariant shift serial is the serial this mapper 0237 // had at the time it emitted the invariant. 0238 // mRowShiftList at that time had at most invariantShiftSerial items. 0239 Q_ASSERT(invariant); 0240 0241 if (invariant->d->rowMapper() != this) { 0242 return -1; 0243 } 0244 0245 if (invariant->d->rowMapperSerial() == d->mCurrentShiftSerial) { 0246 Q_ASSERT(d->mCurrentInvariantHash->value(invariant->d->modelIndexRow()) == invariant); 0247 return invariant->d->modelIndexRow(); // this invariant was emitted very recently and isn't affected by any change 0248 } 0249 0250 // If RowShift elements weren't removed from the list then 0251 // we should have mCurrentShiftSerial items in the list. 0252 // But RowShifts ARE removed sequentially from the beginning of the list 0253 // as the invariants are updated in the user's data. 0254 // We are making sure that if a RowShift belonging to a certain 0255 // serial is removed from the list then there are no more 0256 // ModelInvariantIndexinstances with that (or a lower) serial around. 0257 // Thus invariantShiftSerial is >= mRemovedShiftCount. 0258 0259 // Example: 0260 // Initial state, no shifts, current serial 0, removed shifts 0 0261 // Emit ModelInvariantIndexfor model index row 6, with serial 0. 0262 // User asks for model index row of invariant that has row index 10 and serial 0. 0263 // The serial is equal to the current serial and we return the row index unchanged. 0264 // A row arrives at position 4 0265 // We add a RowShift with start index 5 and offset +1 0266 // We increase current serial to 1 0267 // User asks for model index row of invariant that has row index 6 with serial 0. 0268 // We compute the first RowShift index as serial 0 - removed 0 = 0 0269 // We apply the row shifts starting at that index. 0270 // That is, since the requested row index is 6 >= 5 0271 // We apply +1 shift and return row index 7 serial 1 0272 // User asks for model index row of invariant that has row index 7 with serial 1 0273 // The serial is equal to the current serial and we return the row index unchanged still with serial 1 0274 // We update all the invariants in the user's data so that 0275 // there are no more invariants with serial 0. 0276 // We remove the RowShift and increase removed shift count to 1 0277 // User asks for model index row of invariant that has row index 7 0278 // The ModelInvariantIndex MUST have at least serial 1 because of the removal step above. 0279 // The serial is equal to the current serial and we return the row index unchanged still with serial 1 0280 // A row arrives at position 2 0281 // We add a RowShift with start index 3 and offset +1 0282 // We increase current serial to 2 0283 // User asks for model index row of invariant that has row index 7 with serial 1. 0284 // We compute the first RowShift index as serial 1 - removed 1 = 0 0285 // We apply the row shifts starting at that index. 0286 // That is, since the requested row index is 7 >= 3 0287 // We apply +1 shift and return row index 8 serial 2 0288 // User asks for model index row of invariant that has row index 8 and serial 2 0289 // The serial is equal to the current serial and we return the row index unchanged still with serial 2 0290 // Etc... 0291 0292 // So if we can trust that the user doesn't mess up with serials 0293 // and the requested serial is not equal to the current serial 0294 // then we can be 100% sure that mRowShiftList is not null (it contains at least one item). 0295 // The requested serial is surely >= than mRemovedShiftCount too. 0296 0297 // To find the starting index of the RowShifts that apply to this 0298 // serial we need to offset them by the removed rows. 0299 0300 uint invariantShiftIndex = invariant->d->rowMapperSerial() - d->mRemovedShiftCount; 0301 0302 Q_ASSERT(d->mRowShiftList); 0303 0304 // For the reasoning above invariantShiftIndex is surely < than mRowShiftList.count() 0305 0306 const uint count = static_cast<uint>(d->mRowShiftList->count()); 0307 0308 Q_ASSERT(invariantShiftIndex < count); 0309 0310 int modelIndexRow = invariant->d->modelIndexRow(); 0311 0312 // apply shifts 0313 for (uint idx = invariantShiftIndex; idx < count; idx++) { 0314 RowShift *shift = d->mRowShiftList->at(idx); 0315 if (modelIndexRow >= shift->mMinimumRowIndex) { 0316 modelIndexRow += shift->mShift; 0317 } 0318 } 0319 0320 // Update the invariant on-the-fly too... 0321 d->updateModelInvariantIndex(modelIndexRow, invariant); 0322 0323 return modelIndexRow; 0324 } 0325 0326 void ModelInvariantRowMapper::createModelInvariantIndex(int modelIndexRow, ModelInvariantIndex *invariantToFill) 0327 { 0328 // The user is athemeg for the invariant of the item that is at the CURRENT modelIndexRow. 0329 Q_ASSERT(invariantToFill->d->rowMapper() == nullptr); 0330 0331 // Plain new invariant. Fill it and add to the current hash. 0332 invariantToFill->d->setModelIndexRowAndRowMapperSerial(modelIndexRow, d->mCurrentShiftSerial); 0333 invariantToFill->d->setRowMapper(this); 0334 0335 Q_ASSERT(!d->mCurrentInvariantHash->contains(modelIndexRow)); 0336 0337 d->mCurrentInvariantHash->insert(modelIndexRow, invariantToFill); 0338 } 0339 0340 ModelInvariantIndex *ModelInvariantRowMapper::modelIndexRowToModelInvariantIndex(int modelIndexRow) 0341 { 0342 return d->modelIndexRowToModelInvariantIndexInternal(modelIndexRow, false); 0343 } 0344 0345 QList<ModelInvariantIndex *> *ModelInvariantRowMapper::modelIndexRowRangeToModelInvariantIndexList(int startIndexRow, int count) 0346 { 0347 if (!d->mRowShiftList) { 0348 if (d->mCurrentInvariantHash->isEmpty()) { 0349 return nullptr; // no invariants emitted, even if rows are changed, no invariant is affected. 0350 } 0351 } 0352 0353 // Find the invariants in range. 0354 // It's somewhat impossible to split this in chunks. 0355 0356 auto invariantList = new QList<ModelInvariantIndex *>(); 0357 0358 const int end = startIndexRow + count; 0359 for (int idx = startIndexRow; idx < end; idx++) { 0360 ModelInvariantIndex *invariant = d->modelIndexRowToModelInvariantIndexInternal(idx, true); 0361 if (invariant) { 0362 invariantList->append(invariant); 0363 } 0364 } 0365 0366 if (invariantList->isEmpty()) { 0367 delete invariantList; 0368 return nullptr; 0369 } 0370 0371 return invariantList; 0372 } 0373 0374 void ModelInvariantRowMapper::modelRowsInserted(int modelIndexRowPosition, int count) 0375 { 0376 // Some rows were added to the model at modelIndexRowPosition. 0377 0378 // FIXME: If rows are added at the end then we don't need any mapping. 0379 // The fact is that we don't know which is the model's end... 0380 // But maybe we can consider the end being the greatest row 0381 // index emitted until now... 0382 0383 if (!d->mRowShiftList) { 0384 if (d->mCurrentInvariantHash->isEmpty()) { 0385 return; // no invariants emitted, even if rows are changed, no invariant is affected. 0386 } 0387 // some invariants might be affected 0388 d->mRowShiftList = new QList<RowShift *>(); 0389 } 0390 0391 RowShift *shift; 0392 0393 if (d->mCurrentInvariantHash->isEmpty()) { 0394 // No invariants updated (all existing are outdated) 0395 0396 Q_ASSERT(d->mRowShiftList->count() > 0); // must be true since it's not null 0397 0398 // Check if we can attach to the last existing shift (very common for consecutive row additions) 0399 shift = d->mRowShiftList->at(d->mRowShiftList->count() - 1); 0400 Q_ASSERT(shift); 0401 0402 if (shift->mShift > 0) { // the shift was positive (addition) 0403 if ((shift->mMinimumRowIndex + shift->mShift) == modelIndexRowPosition) { 0404 // Inserting contiguous blocks of rows, just extend this shift 0405 shift->mShift += count; 0406 Q_ASSERT(d->mUpdateTimer->isActive()); 0407 return; 0408 } 0409 } 0410 } 0411 0412 // FIXME: If we have few items, we can just shift the indexes now. 0413 0414 shift = new RowShift(modelIndexRowPosition, count, d->mCurrentInvariantHash); 0415 d->mRowShiftList->append(shift); 0416 0417 d->mCurrentShiftSerial++; 0418 d->mCurrentInvariantHash = new QHash<int, ModelInvariantIndex *>(); 0419 0420 if (d->mRowShiftList->count() > 7) { // 7 is heuristic 0421 // We start losing performance as the stack is growing too much. 0422 // Start updating NOW and hope we can get it in few sweeps. 0423 0424 if (d->mUpdateTimer->isActive()) { 0425 d->mUpdateTimer->stop(); 0426 } 0427 0428 d->slotPerformLazyUpdate(); 0429 } else { 0430 // Make sure we'll get a lazy update somewhere in the future 0431 if (!d->mUpdateTimer->isActive()) { 0432 d->mUpdateTimer->start(d->mLazyUpdateIdleInterval); 0433 } 0434 } 0435 } 0436 0437 QList<ModelInvariantIndex *> *ModelInvariantRowMapper::modelRowsRemoved(int modelIndexRowPosition, int count) 0438 { 0439 // Some rows were added from the model at modelIndexRowPosition. 0440 0441 // FIXME: If rows are removed from the end, we don't need any mapping. 0442 // The fact is that we don't know which is the model's end... 0443 // But maybe we can consider the end being the greatest row 0444 // index emitted until now... 0445 0446 if (!d->mRowShiftList) { 0447 if (d->mCurrentInvariantHash->isEmpty()) { 0448 return nullptr; // no invariants emitted, even if rows are changed, no invariant is affected. 0449 } 0450 // some invariants might be affected 0451 } 0452 0453 // FIXME: If we have few items, we can just shift the indexes now. 0454 0455 // FIXME: Find a way to "merge" the shifts, if possible 0456 // It OFTEN happens that we remove a lot of items at once (as opposed 0457 // to item addition which is usually an incremental operation). 0458 0459 // FIXME: HUGE PROBLEM 0460 // When the items aren't contiguous or are just out of order it's 0461 // impossible to merge the shifts. Deleting many messages 0462 // generates then a very deep delta stack. Since to delete the 0463 // next message you need to traverse the whole stack, this method 0464 // becomes very slow (maybe not as slow as updating all the indexes 0465 // in the general case, but still *slow*). 0466 // 0467 // So one needs to perform updates while rows are being removed 0468 // but that tends to void all your efforts to not update the 0469 // whole list of items every time... 0470 // 0471 // Also deletions don't seem to be asynchronous (or at least 0472 // they eat all the CPU power available for KMail) so the timers 0473 // don't fire and we're not actually processing the model jobs... 0474 // 0475 // It turns out that deleting many items is just slower than 0476 // reloading the view... 0477 0478 // Invalidate the invariants affected by the change 0479 // In most cases it's a relatively small sweep (and it's done once). 0480 // It's somewhat impossible to split this in chunks. 0481 0482 auto deadInvariants = new QList<ModelInvariantIndex *>(); 0483 0484 const int end = modelIndexRowPosition + count; 0485 for (int idx = modelIndexRowPosition; idx < end; idx++) { 0486 // FIXME: One could optimize this by joining the retrieval and destruction functions 0487 // that is by making a special indexDead( int modelIndex ).. 0488 ModelInvariantIndex *dyingInvariant = d->modelIndexRowToModelInvariantIndexInternal(idx, false); 0489 if (dyingInvariant) { 0490 d->indexDead(dyingInvariant); // will remove from this mapper hashes 0491 dyingInvariant->d->setRowMapper(nullptr); // invalidate! 0492 deadInvariants->append(dyingInvariant); 0493 } else { 0494 // got no dying invariant 0495 qCWarning(MESSAGELIST_LOG) << "Could not find invariant to invalidate at current row " << idx; 0496 } 0497 } 0498 0499 if (!d->mRowShiftList) { 0500 // have no pending shifts, look if we are keeping other invariants 0501 if (d->mCurrentInvariantHash->isEmpty()) { 0502 // no more invariants in this mapper, even if rows are changed, no invariant is affected. 0503 if (deadInvariants->isEmpty()) { 0504 // should never happen, but well... 0505 delete deadInvariants; 0506 return nullptr; 0507 } 0508 return deadInvariants; 0509 } 0510 // still have some invariants inside, must add a shift for them 0511 d->mRowShiftList = new QList<RowShift *>(); 0512 } // else already have shifts 0513 0514 // add a shift for this row removal 0515 auto shift = new RowShift(modelIndexRowPosition + count, -count, d->mCurrentInvariantHash); 0516 d->mRowShiftList->append(shift); 0517 0518 d->mCurrentShiftSerial++; 0519 d->mCurrentInvariantHash = new QHash<int, ModelInvariantIndex *>(); 0520 0521 // trigger updates 0522 if (d->mRowShiftList->count() > 7) { // 7 is heuristic 0523 // We start losing performance as the stack is growing too much. 0524 // Start updating NOW and hope we can get it in few sweeps. 0525 0526 if (d->mUpdateTimer->isActive()) { 0527 d->mUpdateTimer->stop(); 0528 } 0529 0530 d->slotPerformLazyUpdate(); 0531 } else { 0532 // Make sure we'll get a lazy update somewhere in the future 0533 if (!d->mUpdateTimer->isActive()) { 0534 d->mUpdateTimer->start(d->mLazyUpdateIdleInterval); 0535 } 0536 } 0537 0538 if (deadInvariants->isEmpty()) { 0539 // should never happen, but well... 0540 delete deadInvariants; 0541 return nullptr; 0542 } 0543 0544 return deadInvariants; 0545 } 0546 0547 void ModelInvariantRowMapper::modelReset() 0548 { 0549 // FIXME: optimize this (it probably can be optimized by providing a more complex user interface) 0550 0551 for (const auto idx : std::as_const(*d->mCurrentInvariantHash)) { 0552 idx->d->setRowMapper(nullptr); 0553 } 0554 d->mCurrentInvariantHash->clear(); 0555 0556 if (d->mRowShiftList) { 0557 while (!d->mRowShiftList->isEmpty()) { 0558 delete d->mRowShiftList->takeFirst(); 0559 } 0560 0561 delete d->mRowShiftList; 0562 d->mRowShiftList = nullptr; 0563 } 0564 0565 d->mCurrentShiftSerial = 0; 0566 d->mRemovedShiftCount = 0; 0567 } 0568 0569 void ModelInvariantRowMapperPrivate::slotPerformLazyUpdate() 0570 { 0571 // The drawback here is that when one row is removed from the middle (say position 500 of 1000) 0572 // then we require ALL the items to be updated...but: 0573 // 0574 // - We can do it very lazily in the background 0575 // - Optimizing this would mean to ALSO keep the indexes in lists or in a large array 0576 // - The list approach would require to keep the indexes sorted 0577 // so it would cost at least N log (N) / 2.. which is worse than N. 0578 // - We could keep a single (or multiple) array as large as the model 0579 // but then we'd have a large memory consumption and large overhead 0580 // when inserting / removing items from the middle. 0581 // 0582 // So finally I think that the multiple hash approach is a "minimum loss" approach. 0583 0584 QTime startTime = QTime::currentTime(); 0585 0586 int curIndex = 0; 0587 0588 while (mRowShiftList) { 0589 // Have at least one row shift 0590 uint count = static_cast<uint>(mRowShiftList->count()); 0591 0592 // Grab it 0593 RowShift *shift = mRowShiftList->at(0); 0594 0595 // and update the invariants that belong to it 0596 auto it = shift->mInvariantHash->begin(); 0597 auto end = shift->mInvariantHash->end(); 0598 0599 while (it != end) { 0600 ModelInvariantIndex *invariant = *it; 0601 0602 it = shift->mInvariantHash->erase(it); 0603 0604 // apply shifts 0605 int modelIndexRow = invariant->d->modelIndexRow(); 0606 0607 for (uint idx = 0; idx < count; ++idx) { 0608 RowShift *thatShift = mRowShiftList->at(idx); 0609 if (modelIndexRow >= thatShift->mMinimumRowIndex) { 0610 modelIndexRow += thatShift->mShift; 0611 } 0612 } 0613 0614 // update and make it belong to the current serial 0615 invariant->d->setModelIndexRowAndRowMapperSerial(modelIndexRow, mCurrentShiftSerial); 0616 0617 mCurrentInvariantHash->insert(modelIndexRow, invariant); 0618 0619 // once in a while check if we ran out of time 0620 if ((curIndex % 15) == 0) { // 15 is heuristic 0621 int elapsed = startTime.msecsTo(QTime::currentTime()); 0622 if ((elapsed > mLazyUpdateChunkInterval) || (elapsed < 0)) { 0623 // interrupt 0624 // qCDebug(MESSAGELIST_LOG) << "Lazy update fixed " << curIndex << " invariants "; 0625 mUpdateTimer->start(mLazyUpdateIdleInterval); 0626 return; 0627 } 0628 } 0629 0630 curIndex++; 0631 } 0632 0633 // no more invariants with serial <= invariantToFill->rowMapperSerial() 0634 killFirstRowShift(); 0635 } 0636 0637 // qCDebug(MESSAGELIST_LOG) << "Lazy update fixed " << curIndex << " invariants "; 0638 0639 // if we're here then no more work needs to be done. 0640 } 0641 0642 #include "moc_modelinvariantrowmapper.cpp"