diff -r 89c329efa980 -r e48454f237ca ganeswidgets/src/HgScrollBufferManager.cpp --- a/ganeswidgets/src/HgScrollBufferManager.cpp Mon Apr 19 14:40:06 2010 +0300 +++ b/ganeswidgets/src/HgScrollBufferManager.cpp Mon May 03 13:32:54 2010 +0300 @@ -216,145 +216,368 @@ mTotalCount-1 : mBufferPosition+mBufferSize; } -void HgScrollBufferManager::removeItems(int start, int end, int totalCount) +void HgScrollBufferManager::addItems(int start, int end) { - int oldTotalCount = mTotalCount; - mTotalCount = totalCount; + FUNC_LOG; + + mTotalCount += (end-start+1); + int lastBufferItem = mBufferPosition+mBufferSize-1; - if (isInsideBuffer(start, end)) { - if (mTotalCount > mBufferSize && mBufferPosition+mBufferSize == oldTotalCount) { - // We are at the end of items, move buffer - int oldBufferPos = mBufferPosition; - mBufferPosition = qMax(0, totalCount-mBufferSize); + 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 (start < oldBufferPos) { // Removed items are partially inside buffer - emit requestItems(mBufferPosition, start-1); - } - else { - emit requestItems(mBufferPosition, oldBufferPos-1); - } - } - else { - int first = qBound(mBufferPosition, start, mBufferPosition+mBufferSize-1); - int last = qBound(mBufferPosition, end, mBufferPosition+mBufferSize-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); + } +} - // Requested from the end - emit requestItems(mBufferPosition+mBufferSize-(last-first+1), - qMin(mBufferPosition+mBufferSize-1, mTotalCount)); - } +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::addItems(int start, int end, int totalCount) +void HgScrollBufferManager::flushRequestBuffers() { - int oldTotalCount = mTotalCount; - mTotalCount = totalCount; + 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(); - if (isInsideBuffer(start, end)) { - int first = start; - int last = end; + 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; +} - if (mTotalCount > mBufferSize && mBufferPosition+mBufferSize == oldTotalCount) { - // We are at the end of items, keep it that way - int oldBufferPos = mBufferPosition; - mBufferPosition = qMin(mBufferPosition+(end-start+1), totalCount-mBufferSize); +/** + 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 (oldBufferPos < mBufferPosition) { - // Release from the beginning - emit releaseItems(oldBufferPos, mBufferPosition-1); - } + 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); + } +} - // Added items may fall outside the buffer as the buffer is moved - if (isInsideBuffer(start, end)) { - first = qBound(mBufferPosition, start, mBufferPosition+mBufferSize-1); - last = qBound(mBufferPosition, end, mBufferPosition+mBufferSize-1); - emit requestItems(first, last); - } +/** + 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 { - first = qBound(mBufferPosition, start, mBufferPosition+mBufferSize-1); - last = qBound(mBufferPosition, end, mBufferPosition+mBufferSize-1); - - if (mTotalCount > mBufferSize) { - // Release from the end - emit releaseItems(mBufferPosition+mBufferSize, - mBufferPosition+mBufferSize+(last-first+1)-1); - } - // If all the items fit in the buffer no need to release items - - emit requestItems(first, last); + // Fetch from end + appendRequestBuffer(lastBufferItem+1-numRemovedItems, numRemovedItems); } } } -void HgScrollBufferManager::moveItems(int start, int end, int target, int totalCount) +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) { - if (isInsideBuffer(start) && isInsideBuffer(end) && isInsideBuffer(target)) { - return; - } + HANDLE_ERROR_BOOL(mCount > 0); +} + +int UpdatePair::start() const +{ + return mStart; +} + +int UpdatePair::end() const +{ + return mStart+mCount-1; +} - if (!isInsideBuffer(start, end) && !isInsideBuffer(target)) { - return; - } +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; +} - if (!isInsideBuffer(target)) { - if (isInsideBuffer(start) && isInsideBuffer(end)) { - if (mBufferPosition+mBufferSize == mTotalCount) { - // Fetch from beginning - emit requestItems(mBufferPosition, mBufferPosition+end-start); - } - else { - // Fetch from end - emit requestItems(mBufferPosition+mBufferSize-(end-start+1), - qMin(mBufferPosition+mBufferSize-1, mTotalCount)); - } +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; } - else if (isInsideBuffer(start) && end >= mBufferPosition+mBufferSize-1) { - emit requestItems(start, mBufferPosition+mBufferSize-1); - } - else if (start <= mBufferPosition && isInsideBuffer(end)) { - emit requestItems(mBufferPosition, end); - } - else { - emit requestItems(mBufferPosition, mBufferPosition+mBufferSize-1); + if (at(i).adjacent(start, count)) { + (*this)[i].extend(start, count); + return; } } + append(UpdatePair(start, count)); +} - if (isInsideBuffer(target)) { - // start-end may be partially inside buffer - if (!isInsideBuffer(start, end)) { - addItems(target, target+end-start, totalCount); +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 (isInsideBuffer(start)) { - addItems(target+(mBufferPosition+mBufferSize-start), target+end-start, totalCount); + else if (pair.contains(comp)) { + // Subtraction from middle is not applicable in mediawall use cases + (*this)[i].subtract(start, count); } - else { // end is inside buffer - addItems(target, target+mBufferPosition-start-1, totalCount); + // 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); } } } -bool HgScrollBufferManager::isInsideBuffer(int pos) +void UpdateBuffer::shiftLeft(int startingFrom, int amount) { - return (pos >= mBufferPosition && pos < mBufferPosition+mBufferSize); -} - -bool HgScrollBufferManager::isInsideBuffer(int start, int end) -{ - INFO("Buffer:" << mBufferPosition << "-" << mBufferPosition+mBufferSize-1); - INFO("Change:" << start << "-" << end); + FUNC_LOG; - if (isInsideBuffer(start)) { - return true; - } - if (isInsideBuffer(end)) { - return true; + int itemCount = this->count(); + for (int i = 0; i < itemCount; i++) { + if (at(i).start() >= startingFrom) { + (*this)[i].shiftLeft(amount); + } } - if (start < mBufferPosition && end >= mBufferPosition+mBufferSize) { - return true; - } - - INFO("Buffer not affected"); - return false; } -