• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

messagelist

  • sources
  • kde-4.12
  • kdepim
  • messagelist
  • core
modelinvariantrowmapper.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  *
3  * Copyright 2008 Szymon Tomasz Stefanek <pragma@kvirc.net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  *******************************************************************************/
20 
21 #include "core/modelinvariantrowmapper.h"
22 #include "core/modelinvariantrowmapper_p.h"
23 #include "core/modelinvariantindex_p.h"
24 
25 #include <QTimer>
26 #include <QTime>
27 
28 #include <KDebug>
29 
30 namespace MessageList
31 {
32 
33 namespace Core
34 {
35 
36 class RowShift
37 {
38 public:
39  int mMinimumRowIndex;
40  int mShift;
41  QHash< int, ModelInvariantIndex * > * mInvariantHash;
42 
43 public:
44  RowShift( int minRowIndex, int shift, QHash< int, ModelInvariantIndex * > * invariantHash )
45  : mMinimumRowIndex( minRowIndex ), mShift( shift ), mInvariantHash( invariantHash )
46  {
47  }
48 
49  ~RowShift()
50  {
51  QHash< int, ModelInvariantIndex * >::ConstIterator end( mInvariantHash->constEnd() );
52  for ( QHash< int, ModelInvariantIndex * >::ConstIterator it = mInvariantHash->constBegin(); it != end; ++it )
53  ( *it )->d->setRowMapper( 0 );
54  delete mInvariantHash;
55  }
56 };
57 
58 } // namespace Core
59 
60 } // namespace MessageList
61 
62 using namespace MessageList::Core;
63 
64 ModelInvariantRowMapper::ModelInvariantRowMapper()
65  : d( new ModelInvariantRowMapperPrivate( this ) )
66 {
67  d->mRowShiftList = new QList< RowShift * >();
68  d->mCurrentShiftSerial = 0;
69  d->mCurrentInvariantHash = new QHash< int, ModelInvariantIndex * >();
70  d->mUpdateTimer = new QTimer( this );
71  d->mUpdateTimer->setSingleShot( true );
72  d->mLazyUpdateChunkInterval = 50;
73  d->mLazyUpdateIdleInterval = 50;
74 
75  connect( d->mUpdateTimer, SIGNAL(timeout()),
76  SLOT(slotPerformLazyUpdate()) );
77 }
78 
79 ModelInvariantRowMapper::~ModelInvariantRowMapper()
80 {
81  if ( d->mUpdateTimer->isActive() )
82  d->mUpdateTimer->stop();
83 
84  // FIXME: optimize this (it CAN be optimized)
85  QHash< int, ModelInvariantIndex * >::ConstIterator end( d->mCurrentInvariantHash->constEnd() );
86  for ( QHash< int, ModelInvariantIndex * >::ConstIterator it = d->mCurrentInvariantHash->constBegin(); it != end; ++it )
87  ( *it )->d->setRowMapper( 0 );
88  delete d->mCurrentInvariantHash;
89 
90  if ( d->mRowShiftList )
91  {
92  while ( !d->mRowShiftList->isEmpty() )
93  delete d->mRowShiftList->takeFirst();
94 
95  delete d->mRowShiftList;
96  }
97 
98  delete d;
99 }
100 
101 void ModelInvariantRowMapperPrivate::killFirstRowShift()
102 {
103  RowShift * shift = mRowShiftList->at( 0 );
104 
105  Q_ASSERT( shift->mInvariantHash->isEmpty() );
106 
107  delete shift;
108  mRowShiftList->removeAt( 0 );
109  mRemovedShiftCount++;
110  if ( mRowShiftList->isEmpty() )
111  {
112  delete mRowShiftList;
113  mRowShiftList = 0;
114  }
115 }
116 
117 void ModelInvariantRowMapperPrivate::indexDead( ModelInvariantIndex * invariant )
118 {
119  Q_ASSERT( invariant->d->rowMapper() == q );
120 
121  if ( invariant->d->rowMapperSerial() == mCurrentShiftSerial )
122  {
123  mCurrentInvariantHash->remove( invariant->d->modelIndexRow() );
124  return;
125  }
126 
127  Q_ASSERT( invariant->d->rowMapperSerial() < mCurrentShiftSerial );
128 
129  if ( !mRowShiftList ) {
130  return; // not found (not requested yet or invalid index at all)
131  }
132 
133  uint invariantShiftIndex = invariant->d->rowMapperSerial() - mRemovedShiftCount;
134 
135  Q_ASSERT( invariantShiftIndex < static_cast< uint >( mRowShiftList->count() ) );
136 
137  RowShift * shift = mRowShiftList->at( invariantShiftIndex );
138 
139  Q_ASSERT( shift );
140 
141  shift->mInvariantHash->remove( invariant->d->modelIndexRow() );
142 
143  if ( ( shift->mInvariantHash->isEmpty() ) && ( invariantShiftIndex == 0 ) )
144  {
145  // no more invariants with serial <= invariant->d->rowMapperSerial()
146  killFirstRowShift();
147  }
148 }
149 
150 void ModelInvariantRowMapperPrivate::updateModelInvariantIndex( int modelIndexRow, ModelInvariantIndex * invariantToFill )
151 {
152  // Here the invariant already belongs to this mapper. We ASSUME that it's somewhere
153  // in the history and not in the hash belonging to the current serial.
154  // modelIndexRow is the CURRENT model index row.
155  Q_ASSERT( invariantToFill->d->rowMapper() == q );
156 
157  uint invariantShiftIndex = invariantToFill->d->rowMapperSerial() - mRemovedShiftCount;
158 
159  Q_ASSERT( invariantShiftIndex < static_cast< uint >( mRowShiftList->count() ) );
160 
161  RowShift * shift = mRowShiftList->at( invariantShiftIndex );
162 
163  int count = shift->mInvariantHash->remove( invariantToFill->d->modelIndexRow() );
164 
165  Q_ASSERT( count > 0 );
166  Q_UNUSED( count );
167 
168  // update and make it belong to the current serial
169  invariantToFill->d->setModelIndexRowAndRowMapperSerial( modelIndexRow, mCurrentShiftSerial );
170 
171  Q_ASSERT( !mCurrentInvariantHash->contains( invariantToFill->d->modelIndexRow() ) );
172 
173  mCurrentInvariantHash->insert( invariantToFill->d->modelIndexRow(), invariantToFill );
174 
175  if ( ( shift->mInvariantHash->isEmpty() ) && ( invariantShiftIndex == 0 ) )
176  {
177  // no more invariants with serial <= invariantToFill->rowMapperSerial()
178  killFirstRowShift();
179  }
180 }
181 
182 ModelInvariantIndex * ModelInvariantRowMapperPrivate::modelIndexRowToModelInvariantIndexInternal( int modelIndexRow, bool updateIfNeeded )
183 {
184  // First of all look it up in the current hash
185  ModelInvariantIndex * invariant = mCurrentInvariantHash->value( modelIndexRow, 0 );
186  if ( invariant )
187  return invariant; // found: was up to date
188 
189  // Go backward in history by unapplying changes
190  if ( !mRowShiftList )
191  return 0; // not found (not requested yet or invalid index at all)
192 
193  int idx = mRowShiftList->count();
194  if ( idx == 0 )
195  {
196  Q_ASSERT( false );
197  return 0; // should never happen (mRowShiftList should have been 0), but well...
198  }
199  idx--;
200 
201  int previousIndexRow = modelIndexRow;
202 
203  while ( idx >= 0 )
204  {
205  RowShift * shift = mRowShiftList->at( idx );
206 
207  // this shift has taken "previousModelIndexRow" in the historic state
208  // and has executed:
209  //
210  // if ( previousIndexRow >= shift->mMinimumRowIndex )
211  // previousIndexRow += shift->mShift;
212  //
213  // so inverting it
214  //
215  // int potentialPreviousModelIndexRow = modelIndexRow - shift->mShift;
216  // if ( potentialPreviousModelIndexRow >= shift->mMinimumRowIndex )
217  // previousIndexRow = potentialPreviousModelIndexRow;
218  //
219  // or by simplyfying...
220 
221  int potentialPreviousModelIndexRow = previousIndexRow - shift->mShift;
222  if ( potentialPreviousModelIndexRow >= shift->mMinimumRowIndex )
223  previousIndexRow = potentialPreviousModelIndexRow;
224 
225  invariant = shift->mInvariantHash->value( previousIndexRow, 0 );
226  if ( invariant )
227  {
228  // found at this level in history
229  if ( updateIfNeeded ) // update it too
230  updateModelInvariantIndex( modelIndexRow, invariant );
231  return invariant;
232  }
233 
234  idx--;
235  }
236 
237  kWarning() << "Requested invariant for storage row index "
238  << modelIndexRow << " not found in history";
239  return 0; // not found in history
240 }
241 
242 void ModelInvariantRowMapper::setLazyUpdateChunkInterval( int chunkInterval )
243 {
244  d->mLazyUpdateChunkInterval = chunkInterval;
245 }
246 
247 void ModelInvariantRowMapper::setLazyUpdateIdleInterval( int idleInterval )
248 {
249  d->mLazyUpdateIdleInterval = idleInterval;
250 }
251 
252 int ModelInvariantRowMapper::modelInvariantIndexToModelIndexRow( ModelInvariantIndex * invariant )
253 {
254  // the invariant shift serial is the serial this mapper
255  // had at the time it emitted the invariant.
256  // mRowShiftList at that time had at most invariantShiftSerial items.
257  Q_ASSERT( invariant );
258 
259  if ( invariant->d->rowMapper() != this )
260  return -1;
261 
262  if ( invariant->d->rowMapperSerial() == d->mCurrentShiftSerial )
263  {
264  Q_ASSERT( d->mCurrentInvariantHash->value( invariant->d->modelIndexRow() ) == invariant );
265  return invariant->d->modelIndexRow(); // this invariant was emitted very recently and isn't affected by any change
266  }
267 
268  // If RowShift elements weren't removed from the list then
269  // we should have mCurrentShiftSerial items in the list.
270  // But RowShifts ARE removed sequentially from the beginning of the list
271  // as the invariants are updated in the user's data.
272  // We are making sure that if a RowShift belonging to a certain
273  // serial is removed from the list then there are no more
274  // ModelInvariantIndexinstances with that (or a lower) serial around.
275  // Thus invariantShiftSerial is >= mRemovedShiftCount.
276 
277  // Example:
278  // Initial state, no shifts, current serial 0, removed shifts 0
279  // Emit ModelInvariantIndexfor model index row 6, with serial 0.
280  // User asks for model index row of invariant that has row index 10 and serial 0.
281  // The serial is equal to the current serial and we return the row index unchanged.
282  // A row arrives at position 4
283  // We add a RowShift with start index 5 and offset +1
284  // We increase current serial to 1
285  // User asks for model index row of invariant that has row index 6 with serial 0.
286  // We compute the first RowShift index as serial 0 - removed 0 = 0
287  // We apply the row shifts starting at that index.
288  // That is, since the requested row index is 6 >= 5
289  // We apply +1 shift and return row index 7 serial 1
290  // User asks for model index row of invariant that has row index 7 with serial 1
291  // The serial is equal to the current serial and we return the row index unchanged still with serial 1
292  // We update all the invariants in the user's data so that
293  // there are no more invariants with serial 0.
294  // We remove the RowShift and increase removed shift count to 1
295  // User asks for model index row of invariant that has row index 7
296  // The ModelInvariantIndex MUST have at least serial 1 because of the removal step above.
297  // The serial is equal to the current serial and we return the row index unchanged still with serial 1
298  // A row arrives at position 2
299  // We add a RowShift with start index 3 and offset +1
300  // We increase current serial to 2
301  // User asks for model index row of invariant that has row index 7 with serial 1.
302  // We compute the first RowShift index as serial 1 - removed 1 = 0
303  // We apply the row shifts starting at that index.
304  // That is, since the requested row index is 7 >= 3
305  // We apply +1 shift and return row index 8 serial 2
306  // User asks for model index row of invariant that has row index 8 and serial 2
307  // The serial is equal to the current serial and we return the row index unchanged still with serial 2
308  // Etc...
309 
310  // So if we can trust that the user doesn't mess up with serials
311  // and the requested serial is not equal to the current serial
312  // then we can be 100% sure that mRowShiftList is not null (it contains at least one item).
313  // The requested serial is surely >= than mRemovedShiftCount too.
314 
315  // To find the starting index of the RowShifts that apply to this
316  // serial we need to offset them by the removed rows.
317 
318  uint invariantShiftIndex = invariant->d->rowMapperSerial() - d->mRemovedShiftCount;
319 
320  Q_ASSERT( d->mRowShiftList );
321 
322  // For the reasoning above invariantShiftIndex is surely < than mRowShiftList.count()
323 
324  const uint count = static_cast< uint >( d->mRowShiftList->count() );
325 
326  Q_ASSERT( invariantShiftIndex < count );
327 
328  int modelIndexRow = invariant->d->modelIndexRow();
329 
330  // apply shifts
331  for ( uint idx = invariantShiftIndex; idx < count; idx++ )
332  {
333  RowShift * shift = d->mRowShiftList->at( idx );
334  if ( modelIndexRow >= shift->mMinimumRowIndex )
335  modelIndexRow += shift->mShift;
336  }
337 
338  // Update the invariant on-the-fly too...
339  d->updateModelInvariantIndex( modelIndexRow, invariant );
340 
341  return modelIndexRow;
342 }
343 
344 void ModelInvariantRowMapper::createModelInvariantIndex( int modelIndexRow, ModelInvariantIndex * invariantToFill )
345 {
346  // The user is athemeg for the invariant of the item that is at the CURRENT modelIndexRow.
347  Q_ASSERT( invariantToFill->d->rowMapper() == 0 );
348 
349  // Plain new invariant. Fill it and add to the current hash.
350  invariantToFill->d->setModelIndexRowAndRowMapperSerial( modelIndexRow, d->mCurrentShiftSerial );
351  invariantToFill->d->setRowMapper( this );
352 
353  Q_ASSERT( !d->mCurrentInvariantHash->contains( modelIndexRow ) );
354 
355  d->mCurrentInvariantHash->insert( modelIndexRow, invariantToFill );
356 }
357 
358 ModelInvariantIndex *ModelInvariantRowMapper::modelIndexRowToModelInvariantIndex( int modelIndexRow )
359 {
360  return d->modelIndexRowToModelInvariantIndexInternal( modelIndexRow, false );
361 }
362 
363 QList< ModelInvariantIndex * > * ModelInvariantRowMapper::modelIndexRowRangeToModelInvariantIndexList( int startIndexRow, int count )
364 {
365  if ( !d->mRowShiftList )
366  {
367  if ( d->mCurrentInvariantHash->isEmpty() )
368  return 0; // no invariants emitted, even if rows are changed, no invariant is affected.
369  }
370 
371  // Find the invariants in range.
372  // It's somewhat impossible to split this in chunks.
373 
374  QList< ModelInvariantIndex * > * invariantList = new QList< ModelInvariantIndex * >();
375 
376  const int end = startIndexRow + count;
377  for ( int idx = startIndexRow; idx < end; idx++ )
378  {
379  ModelInvariantIndex * invariant = d->modelIndexRowToModelInvariantIndexInternal( idx, true );
380  if ( invariant )
381  invariantList->append( invariant );
382  }
383 
384  if ( invariantList->isEmpty() )
385  {
386  delete invariantList;
387  return 0;
388  }
389 
390  return invariantList;
391 }
392 
393 void ModelInvariantRowMapper::modelRowsInserted( int modelIndexRowPosition, int count )
394 {
395  // Some rows were added to the model at modelIndexRowPosition.
396 
397  // FIXME: If rows are added at the end then we don't need any mapping.
398  // The fact is that we don't know which is the model's end...
399  // But maybe we can consider the end being the greatest row
400  // index emitted until now...
401 
402  if ( !d->mRowShiftList )
403  {
404  if ( d->mCurrentInvariantHash->isEmpty() )
405  return; // no invariants emitted, even if rows are changed, no invariant is affected.
406  // some invariants might be affected
407  d->mRowShiftList = new QList< RowShift * >();
408  }
409 
410  RowShift * shift;
411 
412  if ( d->mCurrentInvariantHash->isEmpty() )
413  {
414  // No invariants updated (all existing are outdated)
415 
416  Q_ASSERT( d->mRowShiftList->count() > 0 ); // must be true since it's not null
417 
418  // Check if we can attach to the last existing shift (very common for consecutive row additions)
419  shift = d->mRowShiftList->at( d->mRowShiftList->count() - 1 );
420  Q_ASSERT( shift );
421 
422  if ( shift->mShift > 0 ) // the shift was positive (addition)
423  {
424  if ( ( shift->mMinimumRowIndex + shift->mShift ) == modelIndexRowPosition )
425  {
426  // Inserting contiguous blocks of rows, just extend this shift
427  shift->mShift += count;
428  Q_ASSERT( d->mUpdateTimer->isActive() );
429  return;
430  }
431  }
432  }
433 
434  // FIXME: If we have few items, we can just shift the indexes now.
435 
436  shift = new RowShift( modelIndexRowPosition, count, d->mCurrentInvariantHash );
437  d->mRowShiftList->append( shift );
438 
439  d->mCurrentShiftSerial++;
440  d->mCurrentInvariantHash = new QHash< int, ModelInvariantIndex * >();
441 
442  if ( d->mRowShiftList->count() > 7 ) // 7 is heuristic
443  {
444  // We start loosing performance as the stack is growing too much.
445  // Start updating NOW and hope we can get it in few sweeps.
446 
447  if ( d->mUpdateTimer->isActive() )
448  d->mUpdateTimer->stop();
449 
450  d->slotPerformLazyUpdate();
451 
452  } else {
453  // Make sure we'll get a lazy update somewhere in the future
454  if ( !d->mUpdateTimer->isActive() )
455  d->mUpdateTimer->start( d->mLazyUpdateIdleInterval );
456  }
457 }
458 
459 QList< ModelInvariantIndex * > * ModelInvariantRowMapper::modelRowsRemoved( int modelIndexRowPosition, int count )
460 {
461  // Some rows were added from the model at modelIndexRowPosition.
462 
463  // FIXME: If rows are removed from the end, we don't need any mapping.
464  // The fact is that we don't know which is the model's end...
465  // But maybe we can consider the end being the greatest row
466  // index emitted until now...
467 
468  if ( !d->mRowShiftList )
469  {
470  if ( d->mCurrentInvariantHash->isEmpty() )
471  return 0; // no invariants emitted, even if rows are changed, no invariant is affected.
472  // some invariants might be affected
473  }
474 
475  // FIXME: If we have few items, we can just shift the indexes now.
476 
477  // FIXME: Find a way to "merge" the shifts, if possible
478  // It OFTEN happens that we remove a lot of items at once (as opposed
479  // to item addition which is usually an incremental operation).
480 
481  // FIXME: HUGE PROBLEM
482  // When the items arent contiguous or are just out of order it's
483  // impossible to merge the shifts. Deleting many messages
484  // generates then a very deep delta stack. Since to delete the
485  // next message you need to traverse the whole stack, this method
486  // becomes very slow (maybe not as slow as updating all the indexes
487  // in the general case, but still *slow*).
488  //
489  // So one needs to perform updates while rows are being removed
490  // but that tends to void all your efforts to not update the
491  // whole list of items every time...
492  //
493  // Also deletions don't seem to be asynchronous (or at least
494  // they eat all the CPU power available for KMail) so the timers
495  // don't fire and we're not actually processing the model jobs...
496  //
497  // It turns out that deleting many items is just slower than
498  // reloading the view...
499 
500  // Invalidate the invariants affected by the change
501  // In most cases it's a relatively small sweep (and it's done once).
502  // It's somewhat impossible to split this in chunks.
503 
504  QList< ModelInvariantIndex * > * deadInvariants = new QList< ModelInvariantIndex * >();
505 
506  const int end = modelIndexRowPosition + count;
507  for ( int idx = modelIndexRowPosition; idx < end; idx++ )
508  {
509  // FIXME: One could optimize this by joining the retrieval and destruction functions
510  // that is by making a special indexDead( int modelIndex )..
511  ModelInvariantIndex * dyingInvariant = d->modelIndexRowToModelInvariantIndexInternal( idx, false );
512  if ( dyingInvariant )
513  {
514  d->indexDead( dyingInvariant ); // will remove from this mapper hashes
515  dyingInvariant->d->setRowMapper( 0 ); // invalidate!
516  deadInvariants->append( dyingInvariant );
517  } else {
518  // got no dying invariant
519  kWarning() << "Could not find invariant to invalidate at current row " << idx;
520  }
521  }
522 
523  if ( !d->mRowShiftList )
524  {
525  // have no pending shifts, look if we are keeping other invariants
526  if ( d->mCurrentInvariantHash->isEmpty() )
527  {
528  // no more invariants in this mapper, even if rows are changed, no invariant is affected.
529  if ( deadInvariants->isEmpty() )
530  {
531  // should never happen, but well...
532  delete deadInvariants;
533  return 0;
534  }
535  return deadInvariants;
536  }
537  // still have some invariants inside, must add a shift for them
538  d->mRowShiftList = new QList< RowShift * >();
539  } // else already have shifts
540 
541  // add a shift for this row removal
542  RowShift * shift = new RowShift( modelIndexRowPosition + count, -count, d->mCurrentInvariantHash );
543  d->mRowShiftList->append( shift );
544 
545  d->mCurrentShiftSerial++;
546  d->mCurrentInvariantHash = new QHash< int, ModelInvariantIndex * >();
547 
548 
549  // trigger updates
550  if ( d->mRowShiftList->count() > 7 ) // 7 is heuristic
551  {
552  // We start loosing performance as the stack is growing too much.
553  // Start updating NOW and hope we can get it in few sweeps.
554 
555  if ( d->mUpdateTimer->isActive() )
556  d->mUpdateTimer->stop();
557 
558  d->slotPerformLazyUpdate();
559 
560  } else {
561  // Make sure we'll get a lazy update somewhere in the future
562  if ( !d->mUpdateTimer->isActive() )
563  d->mUpdateTimer->start( d->mLazyUpdateIdleInterval );
564  }
565 
566  if ( deadInvariants->isEmpty() )
567  {
568  // should never happen, but well...
569  delete deadInvariants;
570  return 0;
571  }
572 
573  return deadInvariants;
574 }
575 
576 void ModelInvariantRowMapper::modelReset()
577 {
578  // FIXME: optimize this (it probably can be optimized by providing a more complex user interface)
579  QHash< int, ModelInvariantIndex * >::ConstIterator end( d->mCurrentInvariantHash->constEnd() );
580 
581  for ( QHash< int, ModelInvariantIndex * >::ConstIterator it = d->mCurrentInvariantHash->constBegin(); it != end; ++it )
582  ( *it )->d->setRowMapper( 0 );
583  d->mCurrentInvariantHash->clear();
584 
585  if ( d->mRowShiftList )
586  {
587  while ( !d->mRowShiftList->isEmpty() )
588  delete d->mRowShiftList->takeFirst();
589 
590  delete d->mRowShiftList;
591  d->mRowShiftList = 0;
592  }
593 
594  d->mCurrentShiftSerial = 0;
595  d->mRemovedShiftCount = 0;
596 }
597 
598 void ModelInvariantRowMapperPrivate::slotPerformLazyUpdate()
599 {
600  // The drawback here is that when one row is removed from the middle (say position 500 of 1000)
601  // then we require ALL the items to be updated...but:
602  //
603  // - We can do it very lazily in the background
604  // - Optimizing this would mean to ALSO keep the indexes in lists or in a large array
605  // - The list approach would require to keep the indexes sorted
606  // so it would cost at least N log (N) / 2.. which is worse than N.
607  // - We could keep a single (or multiple) array as large as the model
608  // but then we'd have a large memory consumption and large overhead
609  // when inserting / removing items from the middle.
610  //
611  // So finally I think that the multiple hash approach is a "minimum loss" approach.
612 
613  QTime startTime = QTime::currentTime();
614 
615  int curIndex = 0;
616 
617  while( mRowShiftList )
618  {
619  // Have at least one row shift
620  uint count = static_cast< uint >( mRowShiftList->count() );
621 
622  // Grab it
623  RowShift * shift = mRowShiftList->at( 0 );
624 
625  // and update the invariants that belong to it
626  QHash< int, ModelInvariantIndex * >::Iterator it = shift->mInvariantHash->begin();
627  QHash< int, ModelInvariantIndex * >::Iterator end = shift->mInvariantHash->end();
628 
629  while ( it != end )
630  {
631  ModelInvariantIndex * invariant = *it;
632 
633  it = shift->mInvariantHash->erase( it );
634 
635  // apply shifts
636  int modelIndexRow = invariant->d->modelIndexRow();
637 
638  for ( uint idx = 0; idx < count; ++idx )
639  {
640  RowShift * thatShift = mRowShiftList->at( idx );
641  if ( modelIndexRow >= thatShift->mMinimumRowIndex )
642  modelIndexRow += thatShift->mShift;
643  }
644 
645  // update and make it belong to the current serial
646  invariant->d->setModelIndexRowAndRowMapperSerial( modelIndexRow, mCurrentShiftSerial );
647 
648  mCurrentInvariantHash->insert( modelIndexRow, invariant );
649 
650  // once in a while check if we ran out of time
651  if ( ( curIndex % 15 ) == 0 ) // 15 is heuristic
652  {
653  int elapsed = startTime.msecsTo( QTime::currentTime() );
654  if ( ( elapsed > mLazyUpdateChunkInterval ) || ( elapsed < 0 ) )
655  {
656  // interrupt
657  //kDebug() << "Lazy update fixed " << curIndex << " invariants " << endl;
658  mUpdateTimer->start( mLazyUpdateIdleInterval );
659  return;
660  }
661  }
662 
663  curIndex++;
664  }
665 
666  // no more invariants with serial <= invariantToFill->rowMapperSerial()
667  killFirstRowShift();
668  }
669 
670  //kDebug() << "Lazy update fixed " << curIndex << " invariants " << endl;
671 
672  // if we're here then no more work needs to be done.
673 }
674 
675 #include "modelinvariantrowmapper.moc"
MessageList::Core::ModelInvariantRowMapperPrivate::mRowShiftList
QList< RowShift * > * mRowShiftList
The ordered list of RowShifts, most recent at the end.
Definition: modelinvariantrowmapper_p.h:70
MessageList::Core::ModelInvariantRowMapper::modelReset
void modelReset()
Call this function from your handlers of reset() and layoutChanged() AFTER you ve last accessed the m...
Definition: modelinvariantrowmapper.cpp:576
MessageList::Core::ModelInvariantRowMapperPrivate::slotPerformLazyUpdate
void slotPerformLazyUpdate()
Internal: Performs a lazy update step.
Definition: modelinvariantrowmapper.cpp:598
MessageList::Core::ModelInvariantRowMapper::~ModelInvariantRowMapper
virtual ~ModelInvariantRowMapper()
Definition: modelinvariantrowmapper.cpp:79
MessageList::Core::ModelInvariantRowMapperPrivate::mLazyUpdateIdleInterval
int mLazyUpdateIdleInterval
Msecs: how much time we idle between lazy update chunks.
Definition: modelinvariantrowmapper_p.h:75
MessageList::Core::ModelInvariantRowMapperPrivate::mCurrentShiftSerial
uint mCurrentShiftSerial
Current model change serial: FIXME: it explodes at 2^32 :D.
Definition: modelinvariantrowmapper_p.h:72
MessageList::Core::ModelInvariantRowMapperPrivate::killFirstRowShift
void killFirstRowShift()
Internal: Removes the first RowShift from the list.
Definition: modelinvariantrowmapper.cpp:101
MessageList::Core::ModelInvariantRowMapper::modelIndexRowToModelInvariantIndex
ModelInvariantIndex * modelIndexRowToModelInvariantIndex(int modelIndexRow)
Finds the existing ModelInvariantIndex that belongs to the specified CURRENT modelIndexRow.
Definition: modelinvariantrowmapper.cpp:358
MessageList::Core::ModelInvariantRowMapperPrivate::updateModelInvariantIndex
void updateModelInvariantIndex(int modelIndexRow, ModelInvariantIndex *invariantToFill)
Internal.
Definition: modelinvariantrowmapper.cpp:150
MessageList::Core::ModelInvariantRowMapperPrivate::q
ModelInvariantRowMapper *const q
Definition: modelinvariantrowmapper_p.h:68
MessageList::Core::ModelInvariantRowMapperPrivate::indexDead
void indexDead(ModelInvariantIndex *index)
This is called from the ModelInvariantIndex destructor.
Definition: modelinvariantrowmapper.cpp:117
MessageList::Core::ModelInvariantRowMapper::setLazyUpdateChunkInterval
void setLazyUpdateChunkInterval(int chunkInterval)
Sets the maximum time we can spend inside a single lazy update step.
Definition: modelinvariantrowmapper.cpp:242
modelinvariantrowmapper.h
MessageList::Core::ModelInvariantIndex::Private::rowMapper
ModelInvariantRowMapper * rowMapper() const
Definition: modelinvariantindex_p.h:45
MessageList::Core::ModelInvariantIndex::Private::modelIndexRow
int modelIndexRow() const
Definition: modelinvariantindex_p.h:39
MessageList::Core::ModelInvariantRowMapper::modelRowsInserted
void modelRowsInserted(int modelIndexRowPosition, int count)
Call this function when rows are inserted to the underlying model BEFORE scanning the model for the n...
Definition: modelinvariantrowmapper.cpp:393
MessageList::Core::ModelInvariantRowMapperPrivate::modelIndexRowToModelInvariantIndexInternal
ModelInvariantIndex * modelIndexRowToModelInvariantIndexInternal(int modelIndexRow, bool updateIfNeeded)
Internal.
Definition: modelinvariantrowmapper.cpp:182
MessageList::Core::ModelInvariantRowMapperPrivate::mLazyUpdateChunkInterval
int mLazyUpdateChunkInterval
Msecs: how much time we spend inside a lazy update chunk.
Definition: modelinvariantrowmapper_p.h:74
MessageList::Core::ModelInvariantIndex::Private::rowMapperSerial
uint rowMapperSerial() const
Definition: modelinvariantindex_p.h:41
MessageList::Core::ModelInvariantRowMapper::createModelInvariantIndex
void createModelInvariantIndex(int modelIndexRow, ModelInvariantIndex *invariantToFill)
Binds a ModelInvariantIndex structure to the specified CURRENT modelIndexRow.
Definition: modelinvariantrowmapper.cpp:344
MessageList::Core::ModelInvariantRowMapperPrivate::mCurrentInvariantHash
QHash< int, ModelInvariantIndex * > * mCurrentInvariantHash
The up-to-date invariants.
Definition: modelinvariantrowmapper_p.h:71
MessageList::Core::ModelInvariantRowMapperPrivate::mRemovedShiftCount
uint mRemovedShiftCount
The number of shifts that we have completely processed.
Definition: modelinvariantrowmapper_p.h:73
modelinvariantrowmapper_p.h
MessageList::Core::ModelInvariantRowMapperPrivate
Definition: modelinvariantrowmapper_p.h:37
MessageList::Core::ModelInvariantRowMapper::modelIndexRowRangeToModelInvariantIndexList
QList< ModelInvariantIndex * > * modelIndexRowRangeToModelInvariantIndexList(int startIndexRow, int count)
This basically applies modelIndexRowToModelInvariantIndex() to a range of elements.
Definition: modelinvariantrowmapper.cpp:363
MessageList::Core::ModelInvariantRowMapper::setLazyUpdateIdleInterval
void setLazyUpdateIdleInterval(int idleInterval)
Sets the idle time between two lazy updates in milliseconds.
Definition: modelinvariantrowmapper.cpp:247
modelinvariantindex_p.h
MessageList::Core::ModelInvariantRowMapperPrivate::mUpdateTimer
QTimer * mUpdateTimer
Background lazy update timer.
Definition: modelinvariantrowmapper_p.h:76
MessageList::Core::ModelInvariantRowMapper::modelInvariantIndexToModelIndexRow
int modelInvariantIndexToModelIndexRow(ModelInvariantIndex *invariant)
Maps a ModelInvariantIndex to the CURRENT associated row index in the model.
Definition: modelinvariantrowmapper.cpp:252
MessageList::Core::ModelInvariantIndex
An invariant index that can be ALWAYS used to reference an item inside a QAbstractItemModel.
Definition: modelinvariantindex.h:44
MessageList::Core::ModelInvariantIndex::Private::setModelIndexRowAndRowMapperSerial
void setModelIndexRowAndRowMapperSerial(int modelIndexRow, uint rowMapperSerial)
Definition: modelinvariantindex_p.h:43
MessageList::Core::ModelInvariantIndex::Private::setRowMapper
void setRowMapper(ModelInvariantRowMapper *mapper)
Definition: modelinvariantindex_p.h:47
MessageList::Core::ModelInvariantRowMapper::ModelInvariantRowMapper
ModelInvariantRowMapper()
Definition: modelinvariantrowmapper.cpp:64
MessageList::Core::ModelInvariantRowMapper::modelRowsRemoved
QList< ModelInvariantIndex * > * modelRowsRemoved(int modelIndexRowPosition, int count)
Call this function when rows are removed from the underlying model AFTER accessing the removed rows f...
Definition: modelinvariantrowmapper.cpp:459
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:55:32 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

messagelist

Skip menu "messagelist"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal