ganeswidgets/src/hgscrollbuffermanager.cpp
changeset 6 1cdcc61142d2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ganeswidgets/src/hgscrollbuffermanager.cpp	Thu Jun 24 12:59:29 2010 +0300
@@ -0,0 +1,589 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+
+#include <QTimer>
+
+#include "hgscrollbuffermanager.h"
+#include "trace.h"
+
+// -----------------------------------------------------------------------------
+// HgScrollBufferManager::HgScrollBufferManager()
+// -----------------------------------------------------------------------------
+//
+HgScrollBufferManager::HgScrollBufferManager(
+        int bufferSize,
+        int bufferTreshold,
+        int initialPosition,
+        int totalCount )
+:   mBufferSize( bufferSize ),
+    mBufferTreshold( bufferTreshold ),
+    mBufferPosition( initialPosition ),
+    mDiff(0),
+    mTotalCount( totalCount ),
+    mResetOrdered(false),
+    mRequestStart(0),
+    mRequestCount(0),
+    mReleaseStart(0),
+    mReleaseCount(0),
+    mFirstTime(true)
+    {
+    init();
+    }
+
+// -----------------------------------------------------------------------------
+// HgScrollBufferManager::init()
+// -----------------------------------------------------------------------------
+//
+void HgScrollBufferManager::init()
+    {
+    mResetOrdered = ETrue;
+    mTimer.setSingleShot(true);
+    connect(&mTimer, SIGNAL(timeout()), this, SLOT(timeout()));
+    }
+
+// -----------------------------------------------------------------------------
+// HgScrollBufferManager::~HgScrollBufferManager()
+// -----------------------------------------------------------------------------
+//
+HgScrollBufferManager::~HgScrollBufferManager()
+    {
+    mTimer.stop();
+    }
+
+// -----------------------------------------------------------------------------
+// HgScrollBufferManager::resetBuffer()
+// -----------------------------------------------------------------------------
+//
+void HgScrollBufferManager::resetBuffer( int aPosition, int totalCount )
+    {
+    if( !mResetOrdered )
+        {
+        // release Old buffer
+        mReleaseStart = mBufferPosition;
+        mReleaseCount = mBufferSize;
+        }
+
+    // set position and count
+    mBufferPosition = aPosition - (mBufferSize / 2);
+    mTotalCount = totalCount;
+    mDiff = 0;
+
+    if( mBufferPosition + mBufferSize > mTotalCount - 1 )
+        {
+        mBufferPosition = mTotalCount - mBufferSize;
+        }
+
+    if(mBufferPosition < 0 )
+        {
+        mBufferPosition = 0;
+        }
+
+    //request new Buffer
+    mRequestStart = mBufferPosition;
+    mRequestCount = mBufferSize;
+    mResetOrdered = ETrue;
+    asyncUpdate();
+    }
+
+void HgScrollBufferManager::scrollPositionChanged( int newPosition )
+    {
+    // If all the items fit in the buffer no need to move the buffer.
+    if( mTotalCount <= mBufferSize ) return;
+
+    bool forceUpdate = EFalse;
+    newPosition -= mBufferSize / 2; // normalize index to Buffer start
+
+    if(newPosition < 0)
+        {
+        newPosition = 0;
+        forceUpdate = ETrue;
+        }
+    else if( newPosition > mTotalCount - mBufferSize )
+        {
+        newPosition = mTotalCount - mBufferSize;
+        forceUpdate = ETrue;
+        }
+
+    mDiff = mBufferPosition - newPosition;
+
+    // Too large change reset whole buffer
+    if( mDiff >= mBufferSize || -mDiff >= mBufferSize || mResetOrdered )
+        {
+        resetBuffer(newPosition + (mBufferSize/2), mTotalCount );
+        }
+    // Move Up
+    else if( mDiff >= mBufferTreshold )
+        {
+        mRequestCount = mDiff;
+        mReleaseCount = mDiff;
+        asyncUpdate();
+        }
+    // Move Down
+    else if( -mDiff >= mBufferTreshold )
+        {
+        mRequestCount = -mDiff;
+        mReleaseCount = -mDiff;
+        asyncUpdate();
+        }
+    // Top or bottom has been reached
+    else if( forceUpdate && mDiff )
+        {
+        int diff = mDiff < 0 ? -mDiff : mDiff;
+        mRequestCount = diff;
+        mReleaseCount = diff;
+        asyncUpdate();
+        }
+    }
+
+void HgScrollBufferManager::timeout()
+{
+    mFirstTime = false;
+    
+    if(mResetOrdered)
+        {
+        mResetOrdered = EFalse;
+        }
+    else
+        {
+        if(mDiff < 0)
+            {
+            mReleaseStart = mBufferPosition;
+            mRequestStart = mBufferPosition + mBufferSize;
+            }
+        else if( mDiff > 0)
+            {
+            mReleaseStart = mBufferPosition + mBufferSize - mDiff;
+            mRequestStart = mBufferPosition - mDiff;
+            }
+        }
+
+    // Release
+    int end = mReleaseStart + mReleaseCount < mTotalCount ?
+        mReleaseStart + mReleaseCount: mTotalCount;
+    end--;
+    if(end >= mReleaseStart )
+        {
+        emit releaseItems( mReleaseStart, end );
+        }
+
+    mReleaseCount = 0;
+
+    // Request
+    end = mRequestStart + mRequestCount < mTotalCount ?
+        mRequestStart + mRequestCount : mTotalCount;
+
+    end--;
+    if(end >= mRequestStart )
+        {
+        emit requestItems( mRequestStart, end );
+        }
+
+    mRequestCount = 0;
+
+    // Move Buffer
+    mBufferPosition -= mDiff;
+    // Reset Diff
+    mDiff = 0;
+}
+
+bool HgScrollBufferManager::positionInsideBuffer( int position )
+{
+    return position >= mBufferPosition && position <= (mBufferPosition+mBufferSize);
+}
+
+void HgScrollBufferManager::asyncUpdate()
+{
+    if (!mTimer.isActive() && mFirstTime) {
+        mTimer.start(0);
+    } else if (!mTimer.isActive()){
+        timeout();
+    }
+}
+
+void HgScrollBufferManager::currentBuffer(int& bufferStart, int& bufferEnd)
+{
+    bufferStart = mBufferPosition;
+    bufferEnd = mBufferPosition+mBufferSize > mTotalCount-1 ?
+        mTotalCount-1 : mBufferPosition+mBufferSize;
+}
+
+void HgScrollBufferManager::addItems(int start, int end)
+{
+    FUNC_LOG;
+
+    mTotalCount += (end-start+1);
+    int lastBufferItem = mBufferPosition+mBufferSize-1;
+
+    if (start < mBufferPosition) {
+        simpleAddItems(start, end);
+        // New items push the buffer forward, items inside the buffer do not change
+    }
+    // Check buffer higher limit
+    else if (start <= lastBufferItem && end > lastBufferItem) {
+        simpleAddItems(start, lastBufferItem);
+        // Items added after the buffer are ignored
+    }
+    else {
+        simpleAddItems(start, end);
+    }
+}
+
+void HgScrollBufferManager::removeItems(int start, int end)
+{
+    FUNC_LOG;
+
+    int lastBufferItem = mBufferPosition+mBufferSize-1;
+    int removedItemCount = end-start+1;
+
+    if (mTotalCount < mBufferSize) {
+        // Do nothing
+    }
+    else if (start > lastBufferItem) {
+        // Do nothing
+    }
+    else if (end < mBufferPosition) {
+        mTotalCount = mTotalCount-removedItemCount;
+        simpleRemoveItems(start, end);
+    }
+    else if (start < mBufferPosition && end > lastBufferItem) {
+        mTotalCount = mTotalCount-removedItemCount;
+        mBufferPosition = qBound(0, mBufferPosition, mTotalCount-mBufferSize);
+        resetBuffer(mBufferPosition, mTotalCount);
+    }
+    // Check buffer higher limit
+    else if (start <= lastBufferItem && end > lastBufferItem) {
+        mTotalCount = mTotalCount-(end-lastBufferItem);
+        simpleRemoveItems(lastBufferItem+1, end);
+        mTotalCount = mTotalCount-(lastBufferItem-start+1);
+        simpleRemoveItems(start, lastBufferItem);
+        // Order does matter
+        mTotalCount = mTotalCount-(end-lastBufferItem);
+        simpleRemoveItems(lastBufferItem+1, end);
+        mTotalCount = mTotalCount-(lastBufferItem-start+1);
+        simpleRemoveItems(start, lastBufferItem);
+    }
+    // Check buffer lower limit
+    else if (start < mBufferPosition && end >= mBufferPosition) {
+        // Order does matter
+        mTotalCount = mTotalCount-(end-mBufferPosition+1);
+        simpleRemoveItems(mBufferPosition, end);
+        mTotalCount = mTotalCount-(mBufferPosition-start);
+        simpleRemoveItems(start, mBufferPosition-1);
+    }
+    else {
+        mTotalCount = mTotalCount-removedItemCount;
+        simpleRemoveItems(start, end);
+    }
+}
+
+void HgScrollBufferManager::moveItems(int start, int end, int target)
+{
+    int lastBufferItem = mBufferPosition+mBufferSize-1;
+
+    INFO("Move" << start << "-" << end << "to" << target << ",buffer:" << mBufferPosition << "-" << lastBufferItem << "total count:" << mTotalCount);
+
+    if (mTotalCount < mBufferSize) {
+        // Do nothing
+    }
+    else if (start < mBufferPosition && end > lastBufferItem) {
+        resetBuffer(mBufferPosition, mTotalCount);
+    }
+    else if (start > lastBufferItem && target > lastBufferItem) {
+        // Do nothing
+    }
+    else if (start > lastBufferItem && target < mBufferPosition) {
+        simpleAddItems(start, end);
+    }
+    else if (end < mBufferPosition && target < mBufferPosition) {
+        // Do nothing
+    }
+    else if (end < mBufferPosition && target > lastBufferItem) {
+        simpleRemoveItems(start, end);
+    }
+    else if (start >= mBufferPosition && end <= lastBufferItem &&
+             target >= mBufferPosition && target <= lastBufferItem) {
+        // Do nothing
+    }
+    else {
+        // Rare and complicated use cases: reset the whole buffer
+        resetBuffer(mBufferPosition, mTotalCount);
+    }
+}
+
+void HgScrollBufferManager::flushRequestBuffers()
+{
+    FUNC_LOG;
+
+    qSort(mReleaseBuffer);
+    int releaseCount = mReleaseBuffer.count();
+    int lastReleased = -1;
+    for (int i = 0; i < releaseCount; i++) {
+        UpdatePair update = mReleaseBuffer.at(i);
+        emit releaseItems(qMax(lastReleased+1, update.start()), update.end());
+        lastReleased = update.end();
+    }
+    mReleaseBuffer.clear();
+
+    qSort(mRequestBuffer);
+    int requestCount = mRequestBuffer.count();
+    int lastRequested = -1;
+    for (int i = 0; i < requestCount; i++) {
+        UpdatePair update = mRequestBuffer.at(i);
+        emit requestItems(qMax(lastRequested+1, update.start()), update.end());
+        lastRequested = update.end();
+    }
+    mRequestBuffer.clear();
+}
+
+int HgScrollBufferManager::changeBufferPosition(int newPos)
+{
+    FUNC_LOG;
+    INFO("Change buffer position to" << newPos << "total count:" << mTotalCount);
+    HANDLE_ERROR_BOOL((newPos >= 0));
+    HANDLE_ERROR_BOOL((newPos+mBufferSize <= mTotalCount));
+
+    int bufferShift = newPos-mBufferPosition;
+    if (bufferShift > 0) {
+        mRequestBuffer.shiftRight(mBufferPosition, bufferShift);
+        mReleaseBuffer.shiftRight(mBufferPosition, bufferShift);
+    }
+    else if (bufferShift < 0) {
+        mRequestBuffer.shiftLeft(mBufferPosition, -bufferShift);
+        mReleaseBuffer.shiftLeft(mBufferPosition, -bufferShift);
+    }
+    mBufferPosition = newPos;
+    return bufferShift;
+}
+
+/**
+    This function manages only simple item additions: all items are either
+    outside the buffer or inside it.
+    Firs call prepare, then update model, then call fecth.
+*/
+void HgScrollBufferManager::simpleAddItems(int start, int end)
+{
+    FUNC_LOG;
+
+    int lastBufferItem = mBufferPosition+mBufferSize-1;
+    int numAddedItems = end-start+1; // [start, end] inclusive
+
+    if (mTotalCount < mBufferSize) {
+        appendRequestBuffer(start, numAddedItems);
+    }
+    else if (start > lastBufferItem) {
+        // Do nothing
+    }
+    else if (start <= mBufferPosition) {
+        changeBufferPosition(mBufferPosition+numAddedItems);
+        // No need to fetch items, the indexes just change
+    }
+    else {
+        // free from end
+        appendReleaseBuffer(lastBufferItem+1-numAddedItems, numAddedItems);
+        mReleaseBuffer.shiftRight(start, numAddedItems);
+        mRequestBuffer.shiftRight(start, numAddedItems);
+        appendRequestBuffer(start, numAddedItems);
+    }
+}
+
+/**
+    This function manages only simple item removals: all items are either
+    outside the buffer or inside it.
+    Firs call prepare, then update model, then call fecth.
+*/
+void HgScrollBufferManager::simpleRemoveItems(int start, int end)
+{
+    FUNC_LOG;
+
+    int lastBufferItem = mBufferPosition+mBufferSize-1;
+    int numRemovedItems = end-start+1; // [start, end] inclusive
+
+    if (start > lastBufferItem) {
+        // Do nothing
+    }
+    else if (end < mBufferPosition) {
+        changeBufferPosition(qMax(0, mBufferPosition-numRemovedItems));
+        // No need to fetch items, the indexes just change
+    }
+    else {
+        if (mTotalCount < mBufferPosition+mBufferSize) {
+            // Buffer is at the end of items
+            int bufferShift = changeBufferPosition(qMax(0, mTotalCount-mBufferSize));
+            // Fetch from beginning
+            // Releasing removed items has been done outside this class
+            appendRequestBuffer(mBufferPosition, qAbs(bufferShift));
+        }
+        else {
+            // Fetch from end
+            appendRequestBuffer(lastBufferItem+1-numRemovedItems, numRemovedItems);
+        }
+    }
+}
+
+void HgScrollBufferManager::appendRequestBuffer(int start, int count)
+{
+    FUNC_LOG;
+    INFO("Request items" << start << ":" << count)
+
+    mRequestBuffer.add(start, count);
+    mReleaseBuffer.remove(start, count);
+}
+
+void HgScrollBufferManager::appendReleaseBuffer(int start, int count)
+{
+    FUNC_LOG;
+    INFO("Release items" << start << ":" << count)
+
+    mReleaseBuffer.add(start, count);
+    mRequestBuffer.remove(start, count);
+}
+
+UpdatePair::UpdatePair(int start, int count) : mStart(start), mCount(count)
+{
+    HANDLE_ERROR_BOOL(mCount > 0);
+}
+
+int UpdatePair::start() const
+{
+    return mStart;
+}
+
+int UpdatePair::end() const
+{
+    return mStart+mCount-1;
+}
+
+bool UpdatePair::adjacent(int start, int count) const
+{
+    if (start+count < mStart) return false;
+    if (start > mStart+mCount) return false;
+    return true;
+}
+
+bool UpdatePair::contains(const UpdatePair &other) const
+{
+    if (other.mStart+other.mCount-1 < mStart) return false;
+    if (other.mStart > mStart+mCount-1) return false;
+    return true;
+}
+
+void UpdatePair::extend(int start, int count)
+{
+    int end = qMax(mStart+mCount, start+count);
+    mStart = qMin(mStart, start);
+    mCount = end-mStart;
+    INFO("Pair extended to:" << mStart << ":" << mCount);
+}
+
+void UpdatePair::subtract(int start, int count)
+{
+    int end = qMin(mStart+mCount, start+count);
+    mStart = qMax(mStart, start);
+    mCount = end-mStart;
+    INFO("Pair reduced to:" << mStart << ":" << mCount);
+}
+
+void UpdatePair::shiftRight(int count)
+{
+    mStart += count;
+    INFO("Pair shifted to:" << mStart << ":" << mCount);
+}
+
+void UpdatePair::shiftLeft(int count)
+{
+    mStart -= count;
+    HANDLE_ERROR_BOOL((mStart >= 0));
+    INFO("Pair shifted to:" << mStart << ":" << mCount);
+}
+
+bool UpdatePair::operator== (const UpdatePair &other) const
+{
+    return mStart == other.mStart && mCount == other.mCount;
+}
+
+bool UpdatePair::operator< (const UpdatePair &other) const
+{
+    return (mStart < other.mStart || mStart == other.mStart && mCount < other.mCount);
+}
+
+UpdateBuffer::UpdateBuffer()
+{
+    FUNC_LOG;
+}
+
+void UpdateBuffer::add(int start, int count)
+{
+    FUNC_LOG;
+
+    int itemCount = this->count();
+    for (int i = 0; i < itemCount; i++) {
+        if (at(i).contains(UpdatePair(start, count))) {
+            // It is already there
+            return;
+        }
+        if (at(i).adjacent(start, count)) {
+            (*this)[i].extend(start, count);
+            return;
+        }
+    }
+    append(UpdatePair(start, count));
+}
+
+void UpdateBuffer::remove(int start, int count)
+{
+    FUNC_LOG;
+
+    int itemCount = this->count();
+    for (int i = itemCount-1; i >= 0; i--) {
+        UpdatePair pair = at(i);
+        UpdatePair comp = UpdatePair(start, count);
+        if (comp.contains(pair)) {
+            INFO("Removing pair" << pair.start() << "-" << pair.end());
+            removeAt(i);
+        }
+        else if (pair.contains(comp)) {
+            // Subtraction from middle is not applicable in mediawall use cases
+            (*this)[i].subtract(start, count);
+        }
+        // Item may be present in multiple pairs.
+    }
+}
+
+void UpdateBuffer::shiftRight(int startingFrom, int amount)
+{
+    FUNC_LOG;
+
+    int itemCount = this->count();
+    for (int i = 0; i < itemCount; i++) {
+        if (at(i).start() >= startingFrom) {
+            (*this)[i].shiftRight(amount);
+        }
+    }
+}
+
+void UpdateBuffer::shiftLeft(int startingFrom, int amount)
+{
+    FUNC_LOG;
+
+    int itemCount = this->count();
+    for (int i = 0; i < itemCount; i++) {
+        if (at(i).start() >= startingFrom) {
+            (*this)[i].shiftLeft(amount);
+        }
+    }
+}