photosgallery/viewframework/medialists/src/glxlistwindow.cpp
changeset 0 4e91876724a2
child 18 bcb43dc84c44
child 20 d1bdfdf534bd
--- /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 ) );
+    }