--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/photosgallery/viewframework/medialists/src/glxlistwindow.cpp Thu Dec 17 08:45:44 2009 +0200
@@ -0,0 +1,743 @@
+/*
+* Copyright (c) 2008-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: Window to a list of objects
+*
+*/
+
+
+
+
+#include "glxlistwindow.h"
+
+#include "glxpanic.h"
+#include "glxlistutils.h" //for GlxListUtils
+#include <glxtracer.h> //For logging
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+// CGlxDataWindow
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+//
+class CGlxDataWindow : public CBase
+ {
+ friend class TGlxWindowIterator;
+public:
+ /** Destructor*/
+ ~CGlxDataWindow()
+ {
+ iObjects.Close();
+ }
+
+ /** Destroy contents of the window */
+ void Destroy()
+ {
+ iObjects.ResetAndDestroy();
+ }
+
+ /** Reserve max window size */
+ inline void ReserveL( TInt aMaxCount )
+ {
+ iObjects.ReserveL( aMaxCount );
+ }
+
+ /** Preparet the window to accept items */
+ void Initialize( const CGlxListWindow::TRange& aRange, TInt aTotalSize )
+ {
+ __TEST_INVARIANT;
+
+ iTotalSize = aTotalSize;
+ // use GlxListUtils::NormalizedIndex instead of Normalize to avoid
+ // execution-order dependency to the setting of iTotalSize
+ iWindowStartIndex = GlxListUtils::NormalizedIndex( aRange.iStartIndex, iTotalSize );
+
+ // all existing pointers in the array are NULL, as they have been
+ // set so by PopulateExistingAndUnuseOld
+
+ // add sufficient number of new null pointers
+ for ( TInt i = iObjects.Count(); i < aRange.iLength; i++ )
+ {
+ // Append cannot fail since reservation has been made
+ ( void ) iObjects.Append( NULL );
+ }
+
+ // remove unnecessary pointers
+ for ( TInt i = iObjects.Count() - 1; i >= aRange.iLength; i-- )
+ {
+ iObjects.Remove( i );
+ }
+
+ __TEST_INVARIANT;
+ }
+
+ /** @return an iterator that can iterate throught the items in window */
+ inline TGlxWindowIterator Iterator() const
+ {
+ return TGlxWindowIterator( *this );
+ }
+
+ /** @return whether the list index is within range */
+ inline TBool InRange( TInt aListIndex ) const
+ {
+ return KErrNotFound != WindowIndex( aListIndex );
+ }
+
+ inline CBase*& operator[] ( TInt aListIndex )
+ {
+ return iObjects[ WindowIndex( aListIndex ) ];
+ }
+
+ /** @return the length of the window */
+ inline TInt Size() const
+ {
+ return iObjects.Count();
+ }
+
+ /** @return the total length of the list */
+ inline TInt TotalSize() const
+ {
+ return iTotalSize;
+ }
+
+private:
+ /** convert a window index to list index */
+ TInt ListIndex( TInt aWindowIndex ) const
+ {
+ __ASSERT_DEBUG( aWindowIndex >= 0 && aWindowIndex < Size(), Panic( EGlxPanicIllegalArgument ) );
+ return Normalize( aWindowIndex + iWindowStartIndex );
+ }
+
+ /** convert a list index to window index */
+ /// @todo Make this private
+ TInt WindowIndex( TInt aListIndex ) const
+ {
+ if ( KErrNotFound == aListIndex )
+ {
+ return KErrNotFound;
+ }
+
+ __ASSERT_DEBUG( aListIndex >= 0 && aListIndex < iTotalSize, Panic( EGlxPanicIllegalArgument ) );
+
+ // Convert the list index to a window index
+ TInt windowIndex = Normalize( aListIndex - iWindowStartIndex );
+ if ( windowIndex >= Size() )
+ {
+ return KErrNotFound; // Does not fit to the window
+ }
+ return windowIndex;
+ }
+
+ /** Normalise index to list length */
+ inline TInt Normalize( TInt aIndex ) const
+ {
+ return GlxListUtils::NormalizedIndex( aIndex, iTotalSize );
+ }
+
+ /** Test state */
+ void __DbgTestInvariant() const
+ {
+ __ASSERT_DEBUG(iTotalSize >= 0 && iTotalSize < 50000, Panic(EGlxPanicIllegalState)); // Sanity check
+ __ASSERT_DEBUG(iObjects.Count() <= iTotalSize, Panic(EGlxPanicIllegalState));
+ }
+
+private:
+ /// Full size of the list
+ TInt iTotalSize;
+
+ /// Index of the first object in iWindow in the list,
+ /// i.e., the list index from which the window starts from
+ TInt iWindowStartIndex;
+
+ /// List of objects in the window, starting from iWindowStartIndex
+ RPointerArray< CBase > iObjects;
+ };
+
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Constructor
+// -----------------------------------------------------------------------------
+TGlxWindowIterator::TGlxWindowIterator( const CGlxDataWindow& aWindow )
+ {
+ TRACER("TGlxWindowIterator::TGlxWindowIterator");
+
+ iWindow = &aWindow;
+ iWindowIndex = 0;
+ }
+
+// -----------------------------------------------------------------------------
+// Constructor
+// -----------------------------------------------------------------------------
+EXPORT_C TGlxWindowIterator::TGlxWindowIterator( const TGlxWindowIterator& aIterator )
+ {
+ TRACER("TGlxWindowIterator::TGlxWindowIterator");
+
+ iWindow = aIterator.iWindow;
+ iWindowIndex = aIterator.iWindowIndex;
+ }
+
+// -----------------------------------------------------------------------------
+// ++
+// -----------------------------------------------------------------------------
+EXPORT_C TInt TGlxWindowIterator::operator++( TInt )
+ {
+ TRACER("TGlxWindowIterator::operator++");
+
+ if ( iWindowIndex == iWindow->iObjects.Count() )
+ {
+ return KErrNotFound;
+ }
+ return iWindow->ListIndex( iWindowIndex++ );
+ }
+
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// TChange
+// -----------------------------------------------------------------------------
+//
+CGlxListWindow::TChange::TChange( TInt aNewFocusIndex, TInt aNewTotalSize, TChangeType aChangeType,
+ TInt aFirstChangedIndex, TInt aLastChangedIndex )
+ {
+ TRACER("TChange::Default Constructor");
+
+ iNewFocusIndex = aNewFocusIndex;
+ iNewTotalSize = aNewTotalSize;
+ iChangeType = aChangeType;
+ iFirstChangedIndex = aFirstChangedIndex;
+ iLastChangedIndex = aLastChangedIndex;
+ }
+
+// -----------------------------------------------------------------------------
+// Constructor
+// -----------------------------------------------------------------------------
+//
+EXPORT_C CGlxListWindow::CGlxListWindow( MGlxWindowObjectFactory& aObjectFactory )
+ : iObjectFactory( aObjectFactory )
+ {
+ TRACER("CGlxListWindow::Default Constructor");
+
+ iFocusIndex = KErrNotFound; // No items, hence no focus
+ }
+
+// -----------------------------------------------------------------------------
+// ConstructL
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CGlxListWindow::ConstructL()
+ {
+ TRACER("CGlxListWindow::ConstructL");
+
+ iWorkingWindow = new (ELeave) CGlxDataWindow;
+ iWindow = new (ELeave) CGlxDataWindow;
+ // default range offsets are 0, 0, which means that only the focused item
+ // in in the window by default. Create an object for the focus. This
+ // makes it optional to call SetRangeOffsetsL if range remains 0,0
+ iUnusedObjects.AppendL( iObjectFactory.CreateObjectL() );
+ }
+
+// -----------------------------------------------------------------------------
+// Destructor
+// -----------------------------------------------------------------------------
+//
+EXPORT_C CGlxListWindow::~CGlxListWindow()
+ {
+ TRACER("CGlxListWindow::Destructor");
+
+ // objects may exist in either iWindow or object pool (iUnusedObjects)
+ if ( iWindow )
+ {
+ // this call could be removed, if could call Cleanup here
+ // currently deriving classes implement CleanupObject, so calling it
+ // here is not possible => requires a different object as factory
+ iWindow->Destroy();
+ delete iWindow;
+ }
+ iUnusedObjects.ResetAndDestroy();
+ // the working window does not own anything
+ delete iWorkingWindow;
+ }
+
+// -----------------------------------------------------------------------------
+// Cleans up remaining objects in the window
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CGlxListWindow::Cleanup()
+ {
+ TRACER("CGlxListWindow::Cleanup");
+
+ // make the window empty. this will clean up all remaining objects.
+ Update( TChange( KErrNotFound, 0, EChangeObjectsRemoved, 0, iWindow->TotalSize() - 1 ) );
+ }
+
+// -----------------------------------------------------------------------------
+// SetRangeOffsetsL
+// -----------------------------------------------------------------------------
+// DEPRECATED
+EXPORT_C void CGlxListWindow::SetRangeOffsetsL( TInt aFrontOffset, TInt aRearOffset )
+ {
+ TRACER("CGlxListWindow::SetRangeOffsetsL");
+
+ SetRangeOffsetsL( iFocusIndex, iWindow->TotalSize(), aFrontOffset, aRearOffset );
+ }
+
+// -----------------------------------------------------------------------------
+// SetRangeOffsetsL
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CGlxListWindow::SetRangeOffsetsL( TInt aFocusIndex,
+ TInt aTotalSize, TInt aFrontOffset, TInt aRearOffset )
+ {
+ TRACER("CGlxListWindow::SetRangeOffsetsL");
+
+ __ASSERT_DEBUG( aFrontOffset <= 0 && aRearOffset >= 0, Panic( EGlxPanicIllegalArgument ) );
+ __ASSERT_DEBUG( ( 0 <= aFocusIndex && aFocusIndex < aTotalSize ) ||
+ ( KErrNotFound == aFocusIndex && 0 == aTotalSize ),
+ Panic( EGlxPanicIllegalArgument ) );
+
+ iFrontOffset = aFrontOffset;
+ iRearOffset = aRearOffset;
+
+ // only ever expand the range. there is no use case to do the otherwise,
+ // so don't bother optimising that case by freeing unnecessary objects.
+
+ // all or no objects may be in use at any time - reserve max count in all arrays
+ TInt maxObjectCount = aRearOffset - aFrontOffset + 1;
+ iWorkingWindow->ReserveL( maxObjectCount );
+ iWindow->ReserveL( maxObjectCount );
+ iUnusedObjects.ReserveL( maxObjectCount );
+
+ // create enough new objects into the unused objects pool
+ // objects are either in the main window or in the pool
+ for ( TInt i = iWindow->Size() + iUnusedObjects.Count(); i < maxObjectCount; i++ )
+ {
+ // cannot fail since reservation made above
+ ( void ) iUnusedObjects.Append( iObjectFactory.CreateObjectL() );
+ }
+
+ Update( TChange( aFocusIndex, aTotalSize, EChangeNone, 0, 0 ) );
+ }
+
+// -----------------------------------------------------------------------------
+// Iterator
+// -----------------------------------------------------------------------------
+//
+EXPORT_C TGlxWindowIterator CGlxListWindow::Iterator() const
+ {
+ TRACER("CGlxListWindow::Iterator");
+
+ return iWindow->Iterator();
+ }
+
+// -----------------------------------------------------------------------------
+// SetFocusIndexL
+// -----------------------------------------------------------------------------
+// DEPRICATED
+EXPORT_C void CGlxListWindow::SetFocusIndex( TInt aFocusIndex )
+ {
+ TRACER("CGlxListWindow::SetFocusIndex");
+
+ SetFocusIndex( aFocusIndex, iWindow->TotalSize() );
+ }
+
+// -----------------------------------------------------------------------------
+// SetFocusIndexL
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CGlxListWindow::SetFocusIndex( TInt aFocusIndex, TInt aTotalSize )
+ {
+ TRACER("CGlxListWindow::SetFocusIndex");
+
+ __ASSERT_DEBUG( ( 0 <= aFocusIndex && aFocusIndex < aTotalSize ) ||
+ ( KErrNotFound == aFocusIndex && 0 == aTotalSize ),
+ Panic( EGlxPanicIllegalArgument ) );
+ __TEST_INVARIANT;
+
+ Update( TChange( aFocusIndex, aTotalSize, EChangeNone, 0, 0 ) );
+
+ __TEST_INVARIANT;
+ }
+
+// -----------------------------------------------------------------------------
+// AddObjects
+// -----------------------------------------------------------------------------
+// DEPRICATED
+EXPORT_C void CGlxListWindow::AddObjects( TInt aFirstNewIndex, TInt aLastNewIndex )
+ {
+ TRACER("CGlxListWindow::AddObjects");
+
+ TInt newItemCount = aLastNewIndex - aFirstNewIndex + 1;
+ TInt newTotalSize = iWindow->TotalSize() + newItemCount;
+
+ // Check if it is necessary to move focus
+ TInt newFocusIndex = iFocusIndex;
+ if ( KErrNotFound == newFocusIndex )
+ {
+ // List is empty, so set focus to the first item
+ __ASSERT_DEBUG(iWindow->TotalSize() == 0, Panic(EGlxPanicIllegalState)); // Must have had an empty list to have no focus
+ newFocusIndex = 0;
+ }
+ else if (newFocusIndex >= aFirstNewIndex)
+ {
+ // Move focus, so that the focus stays on the same item that was
+ // focused before items were added
+ newFocusIndex += newItemCount;
+ }
+
+ AddObjects( newFocusIndex, newTotalSize, aFirstNewIndex, aLastNewIndex );
+ }
+
+// -----------------------------------------------------------------------------
+// AddObjects
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CGlxListWindow::AddObjects( TInt aFocusIndex,
+ TInt aTotalSize, TInt aFirstNewIndex, TInt aLastNewIndex )
+ {
+ TRACER("CGlxListWindow::AddObjects");
+
+ __ASSERT_DEBUG( 0 <= aFirstNewIndex && aFirstNewIndex <= aLastNewIndex &&
+ aLastNewIndex < aTotalSize &&
+ 0 <= aFocusIndex && aFocusIndex < aTotalSize,
+ Panic( EGlxPanicIllegalArgument ) );
+ __TEST_INVARIANT;
+
+ Update( TChange( aFocusIndex, aTotalSize, EChangeObjectsAdded,
+ aFirstNewIndex, aLastNewIndex ) );
+
+ __TEST_INVARIANT;
+ }
+
+// -----------------------------------------------------------------------------
+// RemoveObjects
+// -----------------------------------------------------------------------------
+// DEPRICATED
+EXPORT_C void CGlxListWindow::RemoveObjects( TInt aFirstRemovedIndex, TInt aLastRemovedIndex )
+ {
+ TRACER("CGlxListWindow::RemoveObjects");
+
+ TInt itemsRemovedCount = aLastRemovedIndex - aFirstRemovedIndex + 1;
+ TInt newTotalSize = iWindow->TotalSize() - itemsRemovedCount;
+ TInt newFocusIndex = iFocusIndex;
+
+ // check number of items left
+ if (newTotalSize == 0)
+ {
+ // all items have been removed
+ newFocusIndex = KErrNotFound;
+ }
+ else // still some items in the list
+ {
+ // Focus should stay on the same item if possible. If not, it should
+ // go to the first previous item still existing. If none, it should go to
+ // first (=first next) item.
+
+ // @todo: Do not maintain focus index in this class!!! This is broken
+
+ // Update focus if the removal point was before the focus
+ if (aFirstRemovedIndex <= newFocusIndex)
+ {
+ TInt firstToFocusCount = newFocusIndex - aFirstRemovedIndex + 1;
+ newFocusIndex -= Min(itemsRemovedCount, firstToFocusCount);
+
+ if (newFocusIndex < 0)
+ {
+ newFocusIndex = 0;
+ }
+ }
+ }
+
+ RemoveObjects( newFocusIndex, newTotalSize, aFirstRemovedIndex, aLastRemovedIndex );
+ }
+
+// -----------------------------------------------------------------------------
+// RemoveObjects
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CGlxListWindow::RemoveObjects( TInt aFocusIndex,
+ TInt aTotalSize, TInt aFirstRemovedIndex, TInt aLastRemovedIndex )
+ {
+ TRACER("CGlxListWindow::RemoveObjects");
+
+ __ASSERT_DEBUG( 0 <= aFirstRemovedIndex && aFirstRemovedIndex <= aLastRemovedIndex &&
+ ( ( 0 <= aFocusIndex && aFocusIndex < aTotalSize ) ||
+ ( KErrNotFound == aFocusIndex && aTotalSize == 0 ) ),
+ Panic( EGlxPanicIllegalArgument ) );
+ __TEST_INVARIANT;
+
+ Update( TChange( aFocusIndex, aTotalSize, EChangeObjectsRemoved,
+ aFirstRemovedIndex, aLastRemovedIndex ) );
+
+ __TEST_INVARIANT;
+ }
+
+// -----------------------------------------------------------------------------
+// Update
+// -----------------------------------------------------------------------------
+//
+void CGlxListWindow::Update( const TChange& aChange )
+ {
+ TRACER("CGlxListWindow::Update");
+
+ // (in a list of: |abcdefghijklm|
+ // iWindow: |----efghi----|
+ // iWorkingWindow: undefined state
+ // iUnusedObjects: XXX (3 objects, in reality only contains objects if list
+ // is shorter than max window length)
+
+ // Prepare the working window to accept objects
+ iWorkingWindow->Initialize( Range( aChange ), aChange.iNewTotalSize );
+
+ // iWindow: |----efghi----|
+ // iWorkingWindow: |------00000--|
+ // iUnusedObjects: XXX
+
+ // move unused objects to pool, and reusable objects to working window
+ PopulateExistingAndUnuseOld( aChange );
+
+ // iWindow: |----efghi----|
+ // iWorkingWindow: |------ghi00--|
+ // iUnusedObjects: XXXXX (2 objects added, used to be e and f)
+
+ // set the current window to working window and vice versa.
+ // do this before populating new items, since when the deriving class
+ // is asked to set up the items, it is better to allow it to access the
+ // objects from the window (only objects from main window can be accessed
+ // externally)
+ SwapWindows();
+ iFocusIndex = aChange.iNewFocusIndex;
+
+ // iWindow: |------ghi00--|
+ // iWorkingWindow: |----xxxxx----| = undefined
+ // iUnusedObjects: XXXXX
+
+ // populate objects in working window that did not exist in current window
+ PopulateNew();
+
+ // iWindow: |------ghijk--|
+ // iWorkingWindow: |----xxxxx----| = undefined
+ // iUnusedObjects: XXX (2 objects moved to iWindow)
+ }
+
+// -----------------------------------------------------------------------------
+// PopulateExistingAndUnuseOld
+// -----------------------------------------------------------------------------
+//
+void CGlxListWindow::PopulateExistingAndUnuseOld( const TChange& aChange )
+ {
+ TRACER("CGlxListWindow::PopulateExistingAndUnuseOld");
+
+ // move objects that are needed after the change into the working window,
+ // and objects that are not needed into the object pool
+ TGlxWindowIterator currentWindowIterator = iWindow->Iterator();
+ TInt index = 0;
+ while ( KErrNotFound != ( index = currentWindowIterator++ ) )
+ {
+ // translate to what the list index would be after the change.
+ TInt indexAfterChange = IndexAfterChange( index, aChange );
+ // item is needed in the new window if it is in the range of the new window
+ if ( iWorkingWindow->InRange( indexAfterChange ) )
+ {
+ // move the object from the current window to the working window
+ ( *iWorkingWindow )[ indexAfterChange ] = ( *iWindow )[ index ];
+ }
+ else
+ {
+ // ask deriving class to clean up the object (free resources, etc).
+ // do this first, so that the deriving class can access the window
+ // by index, if needed
+ iObjectFactory.CleanupObject( index, *( *iWindow )[ index ] );
+ // add the object to the unused objects pool
+ // cannot fail since reservation made
+ ( void ) iUnusedObjects.Append( ( *iWindow )[ index ] );
+ }
+
+ // clear the pointer in the existing window. it is not strictly necessary
+ // to do here, but saves a loop from doing it in CGlxDataWindow::Initialize
+ ( *iWindow )[ index ] = NULL;
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// Populate objects that were not reused
+// -----------------------------------------------------------------------------
+//
+void CGlxListWindow::PopulateNew()
+ {
+ TRACER("CGlxListWindow::PopulateNew");
+
+ // iWindow is now partially constructed
+ // populate all pointers that are null
+ TGlxWindowIterator newWindowIterator = iWindow->Iterator();
+ TInt index = 0;
+ while ( KErrNotFound != ( index = newWindowIterator++ ) )
+ {
+ if ( NULL == ( *iWindow )[ index ] )
+ {
+ CBase* object = UnusedObject();
+ ( *iWindow )[ index ] = object;
+ // configure the object
+ iObjectFactory.SetupObject( index, *object );
+ }
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// Reuse
+// -----------------------------------------------------------------------------
+//
+void CGlxListWindow::SwapWindows()
+ {
+ TRACER("CGlxListWindow::SwapWindows");
+
+ CGlxDataWindow* temp = iWorkingWindow;
+ iWorkingWindow = iWindow;
+ iWindow = temp;
+ }
+
+// -----------------------------------------------------------------------------
+// Return an object from the pool, and remove it from the pool
+// -----------------------------------------------------------------------------
+//
+CBase* CGlxListWindow::UnusedObject()
+ {
+ TRACER("CGlxListWindow::UnusedObject");
+
+ TInt lastObjectIndex = iUnusedObjects.Count() - 1;
+ CBase* object = iUnusedObjects[ lastObjectIndex ];
+ iUnusedObjects.Remove( lastObjectIndex );
+ return object;
+ }
+
+// -----------------------------------------------------------------------------
+// Access object by index
+// -----------------------------------------------------------------------------
+//
+EXPORT_C CBase* CGlxListWindow::At( TInt aIndex )
+ {
+ TRACER("CGlxListWindow::At");
+
+ if ( iWindow->InRange( aIndex ) )
+ {
+ return ( *iWindow )[ aIndex ];
+ }
+ return NULL;
+ }
+
+// -----------------------------------------------------------------------------
+// Access object by index
+// -----------------------------------------------------------------------------
+//
+EXPORT_C const CBase* CGlxListWindow::At( TInt aIndex ) const
+ {
+ TRACER("CGlxListWindow::At");
+
+ return const_cast< CGlxListWindow* >( this )->At( aIndex );
+ }
+
+// -----------------------------------------------------------------------------
+// Range
+// -----------------------------------------------------------------------------
+//
+CGlxListWindow::TRange CGlxListWindow::Range( const TChange& aChange ) const
+ {
+ TRACER("CGlxListWindow::Range");
+
+ TRange range;
+ range.iLength = iRearOffset - iFrontOffset + 1; // Add 1 for focus;
+
+ // Start index is always zero if the whole list fits into the window
+ if ( range.iLength >= aChange.iNewTotalSize )
+ {
+ // All items are within range
+ range.iStartIndex = 0;
+ range.iLength = aChange.iNewTotalSize;
+ }
+ else
+ {
+ // Not all items in the list fit within the range
+ range.iStartIndex = GlxListUtils::NormalizedIndex(
+ aChange.iNewFocusIndex + iFrontOffset, aChange.iNewTotalSize );
+
+ // check that ranges don't ever meet at the other side of the loop
+ __ASSERT_DEBUG( range.iStartIndex != GlxListUtils::NormalizedIndex( range.iStartIndex + range.iLength, aChange.iNewTotalSize )
+ || ( iFrontOffset == 0 && iRearOffset == 0 ), Panic( EGlxPanicLogicError ) );
+ }
+
+ return range;
+ }
+
+// -----------------------------------------------------------------------------
+// Convert a list index to a list index in a changed list
+// -----------------------------------------------------------------------------
+//
+TInt CGlxListWindow::IndexAfterChange( TInt aIndex,
+ const TChange& aChange ) const
+ {
+ TRACER("CGlxListWindow::IndexAfterChange");
+
+ // Convert the new list index into an old list index
+ TInt changeCount = aChange.iLastChangedIndex - aChange.iFirstChangedIndex + 1;
+
+ TInt indexAfterChange = aIndex;
+ if ( aChange.iFirstChangedIndex <= aIndex )
+ {
+ if ( aChange.iChangeType == EChangeObjectsRemoved )
+ {
+ // f l (first, last)
+ // old: |---NNN---||||||------|
+ // new: |------||||||------|
+
+ // If the index is between the indexes, the change will remove it
+ if ( aIndex <= aChange.iLastChangedIndex )
+ {
+ // The index is one of the new items. It does not exist in the old window
+ indexAfterChange = KErrNotFound;
+ }
+ else
+ {
+ // The index is after the items that will be removed. Adjust the index by "removing" the items.
+ indexAfterChange -= changeCount;
+ }
+ }
+ else if ( aChange.iChangeType == EChangeObjectsAdded )
+ {
+ // Adjust the index by "adding" the items before the index
+ // old: |------||||||------|
+ // new: |---NNN---||||||------|
+ // f l (first, last)
+ indexAfterChange += changeCount;
+ }
+ // if EChangeNone, no adjustment needed
+ }
+ // else no adjustment need, since the change happened after the index
+
+ return indexAfterChange;
+ }
+
+// ---------------------------------------------------------------------------
+// Test invariant
+// ---------------------------------------------------------------------------
+void CGlxListWindow::__DbgTestInvariant() const
+ {
+ TRACER("CGlxListWindow::__DbgTestInvariant");
+
+ __ASSERT_DEBUG( ( iFocusIndex >= 0 && iFocusIndex < iWindow->TotalSize() ) ||
+ ( KErrNotFound == iFocusIndex && 0 == iWindow->TotalSize() ),
+ Panic( EGlxPanicIllegalState ) );
+ __ASSERT_DEBUG( iFrontOffset <= 0 && iRearOffset >= 0, Panic( EGlxPanicIllegalState ) );
+ }