ganeswidgets/src/HgScrollBufferManager.cpp
changeset 6 1cdcc61142d2
parent 5 4fa04caf0f43
child 7 5ebec3429918
equal deleted inserted replaced
5:4fa04caf0f43 6:1cdcc61142d2
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include <QTimer>
       
    20 
       
    21 #include "HgScrollBufferManager.h"
       
    22 #include "trace.h"
       
    23 
       
    24 // -----------------------------------------------------------------------------
       
    25 // HgScrollBufferManager::HgScrollBufferManager()
       
    26 // -----------------------------------------------------------------------------
       
    27 //
       
    28 HgScrollBufferManager::HgScrollBufferManager(
       
    29         int bufferSize,
       
    30         int bufferTreshold,
       
    31         int initialPosition,
       
    32         int totalCount )
       
    33 :   mBufferSize( bufferSize ),
       
    34     mBufferTreshold( bufferTreshold ),
       
    35     mBufferPosition( initialPosition ),
       
    36     mDiff(0),
       
    37     mTotalCount( totalCount ),
       
    38     mResetOrdered(false),
       
    39     mRequestStart(0),
       
    40     mRequestCount(0),
       
    41     mReleaseStart(0),
       
    42     mReleaseCount(0),
       
    43     mFirstTime(true)
       
    44     {
       
    45     init();
       
    46     }
       
    47 
       
    48 // -----------------------------------------------------------------------------
       
    49 // HgScrollBufferManager::init()
       
    50 // -----------------------------------------------------------------------------
       
    51 //
       
    52 void HgScrollBufferManager::init()
       
    53     {
       
    54     mResetOrdered = ETrue;
       
    55     mTimer.setSingleShot(true);
       
    56     connect(&mTimer, SIGNAL(timeout()), this, SLOT(timeout()));
       
    57     }
       
    58 
       
    59 // -----------------------------------------------------------------------------
       
    60 // HgScrollBufferManager::~HgScrollBufferManager()
       
    61 // -----------------------------------------------------------------------------
       
    62 //
       
    63 HgScrollBufferManager::~HgScrollBufferManager()
       
    64     {
       
    65     mTimer.stop();
       
    66     }
       
    67 
       
    68 // -----------------------------------------------------------------------------
       
    69 // HgScrollBufferManager::resetBuffer()
       
    70 // -----------------------------------------------------------------------------
       
    71 //
       
    72 void HgScrollBufferManager::resetBuffer( int aPosition, int totalCount )
       
    73     {
       
    74     if( !mResetOrdered )
       
    75         {
       
    76         // release Old buffer
       
    77         mReleaseStart = mBufferPosition;
       
    78         mReleaseCount = mBufferSize;
       
    79         }
       
    80 
       
    81     // set position and count
       
    82     mBufferPosition = aPosition - (mBufferSize / 2);
       
    83     mTotalCount = totalCount;
       
    84     mDiff = 0;
       
    85 
       
    86     if( mBufferPosition + mBufferSize > mTotalCount - 1 )
       
    87         {
       
    88         mBufferPosition = mTotalCount - mBufferSize;
       
    89         }
       
    90 
       
    91     if(mBufferPosition < 0 )
       
    92         {
       
    93         mBufferPosition = 0;
       
    94         }
       
    95 
       
    96     //request new Buffer
       
    97     mRequestStart = mBufferPosition;
       
    98     mRequestCount = mBufferSize;
       
    99     mResetOrdered = ETrue;
       
   100     asyncUpdate();
       
   101     }
       
   102 
       
   103 void HgScrollBufferManager::scrollPositionChanged( int newPosition )
       
   104     {
       
   105     // If all the items fit in the buffer no need to move the buffer.
       
   106     if( mTotalCount <= mBufferSize ) return;
       
   107 
       
   108     bool forceUpdate = EFalse;
       
   109     newPosition -= mBufferSize / 2; // normalize index to Buffer start
       
   110 
       
   111     if(newPosition < 0)
       
   112         {
       
   113         newPosition = 0;
       
   114         forceUpdate = ETrue;
       
   115         }
       
   116     else if( newPosition > mTotalCount - mBufferSize )
       
   117         {
       
   118         newPosition = mTotalCount - mBufferSize;
       
   119         forceUpdate = ETrue;
       
   120         }
       
   121 
       
   122     mDiff = mBufferPosition - newPosition;
       
   123 
       
   124     // Too large change reset whole buffer
       
   125     if( mDiff >= mBufferSize || -mDiff >= mBufferSize || mResetOrdered )
       
   126         {
       
   127         resetBuffer(newPosition + (mBufferSize/2), mTotalCount );
       
   128         }
       
   129     // Move Up
       
   130     else if( mDiff >= mBufferTreshold )
       
   131         {
       
   132         mRequestCount = mDiff;
       
   133         mReleaseCount = mDiff;
       
   134         asyncUpdate();
       
   135         }
       
   136     // Move Down
       
   137     else if( -mDiff >= mBufferTreshold )
       
   138         {
       
   139         mRequestCount = -mDiff;
       
   140         mReleaseCount = -mDiff;
       
   141         asyncUpdate();
       
   142         }
       
   143     // Top or bottom has been reached
       
   144     else if( forceUpdate && mDiff )
       
   145         {
       
   146         int diff = mDiff < 0 ? -mDiff : mDiff;
       
   147         mRequestCount = diff;
       
   148         mReleaseCount = diff;
       
   149         asyncUpdate();
       
   150         }
       
   151     }
       
   152 
       
   153 void HgScrollBufferManager::timeout()
       
   154 {
       
   155     mFirstTime = false;
       
   156     
       
   157     if(mResetOrdered)
       
   158         {
       
   159         mResetOrdered = EFalse;
       
   160         }
       
   161     else
       
   162         {
       
   163         if(mDiff < 0)
       
   164             {
       
   165             mReleaseStart = mBufferPosition;
       
   166             mRequestStart = mBufferPosition + mBufferSize;
       
   167             }
       
   168         else if( mDiff > 0)
       
   169             {
       
   170             mReleaseStart = mBufferPosition + mBufferSize - mDiff;
       
   171             mRequestStart = mBufferPosition - mDiff;
       
   172             }
       
   173         }
       
   174 
       
   175     // Release
       
   176     int end = mReleaseStart + mReleaseCount < mTotalCount ?
       
   177         mReleaseStart + mReleaseCount: mTotalCount;
       
   178     end--;
       
   179     if(end >= mReleaseStart )
       
   180         {
       
   181         emit releaseItems( mReleaseStart, end );
       
   182         }
       
   183 
       
   184     mReleaseCount = 0;
       
   185 
       
   186     // Request
       
   187     end = mRequestStart + mRequestCount < mTotalCount ?
       
   188         mRequestStart + mRequestCount : mTotalCount;
       
   189 
       
   190     end--;
       
   191     if(end >= mRequestStart )
       
   192         {
       
   193         emit requestItems( mRequestStart, end );
       
   194         }
       
   195 
       
   196     mRequestCount = 0;
       
   197 
       
   198     // Move Buffer
       
   199     mBufferPosition -= mDiff;
       
   200     // Reset Diff
       
   201     mDiff = 0;
       
   202 }
       
   203 
       
   204 bool HgScrollBufferManager::positionInsideBuffer( int position )
       
   205 {
       
   206     return position >= mBufferPosition && position <= (mBufferPosition+mBufferSize);
       
   207 }
       
   208 
       
   209 void HgScrollBufferManager::asyncUpdate()
       
   210 {
       
   211     if (!mTimer.isActive() && mFirstTime) {
       
   212         mTimer.start(0);
       
   213     } else if (!mTimer.isActive()){
       
   214         timeout();
       
   215     }
       
   216 }
       
   217 
       
   218 void HgScrollBufferManager::currentBuffer(int& bufferStart, int& bufferEnd)
       
   219 {
       
   220     bufferStart = mBufferPosition;
       
   221     bufferEnd = mBufferPosition+mBufferSize > mTotalCount-1 ?
       
   222         mTotalCount-1 : mBufferPosition+mBufferSize;
       
   223 }
       
   224 
       
   225 void HgScrollBufferManager::addItems(int start, int end)
       
   226 {
       
   227     FUNC_LOG;
       
   228 
       
   229     mTotalCount += (end-start+1);
       
   230     int lastBufferItem = mBufferPosition+mBufferSize-1;
       
   231 
       
   232     if (start < mBufferPosition) {
       
   233         simpleAddItems(start, end);
       
   234         // New items push the buffer forward, items inside the buffer do not change
       
   235     }
       
   236     // Check buffer higher limit
       
   237     else if (start <= lastBufferItem && end > lastBufferItem) {
       
   238         simpleAddItems(start, lastBufferItem);
       
   239         // Items added after the buffer are ignored
       
   240     }
       
   241     else {
       
   242         simpleAddItems(start, end);
       
   243     }
       
   244 }
       
   245 
       
   246 void HgScrollBufferManager::removeItems(int start, int end)
       
   247 {
       
   248     FUNC_LOG;
       
   249 
       
   250     int lastBufferItem = mBufferPosition+mBufferSize-1;
       
   251     int removedItemCount = end-start+1;
       
   252 
       
   253     if (mTotalCount < mBufferSize) {
       
   254         // Do nothing
       
   255     }
       
   256     else if (start > lastBufferItem) {
       
   257         // Do nothing
       
   258     }
       
   259     else if (end < mBufferPosition) {
       
   260         mTotalCount = mTotalCount-removedItemCount;
       
   261         simpleRemoveItems(start, end);
       
   262     }
       
   263     else if (start < mBufferPosition && end > lastBufferItem) {
       
   264         mTotalCount = mTotalCount-removedItemCount;
       
   265         mBufferPosition = qBound(0, mBufferPosition, mTotalCount-mBufferSize);
       
   266         resetBuffer(mBufferPosition, mTotalCount);
       
   267     }
       
   268     // Check buffer higher limit
       
   269     else if (start <= lastBufferItem && end > lastBufferItem) {
       
   270         mTotalCount = mTotalCount-(end-lastBufferItem);
       
   271         simpleRemoveItems(lastBufferItem+1, end);
       
   272         mTotalCount = mTotalCount-(lastBufferItem-start+1);
       
   273         simpleRemoveItems(start, lastBufferItem);
       
   274         // Order does matter
       
   275         mTotalCount = mTotalCount-(end-lastBufferItem);
       
   276         simpleRemoveItems(lastBufferItem+1, end);
       
   277         mTotalCount = mTotalCount-(lastBufferItem-start+1);
       
   278         simpleRemoveItems(start, lastBufferItem);
       
   279     }
       
   280     // Check buffer lower limit
       
   281     else if (start < mBufferPosition && end >= mBufferPosition) {
       
   282         // Order does matter
       
   283         mTotalCount = mTotalCount-(end-mBufferPosition+1);
       
   284         simpleRemoveItems(mBufferPosition, end);
       
   285         mTotalCount = mTotalCount-(mBufferPosition-start);
       
   286         simpleRemoveItems(start, mBufferPosition-1);
       
   287     }
       
   288     else {
       
   289         mTotalCount = mTotalCount-removedItemCount;
       
   290         simpleRemoveItems(start, end);
       
   291     }
       
   292 }
       
   293 
       
   294 void HgScrollBufferManager::moveItems(int start, int end, int target)
       
   295 {
       
   296     int lastBufferItem = mBufferPosition+mBufferSize-1;
       
   297 
       
   298     INFO("Move" << start << "-" << end << "to" << target << ",buffer:" << mBufferPosition << "-" << lastBufferItem << "total count:" << mTotalCount);
       
   299 
       
   300     if (mTotalCount < mBufferSize) {
       
   301         // Do nothing
       
   302     }
       
   303     else if (start < mBufferPosition && end > lastBufferItem) {
       
   304         resetBuffer(mBufferPosition, mTotalCount);
       
   305     }
       
   306     else if (start > lastBufferItem && target > lastBufferItem) {
       
   307         // Do nothing
       
   308     }
       
   309     else if (start > lastBufferItem && target < mBufferPosition) {
       
   310         simpleAddItems(start, end);
       
   311     }
       
   312     else if (end < mBufferPosition && target < mBufferPosition) {
       
   313         // Do nothing
       
   314     }
       
   315     else if (end < mBufferPosition && target > lastBufferItem) {
       
   316         simpleRemoveItems(start, end);
       
   317     }
       
   318     else if (start >= mBufferPosition && end <= lastBufferItem &&
       
   319              target >= mBufferPosition && target <= lastBufferItem) {
       
   320         // Do nothing
       
   321     }
       
   322     else {
       
   323         // Rare and complicated use cases: reset the whole buffer
       
   324         resetBuffer(mBufferPosition, mTotalCount);
       
   325     }
       
   326 }
       
   327 
       
   328 void HgScrollBufferManager::flushRequestBuffers()
       
   329 {
       
   330     FUNC_LOG;
       
   331 
       
   332     qSort(mReleaseBuffer);
       
   333     int releaseCount = mReleaseBuffer.count();
       
   334     int lastReleased = -1;
       
   335     for (int i = 0; i < releaseCount; i++) {
       
   336         UpdatePair update = mReleaseBuffer.at(i);
       
   337         emit releaseItems(qMax(lastReleased+1, update.start()), update.end());
       
   338         lastReleased = update.end();
       
   339     }
       
   340     mReleaseBuffer.clear();
       
   341 
       
   342     qSort(mRequestBuffer);
       
   343     int requestCount = mRequestBuffer.count();
       
   344     int lastRequested = -1;
       
   345     for (int i = 0; i < requestCount; i++) {
       
   346         UpdatePair update = mRequestBuffer.at(i);
       
   347         emit requestItems(qMax(lastRequested+1, update.start()), update.end());
       
   348         lastRequested = update.end();
       
   349     }
       
   350     mRequestBuffer.clear();
       
   351 }
       
   352 
       
   353 int HgScrollBufferManager::changeBufferPosition(int newPos)
       
   354 {
       
   355     FUNC_LOG;
       
   356     INFO("Change buffer position to" << newPos << "total count:" << mTotalCount);
       
   357     HANDLE_ERROR_BOOL((newPos >= 0));
       
   358     HANDLE_ERROR_BOOL((newPos+mBufferSize <= mTotalCount));
       
   359 
       
   360     int bufferShift = newPos-mBufferPosition;
       
   361     if (bufferShift > 0) {
       
   362         mRequestBuffer.shiftRight(mBufferPosition, bufferShift);
       
   363         mReleaseBuffer.shiftRight(mBufferPosition, bufferShift);
       
   364     }
       
   365     else if (bufferShift < 0) {
       
   366         mRequestBuffer.shiftLeft(mBufferPosition, -bufferShift);
       
   367         mReleaseBuffer.shiftLeft(mBufferPosition, -bufferShift);
       
   368     }
       
   369     mBufferPosition = newPos;
       
   370     return bufferShift;
       
   371 }
       
   372 
       
   373 /**
       
   374     This function manages only simple item additions: all items are either
       
   375     outside the buffer or inside it.
       
   376     Firs call prepare, then update model, then call fecth.
       
   377 */
       
   378 void HgScrollBufferManager::simpleAddItems(int start, int end)
       
   379 {
       
   380     FUNC_LOG;
       
   381 
       
   382     int lastBufferItem = mBufferPosition+mBufferSize-1;
       
   383     int numAddedItems = end-start+1; // [start, end] inclusive
       
   384 
       
   385     if (mTotalCount < mBufferSize) {
       
   386         appendRequestBuffer(start, numAddedItems);
       
   387     }
       
   388     else if (start > lastBufferItem) {
       
   389         // Do nothing
       
   390     }
       
   391     else if (start <= mBufferPosition) {
       
   392         changeBufferPosition(mBufferPosition+numAddedItems);
       
   393         // No need to fetch items, the indexes just change
       
   394     }
       
   395     else {
       
   396         // free from end
       
   397         appendReleaseBuffer(lastBufferItem+1-numAddedItems, numAddedItems);
       
   398         mReleaseBuffer.shiftRight(start, numAddedItems);
       
   399         mRequestBuffer.shiftRight(start, numAddedItems);
       
   400         appendRequestBuffer(start, numAddedItems);
       
   401     }
       
   402 }
       
   403 
       
   404 /**
       
   405     This function manages only simple item removals: all items are either
       
   406     outside the buffer or inside it.
       
   407     Firs call prepare, then update model, then call fecth.
       
   408 */
       
   409 void HgScrollBufferManager::simpleRemoveItems(int start, int end)
       
   410 {
       
   411     FUNC_LOG;
       
   412 
       
   413     int lastBufferItem = mBufferPosition+mBufferSize-1;
       
   414     int numRemovedItems = end-start+1; // [start, end] inclusive
       
   415 
       
   416     if (start > lastBufferItem) {
       
   417         // Do nothing
       
   418     }
       
   419     else if (end < mBufferPosition) {
       
   420         changeBufferPosition(qMax(0, mBufferPosition-numRemovedItems));
       
   421         // No need to fetch items, the indexes just change
       
   422     }
       
   423     else {
       
   424         if (mTotalCount < mBufferPosition+mBufferSize) {
       
   425             // Buffer is at the end of items
       
   426             int bufferShift = changeBufferPosition(qMax(0, mTotalCount-mBufferSize));
       
   427             // Fetch from beginning
       
   428             // Releasing removed items has been done outside this class
       
   429             appendRequestBuffer(mBufferPosition, qAbs(bufferShift));
       
   430         }
       
   431         else {
       
   432             // Fetch from end
       
   433             appendRequestBuffer(lastBufferItem+1-numRemovedItems, numRemovedItems);
       
   434         }
       
   435     }
       
   436 }
       
   437 
       
   438 void HgScrollBufferManager::appendRequestBuffer(int start, int count)
       
   439 {
       
   440     FUNC_LOG;
       
   441     INFO("Request items" << start << ":" << count)
       
   442 
       
   443     mRequestBuffer.add(start, count);
       
   444     mReleaseBuffer.remove(start, count);
       
   445 }
       
   446 
       
   447 void HgScrollBufferManager::appendReleaseBuffer(int start, int count)
       
   448 {
       
   449     FUNC_LOG;
       
   450     INFO("Release items" << start << ":" << count)
       
   451 
       
   452     mReleaseBuffer.add(start, count);
       
   453     mRequestBuffer.remove(start, count);
       
   454 }
       
   455 
       
   456 UpdatePair::UpdatePair(int start, int count) : mStart(start), mCount(count)
       
   457 {
       
   458     HANDLE_ERROR_BOOL(mCount > 0);
       
   459 }
       
   460 
       
   461 int UpdatePair::start() const
       
   462 {
       
   463     return mStart;
       
   464 }
       
   465 
       
   466 int UpdatePair::end() const
       
   467 {
       
   468     return mStart+mCount-1;
       
   469 }
       
   470 
       
   471 bool UpdatePair::adjacent(int start, int count) const
       
   472 {
       
   473     if (start+count < mStart) return false;
       
   474     if (start > mStart+mCount) return false;
       
   475     return true;
       
   476 }
       
   477 
       
   478 bool UpdatePair::contains(const UpdatePair &other) const
       
   479 {
       
   480     if (other.mStart+other.mCount-1 < mStart) return false;
       
   481     if (other.mStart > mStart+mCount-1) return false;
       
   482     return true;
       
   483 }
       
   484 
       
   485 void UpdatePair::extend(int start, int count)
       
   486 {
       
   487     int end = qMax(mStart+mCount, start+count);
       
   488     mStart = qMin(mStart, start);
       
   489     mCount = end-mStart;
       
   490     INFO("Pair extended to:" << mStart << ":" << mCount);
       
   491 }
       
   492 
       
   493 void UpdatePair::subtract(int start, int count)
       
   494 {
       
   495     int end = qMin(mStart+mCount, start+count);
       
   496     mStart = qMax(mStart, start);
       
   497     mCount = end-mStart;
       
   498     INFO("Pair reduced to:" << mStart << ":" << mCount);
       
   499 }
       
   500 
       
   501 void UpdatePair::shiftRight(int count)
       
   502 {
       
   503     mStart += count;
       
   504     INFO("Pair shifted to:" << mStart << ":" << mCount);
       
   505 }
       
   506 
       
   507 void UpdatePair::shiftLeft(int count)
       
   508 {
       
   509     mStart -= count;
       
   510     HANDLE_ERROR_BOOL((mStart >= 0));
       
   511     INFO("Pair shifted to:" << mStart << ":" << mCount);
       
   512 }
       
   513 
       
   514 bool UpdatePair::operator== (const UpdatePair &other) const
       
   515 {
       
   516     return mStart == other.mStart && mCount == other.mCount;
       
   517 }
       
   518 
       
   519 bool UpdatePair::operator< (const UpdatePair &other) const
       
   520 {
       
   521     return (mStart < other.mStart || mStart == other.mStart && mCount < other.mCount);
       
   522 }
       
   523 
       
   524 UpdateBuffer::UpdateBuffer()
       
   525 {
       
   526     FUNC_LOG;
       
   527 }
       
   528 
       
   529 void UpdateBuffer::add(int start, int count)
       
   530 {
       
   531     FUNC_LOG;
       
   532 
       
   533     int itemCount = this->count();
       
   534     for (int i = 0; i < itemCount; i++) {
       
   535         if (at(i).contains(UpdatePair(start, count))) {
       
   536             // It is already there
       
   537             return;
       
   538         }
       
   539         if (at(i).adjacent(start, count)) {
       
   540             (*this)[i].extend(start, count);
       
   541             return;
       
   542         }
       
   543     }
       
   544     append(UpdatePair(start, count));
       
   545 }
       
   546 
       
   547 void UpdateBuffer::remove(int start, int count)
       
   548 {
       
   549     FUNC_LOG;
       
   550 
       
   551     int itemCount = this->count();
       
   552     for (int i = itemCount-1; i >= 0; i--) {
       
   553         UpdatePair pair = at(i);
       
   554         UpdatePair comp = UpdatePair(start, count);
       
   555         if (comp.contains(pair)) {
       
   556             INFO("Removing pair" << pair.start() << "-" << pair.end());
       
   557             removeAt(i);
       
   558         }
       
   559         else if (pair.contains(comp)) {
       
   560             // Subtraction from middle is not applicable in mediawall use cases
       
   561             (*this)[i].subtract(start, count);
       
   562         }
       
   563         // Item may be present in multiple pairs.
       
   564     }
       
   565 }
       
   566 
       
   567 void UpdateBuffer::shiftRight(int startingFrom, int amount)
       
   568 {
       
   569     FUNC_LOG;
       
   570 
       
   571     int itemCount = this->count();
       
   572     for (int i = 0; i < itemCount; i++) {
       
   573         if (at(i).start() >= startingFrom) {
       
   574             (*this)[i].shiftRight(amount);
       
   575         }
       
   576     }
       
   577 }
       
   578 
       
   579 void UpdateBuffer::shiftLeft(int startingFrom, int amount)
       
   580 {
       
   581     FUNC_LOG;
       
   582 
       
   583     int itemCount = this->count();
       
   584     for (int i = 0; i < itemCount; i++) {
       
   585         if (at(i).start() >= startingFrom) {
       
   586             (*this)[i].shiftLeft(amount);
       
   587         }
       
   588     }
       
   589 }