diff -r 000000000000 -r 4e91876724a2 photosgallery/viewframework/medialists/src/glxlistwindow.cpp --- /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 //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 ) ); + }