You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
641 lines
22 KiB
641 lines
22 KiB
/****************************************************************************** |
|
* |
|
* Copyright 2008 Szymon Tomasz Stefanek <pragma@kvirc.net> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 2 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
* |
|
*******************************************************************************/ |
|
|
|
#include "messagelistview/core/modelinvariantrowmapper.h" |
|
|
|
#include <QTimer> |
|
#include <QTime> |
|
|
|
#include <KDebug> |
|
|
|
namespace KMail |
|
{ |
|
|
|
namespace MessageListView |
|
{ |
|
|
|
namespace Core |
|
{ |
|
|
|
class RowShift |
|
{ |
|
public: |
|
int mMinimumRowIndex; |
|
int mShift; |
|
QHash< int, ModelInvariantIndex * > * mInvariantHash; |
|
|
|
public: |
|
RowShift( int minRowIndex, int shift, QHash< int, ModelInvariantIndex * > * invariantHash ) |
|
: mMinimumRowIndex( minRowIndex ), mShift( shift ), mInvariantHash( invariantHash ) |
|
{ |
|
} |
|
|
|
~RowShift() |
|
{ |
|
for ( QHash< int, ModelInvariantIndex * >::Iterator it = mInvariantHash->begin(); it != mInvariantHash->end(); ++it ) |
|
( *it )->setRowMapper( 0 ); |
|
delete mInvariantHash; |
|
} |
|
}; |
|
|
|
|
|
|
|
ModelInvariantRowMapper::ModelInvariantRowMapper() |
|
{ |
|
mRowShiftList = new QList< RowShift * >(); |
|
mCurrentShiftSerial = 0; |
|
mCurrentInvariantHash = new QHash< int, ModelInvariantIndex * >(); |
|
mUpdateTimer = new QTimer( this ); |
|
mUpdateTimer->setSingleShot( true ); |
|
mLazyUpdateChunkInterval = 50; |
|
mLazyUpdateIdleInterval = 50; |
|
|
|
connect( mUpdateTimer, SIGNAL( timeout() ), |
|
SLOT( slotPerformLazyUpdate() ) ); |
|
} |
|
|
|
ModelInvariantRowMapper::~ModelInvariantRowMapper() |
|
{ |
|
if ( mUpdateTimer->isActive() ) |
|
mUpdateTimer->stop(); |
|
|
|
// FIXME: optimize this (it CAN be optimized) |
|
for ( QHash< int, ModelInvariantIndex * >::Iterator it = mCurrentInvariantHash->begin(); it != mCurrentInvariantHash->end(); ++it ) |
|
( *it )->setRowMapper( 0 ); |
|
delete mCurrentInvariantHash; |
|
|
|
if ( mRowShiftList ) |
|
{ |
|
while ( !mRowShiftList->isEmpty() ) |
|
delete mRowShiftList->takeFirst(); |
|
|
|
delete mRowShiftList; |
|
} |
|
} |
|
|
|
void ModelInvariantRowMapper::killFirstRowShift() |
|
{ |
|
RowShift * shift = mRowShiftList->at( 0 ); |
|
|
|
Q_ASSERT( shift->mInvariantHash->isEmpty() ); |
|
|
|
delete shift; |
|
mRowShiftList->removeAt( 0 ); |
|
mRemovedShiftCount++; |
|
if ( mRowShiftList->count() < 1 ) |
|
{ |
|
delete mRowShiftList; |
|
mRowShiftList = 0; |
|
} |
|
} |
|
|
|
void ModelInvariantRowMapper::indexDead( ModelInvariantIndex * invariant ) |
|
{ |
|
Q_ASSERT( invariant->rowMapper() == this ); |
|
|
|
if ( invariant->rowMapperSerial() == mCurrentShiftSerial ) |
|
{ |
|
int count = mCurrentInvariantHash->remove( invariant->modelIndexRow() ); |
|
Q_ASSERT( count > 0 ); |
|
return; |
|
} |
|
|
|
Q_ASSERT( invariant->rowMapperSerial() < mCurrentShiftSerial ); |
|
|
|
Q_ASSERT( mRowShiftList ); // index has no current serial: we _must_ have a shift for it |
|
|
|
uint invariantShiftIndex = invariant->rowMapperSerial() - mRemovedShiftCount; |
|
|
|
Q_ASSERT( invariantShiftIndex < static_cast< uint >( mRowShiftList->count() ) ); |
|
|
|
RowShift * shift = mRowShiftList->at( invariantShiftIndex ); |
|
|
|
Q_ASSERT( shift ); |
|
|
|
int removed = shift->mInvariantHash->remove( invariant->modelIndexRow() ); |
|
|
|
Q_ASSERT( removed > 0 ); |
|
|
|
if ( ( shift->mInvariantHash->isEmpty() ) && ( invariantShiftIndex == 0 ) ) |
|
{ |
|
// no more invariants with serial <= invariant->rowMapperSerial() |
|
killFirstRowShift(); |
|
} |
|
} |
|
|
|
void ModelInvariantRowMapper::updateModelInvariantIndex( int modelIndexRow, ModelInvariantIndex * invariantToFill ) |
|
{ |
|
// Here the invariant already belongs to this mapper. We ASSUME that it's somewhere |
|
// in the history and not in the hash belonging to the current serial. |
|
// modelIndexRow is the CURRENT model index row. |
|
Q_ASSERT( invariantToFill->rowMapper() == this ); |
|
|
|
uint invariantShiftIndex = invariantToFill->rowMapperSerial() - mRemovedShiftCount; |
|
|
|
Q_ASSERT( invariantShiftIndex < static_cast< uint >( mRowShiftList->count() ) ); |
|
|
|
RowShift * shift = mRowShiftList->at( invariantShiftIndex ); |
|
|
|
int count = shift->mInvariantHash->remove( invariantToFill->modelIndexRow() ); |
|
|
|
Q_ASSERT( count > 0 ); |
|
|
|
// update and make it belong to the current serial |
|
invariantToFill->setModelIndexRowAndRowMapperSerial( modelIndexRow, mCurrentShiftSerial ); |
|
|
|
Q_ASSERT( !mCurrentInvariantHash->contains( invariantToFill->modelIndexRow() ) ); |
|
|
|
mCurrentInvariantHash->insert( invariantToFill->modelIndexRow(), invariantToFill ); |
|
|
|
if ( ( shift->mInvariantHash->isEmpty() ) && ( invariantShiftIndex == 0 ) ) |
|
{ |
|
// no more invariants with serial <= invariantToFill->rowMapperSerial() |
|
killFirstRowShift(); |
|
} |
|
} |
|
|
|
ModelInvariantIndex * ModelInvariantRowMapper::modelIndexRowToModelInvariantIndexInternal( int modelIndexRow, bool updateIfNeeded ) |
|
{ |
|
// First of all look it up in the current hash |
|
ModelInvariantIndex * invariant = mCurrentInvariantHash->value( modelIndexRow, 0 ); |
|
if ( invariant ) |
|
return invariant; // found: was up to date |
|
|
|
// Go backward in history by unapplying changes |
|
if ( !mRowShiftList ) |
|
return 0; // not found (not requested yet or invalid index at all) |
|
|
|
int idx = mRowShiftList->count(); |
|
if ( idx == 0 ) |
|
{ |
|
Q_ASSERT( false ); |
|
return 0; // should never happen (mRowShiftList should have been 0), but well... |
|
} |
|
idx--; |
|
|
|
int previousIndexRow = modelIndexRow; |
|
|
|
while ( idx >= 0 ) |
|
{ |
|
RowShift * shift = mRowShiftList->at( idx ); |
|
|
|
// this shift has taken "previousModelIndexRow" in the historic state |
|
// and has executed: |
|
// |
|
// if ( previousIndexRow >= shift->mMinimumRowIndex ) |
|
// previousIndexRow += shift->mShift; |
|
// |
|
// so inverting it |
|
// |
|
// int potentialPreviousModelIndexRow = modelIndexRow - shift->mShift; |
|
// if ( potentialPreviousModelIndexRow >= shift->mMinimumRowIndex ) |
|
// previousIndexRow = potentialPreviousModelIndexRow; |
|
// |
|
// or by simplyfying... |
|
|
|
int potentialPreviousModelIndexRow = previousIndexRow - shift->mShift; |
|
if ( potentialPreviousModelIndexRow >= shift->mMinimumRowIndex ) |
|
previousIndexRow = potentialPreviousModelIndexRow; |
|
|
|
invariant = shift->mInvariantHash->value( previousIndexRow, 0 ); |
|
if ( invariant ) |
|
{ |
|
// found at this level in history |
|
if ( updateIfNeeded ) // update it too |
|
updateModelInvariantIndex( modelIndexRow, invariant ); |
|
return invariant; |
|
} |
|
|
|
idx--; |
|
} |
|
|
|
kWarning() << "Requested invariant for storage row index " << modelIndexRow << " not found in history" << endl; |
|
return 0; // not found in history |
|
} |
|
|
|
int ModelInvariantRowMapper::modelInvariantIndexToModelIndexRow( ModelInvariantIndex * invariant ) |
|
{ |
|
// the invariant shift serial is the serial this mapper |
|
// had at the time it emitted the invariant. |
|
// mRowShiftList at that time had at most invariantShiftSerial items. |
|
Q_ASSERT( invariant ); |
|
|
|
if ( invariant->rowMapper() != this ) |
|
return -1; |
|
|
|
if ( invariant->rowMapperSerial() == mCurrentShiftSerial ) |
|
{ |
|
Q_ASSERT( mCurrentInvariantHash->value( invariant->modelIndexRow() ) == invariant ); |
|
return invariant->modelIndexRow(); // this invariant was emitted very recently and isn't affected by any change |
|
} |
|
|
|
// If RowShift elements weren't removed from the list then |
|
// we should have mCurrentShiftSerial items in the list. |
|
// But RowShifts ARE removed sequentially from the beginning of the list |
|
// as the invariants are updated in the user's data. |
|
// We are making sure that if a RowShift belonging to a certain |
|
// serial is removed from the list then there are no more |
|
// ModelInvariantIndexinstances with that (or a lower) serial around. |
|
// Thus invariantShiftSerial is >= mRemovedShiftCount. |
|
|
|
// Example: |
|
// Initial state, no shifts, current serial 0, removed shifts 0 |
|
// Emit ModelInvariantIndexfor model index row 6, with serial 0. |
|
// User asks for model index row of invariant that has row index 10 and serial 0. |
|
// The serial is equal to the current serial and we return the row index unchanged. |
|
// A row arrives at position 4 |
|
// We add a RowShift with start index 5 and offset +1 |
|
// We increase current serial to 1 |
|
// User asks for model index row of invariant that has row index 6 with serial 0. |
|
// We compute the first RowShift index as serial 0 - removed 0 = 0 |
|
// We apply the row shifts starting at that index. |
|
// That is, since the requested row index is 6 >= 5 |
|
// We apply +1 shift and return row index 7 serial 1 |
|
// User asks for model index row of invariant that has row index 7 with serial 1 |
|
// The serial is equal to the current serial and we return the row index unchanged still with serial 1 |
|
// We update all the invariants in the user's data so that |
|
// there are no more invariants with serial 0. |
|
// We remove the RowShift and increase removed shift count to 1 |
|
// User asks for model index row of invariant that has row index 7 |
|
// The ModelInvariantIndex MUST have at least serial 1 because of the removal step above. |
|
// The serial is equal to the current serial and we return the row index unchanged still with serial 1 |
|
// A row arrives at position 2 |
|
// We add a RowShift with start index 3 and offset +1 |
|
// We increase current serial to 2 |
|
// User asks for model index row of invariant that has row index 7 with serial 1. |
|
// We compute the first RowShift index as serial 1 - removed 1 = 0 |
|
// We apply the row shifts starting at that index. |
|
// That is, since the requested row index is 7 >= 3 |
|
// We apply +1 shift and return row index 8 serial 2 |
|
// User asks for model index row of invariant that has row index 8 and serial 2 |
|
// The serial is equal to the current serial and we return the row index unchanged still with serial 2 |
|
// Etc... |
|
|
|
// So if we can trust that the user doesn't mess up with serials |
|
// and the requested serial is not equal to the current serial |
|
// then we can be 100% sure that mRowShiftList is not null (it contains at least one item). |
|
// The requested serial is surely >= than mRemovedShiftCount too. |
|
|
|
// To find the starting index of the RowShifts that apply to this |
|
// serial we need to offset them by the removed rows. |
|
|
|
uint invariantShiftIndex = invariant->rowMapperSerial() - mRemovedShiftCount; |
|
|
|
Q_ASSERT( mRowShiftList ); |
|
|
|
// For the reasoning above invariantShiftIndex is surely < than mRowShiftList.count() |
|
|
|
uint count = static_cast< uint >( mRowShiftList->count() ); |
|
|
|
Q_ASSERT( invariantShiftIndex < count ); |
|
|
|
int modelIndexRow = invariant->modelIndexRow(); |
|
|
|
// apply shifts |
|
for ( uint idx = invariantShiftIndex; idx < count; idx++ ) |
|
{ |
|
RowShift * shift = mRowShiftList->at( idx ); |
|
if ( modelIndexRow >= shift->mMinimumRowIndex ) |
|
modelIndexRow += shift->mShift; |
|
} |
|
|
|
// Update the invariant on-the-fly too... |
|
updateModelInvariantIndex( modelIndexRow, invariant ); |
|
|
|
return modelIndexRow; |
|
} |
|
|
|
QList< ModelInvariantIndex * > * ModelInvariantRowMapper::modelIndexRowRangeToModelInvariantIndexList( int startIndexRow, int count ) |
|
{ |
|
if ( !mRowShiftList ) |
|
{ |
|
if ( mCurrentInvariantHash->isEmpty() ) |
|
return 0; // no invariants emitted, even if rows are changed, no invariant is affected. |
|
} |
|
|
|
// Find the invariants in range. |
|
// It's somewhat impossible to split this in chunks. |
|
|
|
QList< ModelInvariantIndex * > * invariantList = new QList< ModelInvariantIndex * >(); |
|
|
|
int end = startIndexRow + count; |
|
for ( int idx = startIndexRow; idx < end; idx++ ) |
|
{ |
|
ModelInvariantIndex * invariant = modelIndexRowToModelInvariantIndexInternal( idx, true ); |
|
if ( invariant ) |
|
invariantList->append( invariant ); |
|
} |
|
|
|
if ( invariantList->isEmpty() ) |
|
{ |
|
delete invariantList; |
|
return 0; |
|
} |
|
|
|
return invariantList; |
|
} |
|
|
|
void ModelInvariantRowMapper::modelRowsInserted( int modelIndexRowPosition, int count ) |
|
{ |
|
// Some rows were added to the model at modelIndexRowPosition. |
|
|
|
// FIXME: If rows are added at the end then we don't need any mapping. |
|
// The fact is that we don't know which is the model's end... |
|
// But maybe we can consider the end being the greatest row |
|
// index emitted until now... |
|
|
|
if ( !mRowShiftList ) |
|
{ |
|
if ( mCurrentInvariantHash->isEmpty() ) |
|
return; // no invariants emitted, even if rows are changed, no invariant is affected. |
|
// some invariants might be affected |
|
mRowShiftList = new QList< RowShift * >(); |
|
} |
|
|
|
RowShift * shift; |
|
|
|
if ( mCurrentInvariantHash->isEmpty() ) |
|
{ |
|
// No invariants updated (all existing are outdated) |
|
|
|
Q_ASSERT( mRowShiftList->count() > 0 ); // must be true since it's not null |
|
|
|
// Check if we can attach to the last existing shift (very common for consecutive row additions) |
|
shift = mRowShiftList->at( mRowShiftList->count() - 1 ); |
|
Q_ASSERT( shift ); |
|
|
|
if ( shift->mShift > 0 ) // the shift was positive (addition) |
|
{ |
|
if ( ( shift->mMinimumRowIndex + shift->mShift ) == modelIndexRowPosition ) |
|
{ |
|
// Inserting contiguous blocks of rows, just extend this shift |
|
shift->mShift += count; |
|
Q_ASSERT( mUpdateTimer->isActive() ); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
// FIXME: If we have few items, we can just shift the indexes now. |
|
|
|
shift = new RowShift( modelIndexRowPosition, count, mCurrentInvariantHash ); |
|
mRowShiftList->append( shift ); |
|
|
|
mCurrentShiftSerial++; |
|
mCurrentInvariantHash = new QHash< int, ModelInvariantIndex * >(); |
|
|
|
if ( mRowShiftList->count() > 7 ) // 7 is heuristic |
|
{ |
|
// We start loosing performance as the stack is growing too much. |
|
// Start updating NOW and hope we can get it in few sweeps. |
|
|
|
if ( mUpdateTimer->isActive() ) |
|
mUpdateTimer->stop(); |
|
|
|
slotPerformLazyUpdate(); |
|
|
|
} else { |
|
// Make sure we'll get a lazy update somewhere in the future |
|
if ( !mUpdateTimer->isActive() ) |
|
mUpdateTimer->start( mLazyUpdateIdleInterval ); |
|
} |
|
} |
|
|
|
QList< ModelInvariantIndex * > * ModelInvariantRowMapper::modelRowsRemoved( int modelIndexRowPosition, int count ) |
|
{ |
|
// Some rows were added from the model at modelIndexRowPosition. |
|
|
|
// FIXME: If rows are removed from the end, we don't need any mapping. |
|
// The fact is that we don't know which is the model's end... |
|
// But maybe we can consider the end being the greatest row |
|
// index emitted until now... |
|
|
|
if ( !mRowShiftList ) |
|
{ |
|
if ( mCurrentInvariantHash->isEmpty() ) |
|
return 0; // no invariants emitted, even if rows are changed, no invariant is affected. |
|
// some invariants might be affected |
|
} |
|
|
|
// FIXME: If we have few items, we can just shift the indexes now. |
|
|
|
// FIXME: Find a way to "merge" the shifts, if possible |
|
// It OFTEN happens that we remove a lot of items at once (as opposed |
|
// to item addition which is usually an incremental operation). |
|
|
|
// FIXME: HUGE PROBLEM |
|
// When the items arent contiguous or are just out of order it's |
|
// impossible to merge the shifts. Deleting many messages |
|
// generates then a very deep delta stack. Since to delete the |
|
// next message you need to traverse the whole stack, this method |
|
// becomes very slow (maybe not as slow as updating all the indexes |
|
// in the general case, but still *slow*). |
|
// |
|
// So one needs to perform updates while rows are being removed |
|
// but that tends to void all your efforts to not update the |
|
// whole list of items every time... |
|
// |
|
// Also deletions don't seem to be asynchronous (or at least |
|
// they eat all the CPU power available for KMail) so the timers |
|
// don't fire and we're not actually processing the model jobs... |
|
// |
|
// It turns out that deleting many items is just slower than |
|
// reloading the view... |
|
|
|
// Invalidate the invariants affected by the change |
|
// In most cases it's a relatively small sweep (and it's done once). |
|
// It's somewhat impossible to split this in chunks. |
|
|
|
QList< ModelInvariantIndex * > * deadInvariants = new QList< ModelInvariantIndex * >(); |
|
|
|
int end = modelIndexRowPosition + count; |
|
for ( int idx = modelIndexRowPosition; idx < end; idx++ ) |
|
{ |
|
// FIXME: One could optimize this by joining the retrieval and destruction functions |
|
// that is by making a special indexDead( int modelIndex ).. |
|
ModelInvariantIndex * dyingInvariant = modelIndexRowToModelInvariantIndexInternal( idx, false ); |
|
if ( dyingInvariant ) |
|
{ |
|
indexDead( dyingInvariant ); // will remove from this mapper hashes |
|
dyingInvariant->setRowMapper( 0 ); // invalidate! |
|
deadInvariants->append( dyingInvariant ); |
|
} else { |
|
// got no dying invariant |
|
kWarning() << "Could not find invariant to invalidate at current row " << idx << endl; |
|
} |
|
} |
|
|
|
if ( !mRowShiftList ) |
|
{ |
|
// have no pending shifts, look if we are keeping other invariants |
|
if ( mCurrentInvariantHash->isEmpty() ) |
|
{ |
|
// no more invariants in this mapper, even if rows are changed, no invariant is affected. |
|
if ( deadInvariants->isEmpty() ) |
|
{ |
|
// should never happen, but well... |
|
delete deadInvariants; |
|
return 0; |
|
} |
|
return deadInvariants; |
|
} |
|
// still have some invariants inside, must add a shift for them |
|
mRowShiftList = new QList< RowShift * >(); |
|
} // else already have shifts |
|
|
|
// add a shift for this row removal |
|
RowShift * shift = new RowShift( modelIndexRowPosition + count, -count, mCurrentInvariantHash ); |
|
mRowShiftList->append( shift ); |
|
|
|
mCurrentShiftSerial++; |
|
mCurrentInvariantHash = new QHash< int, ModelInvariantIndex * >(); |
|
|
|
|
|
// trigger updates |
|
if ( mRowShiftList->count() > 7 ) // 7 is heuristic |
|
{ |
|
// We start loosing performance as the stack is growing too much. |
|
// Start updating NOW and hope we can get it in few sweeps. |
|
|
|
if ( mUpdateTimer->isActive() ) |
|
mUpdateTimer->stop(); |
|
|
|
slotPerformLazyUpdate(); |
|
|
|
} else { |
|
// Make sure we'll get a lazy update somewhere in the future |
|
if ( !mUpdateTimer->isActive() ) |
|
mUpdateTimer->start( mLazyUpdateIdleInterval ); |
|
} |
|
|
|
if ( deadInvariants->isEmpty() ) |
|
{ |
|
// should never happen, but well... |
|
delete deadInvariants; |
|
return 0; |
|
} |
|
|
|
return deadInvariants; |
|
} |
|
|
|
void ModelInvariantRowMapper::modelReset() |
|
{ |
|
// FIXME: optimize this (it probably can be optimized by providing a more complex user interface) |
|
for ( QHash< int, ModelInvariantIndex * >::Iterator it = mCurrentInvariantHash->begin(); it != mCurrentInvariantHash->end(); ++it ) |
|
( *it )->setRowMapper( 0 ); |
|
mCurrentInvariantHash->clear(); |
|
|
|
if ( mRowShiftList ) |
|
{ |
|
while ( !mRowShiftList->isEmpty() ) |
|
delete mRowShiftList->takeFirst(); |
|
|
|
delete mRowShiftList; |
|
mRowShiftList = 0; |
|
} |
|
|
|
mCurrentShiftSerial = 0; |
|
mRemovedShiftCount = 0; |
|
} |
|
|
|
void ModelInvariantRowMapper::slotPerformLazyUpdate() |
|
{ |
|
// The drawback here is that when one row is removed from the middle (say position 500 of 1000) |
|
// then we require ALL the items to be updated...but: |
|
// |
|
// - We can do it very lazily in the background |
|
// - Optimizing this would mean to ALSO keep the indexes in lists or in a large array |
|
// - The list approach would require to keep the indexes sorted |
|
// so it would cost at least N log (N) / 2.. which is worse than N. |
|
// - We could keep a single (or multiple) array as large as the model |
|
// but then we'd have a large memory consumption and large overhead |
|
// when inserting / removing items from the middle. |
|
// |
|
// So finally I think that the multiple hash approach is a "minimum loss" approach. |
|
|
|
QTime startTime = QTime::currentTime(); |
|
|
|
int curIndex = 0; |
|
|
|
while( mRowShiftList ) |
|
{ |
|
// Have at least one row shift |
|
uint count = static_cast< uint >( mRowShiftList->count() ); |
|
|
|
// Grab it |
|
RowShift * shift = mRowShiftList->at( 0 ); |
|
|
|
// and update the invariants that belong to it |
|
QHash< int, ModelInvariantIndex * >::Iterator it = shift->mInvariantHash->begin(); |
|
|
|
while ( it != shift->mInvariantHash->end() ) |
|
{ |
|
ModelInvariantIndex * invariant = *it; |
|
|
|
shift->mInvariantHash->erase( it ); |
|
|
|
// apply shifts |
|
int modelIndexRow = invariant->modelIndexRow(); |
|
|
|
for ( uint idx = 0; idx < count; idx++ ) |
|
{ |
|
RowShift * thatShift = mRowShiftList->at( idx ); |
|
if ( modelIndexRow >= thatShift->mMinimumRowIndex ) |
|
modelIndexRow += thatShift->mShift; |
|
} |
|
|
|
// update and make it belong to the current serial |
|
invariant->setModelIndexRowAndRowMapperSerial( modelIndexRow, mCurrentShiftSerial ); |
|
|
|
mCurrentInvariantHash->insert( modelIndexRow, invariant ); |
|
|
|
// once in a while check if we ran out of time |
|
if ( ( curIndex % 15 ) == 0 ) // 15 is heuristic |
|
{ |
|
int elapsed = startTime.msecsTo( QTime::currentTime() ); |
|
if ( ( elapsed > mLazyUpdateChunkInterval ) || ( elapsed < 0 ) ) |
|
{ |
|
// interrupt |
|
//kDebug() << "Lazy update fixed " << curIndex << " invariants " << endl; |
|
mUpdateTimer->start( mLazyUpdateIdleInterval ); |
|
return; |
|
} |
|
} |
|
|
|
it = shift->mInvariantHash->begin(); |
|
|
|
curIndex++; |
|
} |
|
|
|
// no more invariants with serial <= invariantToFill->rowMapperSerial() |
|
killFirstRowShift(); |
|
} |
|
|
|
//kDebug() << "Lazy update fixed " << curIndex << " invariants " << endl; |
|
|
|
// if we're here then no more work needs to be done. |
|
} |
|
|
|
} // namespace Core |
|
|
|
} // namespace MessageListView |
|
|
|
} // namespace KMail |
|
|
|
|