/*
* 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: List of media items, which has focus
*
*/
// my include
#include "glxnavigablelist.h"
// system includes
#include <mpxcollectionpath.h>
#include <glxtracer.h>
#include <glxlog.h>
// user includes
#include "glxlistutils.h"
#include "glxmedia.h"
#include "glxstaticitemlist.h"
#include "mglxnavigablelistobserver.h"
using namespace NGlxNavigableList;
namespace NGlxNavigableList
{
/**
* Strategy to process selection indexes
*/
class MSelectionIndexStrategy
{
public:
/**
* Process a single selection index
* @param aSelectedItemIndex index in selected item index array to process
*/
virtual void Process( TInt aSelectedItemIndex ) = 0;
};
/**
* Strategy to remove selection indexes
*/
NONSHARABLE_CLASS( TRemoveSelectedItemIndexStrategy ) :
public MSelectionIndexStrategy
{
public:
/**
* Constructor
* @param aSelectedItemIndices Selected item index array
*/
TRemoveSelectedItemIndexStrategy( RArray< TInt>& aSelectedItemIndices )
: iSelectedItemIndices( aSelectedItemIndices )
{
}
/** See @ref MSelectionIndexStrategy */
void Process( TInt aSelectedItemIndex )
{
iSelectedItemIndices.Remove( aSelectedItemIndex );
}
private:
/// Selected item indexes
RArray< TInt>& iSelectedItemIndices;
};
/**
* Strategy to move selection indexes
*/
NONSHARABLE_CLASS( TMoveSelectedItemIndexStrategy ) :
public MSelectionIndexStrategy
{
public:
/**
* Constructor
* @param aMoveCount a number of items to move index by
*/
TMoveSelectedItemIndexStrategy( RArray< TInt>& aSelectedItemIndices,
TInt aMoveCount )
: iSelectedItemIndices( aSelectedItemIndices )
{
iMoveCount = aMoveCount;
}
/** See @ref MSelectionIndexStrategy */
void Process( TInt aSelectedItemIndex )
{
iSelectedItemIndices[ aSelectedItemIndex ] += iMoveCount;
}
private:
/// The count of items to move by
TInt iMoveCount;
/// Selected item indexes
RArray< TInt>& iSelectedItemIndices;
};
} // namespace NGlxNavigableList
// -----------------------------------------------------------------------------
// Two-phase constructor
// -----------------------------------------------------------------------------
//
CGlxNavigableList* CGlxNavigableList::NewL( const TGlxIdSpaceId& aIdSpaceId,
MGlxNavigableListObserver& aObserver, MGlxMediaUser& aMediaUser )
{
TRACER("CGlxNavigableList::NewL");
CGlxNavigableList* self = new (ELeave) CGlxNavigableList( aObserver );
CleanupStack::PushL( self );
self->ConstructL( aIdSpaceId, aMediaUser );
CleanupStack::Pop( self );
return self;
}
// -----------------------------------------------------------------------------
// Constructor
// -----------------------------------------------------------------------------
//
CGlxNavigableList::CGlxNavigableList( MGlxNavigableListObserver& aObserver ) :
iFocusInitialPosition( NGlxListDefs::EFocusFirst ),
iObserver( aObserver )
{
TRACER("CGlxNavigableList::CGlxNavigableList");
iFocusIndex = KErrNotFound;
}
// -----------------------------------------------------------------------------
// Second-phase constructor
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::ConstructL( const TGlxIdSpaceId& aIdSpaceId,
MGlxMediaUser& aMediaUser )
{
TRACER("CGlxNavigableList::ConstructL");
iItemList = CGlxStaticItemList::NewL( aIdSpaceId, *this, aMediaUser );
}
// -----------------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------------
//
CGlxNavigableList::~CGlxNavigableList()
{
TRACER( "CGlxNavigableList::~CGlxNavigableList" );
iSelectedItemIndices.Close();
delete iItemList;
}
// -----------------------------------------------------------------------------
// return id space id
// -----------------------------------------------------------------------------
//
const TGlxIdSpaceId& CGlxNavigableList::IdSpaceId() const
{
TRACER( "CGlxNavigableList::IdSpaceId");
return iItemList->IdSpaceId();
}
// -----------------------------------------------------------------------------
// Synchronise contents of the list with the collection path
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::SetContentsL( const CMPXCollectionPath& aPath,
const MGlxMediaPool& aMediaPool )
{
TRACER( "CGlxNavigableList::SetContentsL" );
iItemList->SetContentsL( aPath, aMediaPool );
}
// -----------------------------------------------------------------------------
// Re-orders contents of the list with the collection path
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::ReorderContentsL( const CMPXCollectionPath& aPath,
const MGlxMediaPool& aMediaPool )
{
TRACER( "CGlxNavigableList::ReorderContentsL" );
__TEST_INVARIANT;
// List contents have been reordered. If we called SetContentsL directly,
// the client would get semi-random (but correct) sequence of added/removed
// notifications. To provide a cleaner notification, send only "all removed"
// and "all added" notifications.
// store the focus and selection temporarily for restoring later-
// (store ids; cannot store index, since indexes may change upon reorder)
TGlxMediaId focusedItemId = FocusId();
RArray< TGlxMediaId > selectedItemIds;
CleanupClosePushL( selectedItemIds );
SelectionL( selectedItemIds );
// send notification "all items removed"
ClearContentsL( aMediaPool );
// set contents to reordered list, this removes focus and selection.
// send notification of "all items added" (in new order)
SetContentsL( aPath, aMediaPool );
// restore focus and selection
SetFocus( focusedItemId );
SelectL( selectedItemIds );
CleanupStack::PopAndDestroy( &selectedItemIds );
__TEST_INVARIANT;
}
// -----------------------------------------------------------------------------
// Remove an item form the list
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::Remove( const TGlxIdSpaceId& aIdSpaceId,
const TGlxMediaId& aItemId )
{
TRACER( "CGlxNavigableList::Remove" );
iItemList->Remove( aIdSpaceId, aItemId );
}
// -----------------------------------------------------------------------------
// Remove any pointers to the media object at the specified index
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::RemoveReference( TInt aIndex )
{
TRACER( "CGlxNavigableList::RemoveReference" );
iItemList->RemoveReference( aIndex );
}
// -----------------------------------------------------------------------------
// Add a static item
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::AddStaticItemL( CGlxMedia* aStaticItem,
NGlxListDefs::TInsertionPosition aTargetPosition )
{
TRACER( "CGlxNavigableList::AddStaticItemL" );
iItemList->AddStaticItemL( aStaticItem, aTargetPosition );
}
// -----------------------------------------------------------------------------
// Enable/disable static items
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::SetStaticItemsEnabled( TBool aEnabled )
{
TRACER( "CGlxNavigableList::SetStaticItemsEnabled" );
iItemList->SetStaticItemsEnabled( aEnabled );
}
// -----------------------------------------------------------------------------
// return ETrue if static items are enabled
// -----------------------------------------------------------------------------
//
TBool CGlxNavigableList::IsStaticItemsEnabled() const
{
TRACER( "CGlxNavigableList::IsStaticItemsEnabled");
return iItemList->IsStaticItemsEnabled();
}
// -----------------------------------------------------------------------------
// Return count
// -----------------------------------------------------------------------------
//
TInt CGlxNavigableList::Count( NGlxListDefs::TCountType aType ) const
{
TRACER( "CGlxNavigableList::Count");
return iItemList->Count( aType );
}
// -----------------------------------------------------------------------------
// Sets the initial focus position, first or last item
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::SetFocusInitialPosition(NGlxListDefs::TFocusInitialPosition aFocusInitialPosition)
{
TRACER( "CGlxNavigableList::SetFocusInitialPosition");
iFocusInitialPosition = aFocusInitialPosition;
SetInitialFocus();
}
// -----------------------------------------------------------------------------
// Return focus index
// -----------------------------------------------------------------------------
//
TInt CGlxNavigableList::FocusIndex() const
{
TRACER( "CGlxNavigableList::FocusIndex");
return iFocusIndex;
}
// -----------------------------------------------------------------------------
// Set focus index
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::SetFocus( NGlxListDefs::TFocusSetType aType, TInt aValue )
{
TRACER( "CGlxNavigableList::SetFocus");
GLX_LOG_INFO1( "CGlxNavigableList::SetFocus: Entry: %d", aValue );
__TEST_INVARIANT;
// Observer callback needs to know the focus change type
NGlxListDefs::TFocusChangeType type = NGlxListDefs::EUnknown;
TInt oldIndex = iFocusIndex;
switch ( aType )
{
case NGlxListDefs::EAbsolute:
type = SetFocus( aValue );
break;
case NGlxListDefs::ERelative:
type = MoveFocus( aValue );
break;
default:
Panic( EGlxPanicIllegalArgument ); // Unsupported focus change type
break;
}
// notify observer if focus changed
NotifyFocusChange( type, oldIndex, iFocusIndex != oldIndex );
__TEST_INVARIANT;
GLX_LOG_INFO( "CGlxNavigableList::SetFocusL: Exit" );
}
// -----------------------------------------------------------------------------
// Set focus by id
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline void CGlxNavigableList::SetFocus( const TGlxMediaId& aItemId )
{
TRACER( "CGlxNavigableList::SetFocus");
TInt index = Index( IdSpaceId(), aItemId );
if ( KErrNotFound != index )
{
// Focus the item if it still exists in the new list
SetFocus( NGlxListDefs::EAbsolute, index );
}
}
// -----------------------------------------------------------------------------
// Set focus index
// -----------------------------------------------------------------------------
//
NGlxListDefs::TFocusChangeType CGlxNavigableList::SetFocus( TInt aIndex )
{
TRACER( "CGlxNavigableList::SetFocus");
__ASSERT_ALWAYS( aIndex >= 0 && aIndex < Count(), Panic( EGlxPanicIllegalArgument ) );
iFocusIndex = aIndex;
// it is not possible to know which direction focus was moving, since
// client did not specify it. Looping of the list makes it impossible to
// compare the old and new index.
return NGlxListDefs::EUnknown;
}
// -----------------------------------------------------------------------------
// Move focus index
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline NGlxListDefs::TFocusChangeType CGlxNavigableList::MoveFocus( TInt aDelta )
{
TRACER( "CGlxNavigableList::MoveFocus");
// Moving focus in not possible when the list is empty
if ( KErrNotFound != iFocusIndex )
{
// Move focus index
iFocusIndex += aDelta;
// wrap back into the loop
iFocusIndex = GlxListUtils::NormalizedIndex( iFocusIndex, Count() );
// determine direction of focus change
if ( aDelta > 0 )
{
return NGlxListDefs::EForward;
}
// client won't be notified if aDelta is 0, so it is suitable
// to return EBackward, even if focus does not move
return NGlxListDefs::EBackward;
}
return NGlxListDefs::EUnknown;
}
// -----------------------------------------------------------------------------
// Notify observers of focus change
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::NotifyFocusChange( NGlxListDefs::TFocusChangeType aType,
TInt aOldIndex, TBool aNotify )
{
TRACER( "CGlxNavigableList::NotifyFocusChange");
if ( aNotify )
{
iObserver.HandleFocusChanged( aType, iFocusIndex, aOldIndex );
}
}
// -----------------------------------------------------------------------------
// Sets the initial focus
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::SetInitialFocus()
{
TRACER( "CGlxNavigableList::SetInitialFocus");
// Only need to set focus if there are some items
if ( Count() )
{
if ( iFocusInitialPosition == NGlxListDefs::EFocusFirst )
{
iFocusIndex = 0;
}
else // iFocusInitialPosition == NGlxListDefs::EFocusLast
{
iFocusIndex = Count() - 1;
}
}
}
// -----------------------------------------------------------------------------
// Resets the focus to the initial position
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::ResetFocus()
{
TRACER( "CGlxNavigableList::ResetFocus");
TInt oldFocusIndex = iFocusIndex;
SetInitialFocus();
// notify of focus change after resetting focus
// Only need to notify if there are some items
if( Count() )
{
NotifyFocusChange( NGlxListDefs::EUnknown, oldFocusIndex, ETrue );
}
}
// -----------------------------------------------------------------------------
// Return item by index
// -----------------------------------------------------------------------------
//
TGlxMedia& CGlxNavigableList::Item( TInt aIndex )
{
TRACER( "CGlxNavigableList::Item");
return iItemList->Item( aIndex );
}
// -----------------------------------------------------------------------------
// Return index by id
// -----------------------------------------------------------------------------
//
TInt CGlxNavigableList::Index(const TGlxIdSpaceId& aIdSpaceId, const TGlxMediaId& aId ) const
{
TRACER( "CGlxNavigableList::Index");
return iItemList->Index(aIdSpaceId, aId );
}
// -----------------------------------------------------------------------------
// Return whether item at index is selected
// -----------------------------------------------------------------------------
//
TBool CGlxNavigableList::IsSelected(TInt aIndex) const
{
TRACER( "CGlxNavigableList::IsSelected");
return ( KErrNotFound != iSelectedItemIndices.FindInOrder( aIndex ) );
}
// -----------------------------------------------------------------------------
// Select/deselect item
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::SetSelectedL( TInt aIndex, TBool aSelected )
{
TRACER( "CGlxNavigableList::SetSelectedL");
GLX_LOG_INFO2( "CGlxNavigableList::SetSelectedL: index %d selected %b",
aIndex, aSelected );
__TEST_INVARIANT;
if ( aSelected )
{
SelectL( aIndex );
}
else
{
Deselect( aIndex );
}
__TEST_INVARIANT;
}
// -----------------------------------------------------------------------------
// Return id of focused item or KGlxIdNone
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline TGlxMediaId CGlxNavigableList::FocusId() const
{
TRACER( "CGlxNavigableList::FocusId");
if ( KErrNotFound != iFocusIndex )
{
return iItemList->Item( iFocusIndex ).Id();
}
return KGlxIdNone;
}
// -----------------------------------------------------------------------------
// Return id of focused item or KGlxIdNone
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline void CGlxNavigableList::SelectionL( RArray< TGlxMediaId >& aItemIds ) const
{
TRACER( "CGlxNavigableList::SelectionL");
aItemIds.Reset();
// Reserve full required space to avoid reallocations during loop
aItemIds.ReserveL( iSelectedItemIndices.Count() );
TInt count = iSelectedItemIndices.Count();
for ( TInt i = 0; i < count; ++i )
{
aItemIds.AppendL( iItemList->Item( iSelectedItemIndices[ i ] ).Id() );
}
}
// -----------------------------------------------------------------------------
// Clear list contents
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline void CGlxNavigableList::ClearContentsL( const MGlxMediaPool& aMediaPool )
{
TRACER( "CGlxNavigableList::ClearContentsL");
// Create a path with no items in order to empty the list
CMPXCollectionPath* emptyPath = CMPXCollectionPath::NewL();
CleanupStack::PushL( emptyPath );
iItemList->SetContentsL( *emptyPath, aMediaPool );
CleanupStack::PopAndDestroy( emptyPath );
}
// -----------------------------------------------------------------------------
// Select items by id
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline void CGlxNavigableList::SelectL( const RArray< TGlxMediaId >& aItemIds )
{
TRACER( "CGlxNavigableList::SelectL");
// Reserve free space for the full selection, so that the operation can
// be atomic
ReserveFreeSpaceInSelectionL( aItemIds.Count() );
// pick id space id locally to avoid re-retrieving during the loop
TGlxIdSpaceId idSpaceId = IdSpaceId();
// Select the requested items
TInt count = aItemIds.Count();
for ( TInt i = 0; i < count; i++ )
{
TInt index = Index( idSpaceId, aItemIds[ i ] );
if ( index != KErrNotFound )
{
Select( index );
}
}
}
// -----------------------------------------------------------------------------
// Select item
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline void CGlxNavigableList::SelectL( TInt aIndex )
{
TRACER( "CGlxNavigableList::SelectL");
// Don't allow static items to be selected
if ( !Item( aIndex ).IsStatic() )
{
ReserveFreeSpaceInSelectionL( 1 );
Select( aIndex );
}
}
// -----------------------------------------------------------------------------
// Select item. Must call ReserveFreeSpaceInSelectionL before
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::Select( TInt aIndex )
{
TRACER( "CGlxNavigableList::Select");
__ASSERT_DEBUG( _iSelectionReserveCount > 0, Panic( EGlxPanicNoReservation ) ); // No reservation made
__DEBUG_ONLY( _iSelectionReserveCount-- );
// returns KErrNone if inserted successfully, KErrAlreadyExists if already exists
if ( KErrNone == iSelectedItemIndices.InsertInOrder( aIndex ) )
{
iObserver.HandleItemSelected( aIndex, ETrue );
}
}
// -----------------------------------------------------------------------------
// Reserve free space is selected item indexes array
// -----------------------------------------------------------------------------
//
inline void CGlxNavigableList::ReserveFreeSpaceInSelectionL( TInt aCount )
{
TRACER( "CGlxNavigableList::ReserveFreeSpaceInSelectionL");
iSelectedItemIndices.ReserveL( iSelectedItemIndices.Count() + aCount );
// store reservation count so can protect agains Select(...) being called
// without reservation
__DEBUG_ONLY( _iSelectionReserveCount = aCount );
}
// -----------------------------------------------------------------------------
// Deselect item
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline void CGlxNavigableList::Deselect( TInt aIndex )
{
TRACER( "CGlxNavigableList::Deselect");
// item has been deselected, remove if found
TInt selectionArrayIndex = iSelectedItemIndices.FindInOrder( aIndex );
if ( KErrNotFound != selectionArrayIndex )
{
// remove from array of selected items
iSelectedItemIndices.Remove( selectionArrayIndex );
// free unused memory from selected item indexes array
// (this call may be a performance bottleneck if executing unmark all
// in a list of many thousands of items when most of them are marked.
// if it is proved to be a performance bottleneck, it may be sensible
// to count how many times Deselect has been called, an only
// compress every x times.)
//iSelectedItemIndices.Compress();
// selection has changed, notify observer
iObserver.HandleItemSelected( aIndex, EFalse );
}
// else: ignore if item was not selected
}
// -----------------------------------------------------------------------------
// Return selected items
// -----------------------------------------------------------------------------
//
const TArray< TInt > CGlxNavigableList::SelectedItemIndices() const
{
TRACER( "CGlxNavigableList::SelectedItemIndices");
return iSelectedItemIndices.Array();
}
// -----------------------------------------------------------------------------
// Handle items being added
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::HandleItemsAdded( TInt aFirstInsertedIndex,
TInt aCount )
{
TRACER( "CGlxNavigableList::HandleItemsAdded" );
// Do not test invariant in the beginning of this function. Right now,
// the state is not valid, since iItemList's state has already changed, and
// this function will align the state of this object with iItemList.
// Move selection indexes from insertion point
TMoveSelectedItemIndexStrategy moveStrategy( iSelectedItemIndices, aCount );
ProcessSelectionItemIndicesBetweenIndexes( aFirstInsertedIndex,
Count( NGlxListDefs::ECountAll ), moveStrategy );
// move focus
TInt oldFocusIndex = iFocusIndex;
TInt notifyOfFocusChange = UpdateFocusAfterInsertion( aFirstInsertedIndex, aCount );
// Adjust index by amount of static items
iObserver.HandleItemsAdded( aFirstInsertedIndex, aCount );
// notify of focus change after having notified of items being added,
// so that focus index points to the correct real item on client side
NotifyFocusChange( NGlxListDefs::EUnknown, oldFocusIndex,
notifyOfFocusChange );
__TEST_INVARIANT;
}
// -----------------------------------------------------------------------------
// Move focus after items have been inserted
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline TInt CGlxNavigableList::UpdateFocusAfterInsertion( TInt aInsertionIndex,
TInt aInsertionCount )
{
TRACER( "CGlxNavigableList::UpdateFocusAfterInsertion" );
// Move focus if index is changed.
TBool notifyObserver = MoveFocusIfIndexChanged( aInsertionIndex, aInsertionCount );
// Set focus to initial focus position if not previously set. Test also that count larger
// than zero, just to be safe for future maintenance of other classes
if ( KErrNotFound == iFocusIndex && Count( NGlxListDefs::ECountAll ) > 0 )
{
GLX_LOG_INFO("iFocusIndex is KErrNotFound");
// Set initial focus position
SetInitialFocus();
notifyObserver = ETrue;
}
return notifyObserver;
}
// -----------------------------------------------------------------------------
// Move focus if index is changed.
// -----------------------------------------------------------------------------
//
TBool CGlxNavigableList::MoveFocusIfIndexChanged( TInt aChangedIndex, TInt aMoveBy )
{
TRACER( "CGlxNavigableList::MoveFocusIfIndexChanged" );
if ( iFocusIndex >= aChangedIndex )
{
iFocusIndex += aMoveBy;
return ETrue; // notify observer
}
return EFalse; // don't notify observer
}
// -----------------------------------------------------------------------------
// Handle items being removed
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::HandleItemsRemoved( TInt aRemovedFromIndex, TInt aCount )
{
TRACER( "CGlxNavigableList::HandleItemsRemoved" );
// Do not test invariant in the beginning of this function. Right now,
// the state is not valid, since iItemList's state has already changed, and
// this function will align the state of this object with iItemList.
// update selection
UpdateSelectionAfterRemove( aRemovedFromIndex, aCount );
TInt oldFocusIndex = iFocusIndex;
TBool notifyOfFocusChange = UpdateFocusAfterRemoval( aRemovedFromIndex,
aCount );
// notify observer
iObserver.HandleItemsRemoved( aRemovedFromIndex, aCount );
// notify of focus change after having notified of items being removed,
// so that focus index points to the correct real item on client side
NotifyFocusChange( NGlxListDefs::EUnknown, oldFocusIndex,
notifyOfFocusChange );
__TEST_INVARIANT;
}
// -----------------------------------------------------------------------------
// Update selection after remove
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline void CGlxNavigableList::UpdateSelectionAfterRemove( TInt aRemovedFromIndex,
TInt aRemovedCount )
{
TRACER( "CGlxNavigableList::UpdateSelectionAfterRemove");
// remove items from selected list that were removed
TRemoveSelectedItemIndexStrategy removeStrategy( iSelectedItemIndices );
ProcessSelectionItemIndicesBetweenIndexes( aRemovedFromIndex,
aRemovedFromIndex + aRemovedCount, removeStrategy );
// move selection indexes from removal point
TMoveSelectedItemIndexStrategy moveStrategy( iSelectedItemIndices, -aRemovedCount );
ProcessSelectionItemIndicesBetweenIndexes( aRemovedFromIndex + aRemovedCount,
KMaxTInt, moveStrategy );
}
// -----------------------------------------------------------------------------
// Move focus after items have been removed
// inline private member function only in cpp file, so will be inlined in arm
// (ok as long as called only once)
// -----------------------------------------------------------------------------
//
inline TBool CGlxNavigableList::UpdateFocusAfterRemoval( TInt aRemovedFromIndex,
TInt aRemovedCount )
{
TRACER( "CGlxNavigableList::UpdateFocusAfterRemoval");
// Assume focus will be changed (simplifies code below)
TBool notifyObserver = EFalse;
// move focus if it is after the last removed item (incl. list has become empty)
if ( iFocusIndex >= aRemovedFromIndex + aRemovedCount )
{
iFocusIndex -= aRemovedCount;
// although the focus did not logically change in this case, notify
// anyway to simplify client code
notifyObserver = ETrue;
}
// move focus if it is on removed items
else if ( iFocusIndex >= aRemovedFromIndex )
{
iFocusIndex = Min( aRemovedFromIndex, Count() - 1 );
notifyObserver = ETrue;
}
// else focus is before removed items, so do nothing
return notifyObserver;
}
// -----------------------------------------------------------------------------
// Remove selection indices between two indexes
// -----------------------------------------------------------------------------
//
void CGlxNavigableList::ProcessSelectionItemIndicesBetweenIndexes(
TInt aFromIndex, TInt aToIndex, MSelectionIndexStrategy& aStrategy )
{
TRACER("CGlxNavigableList::ProcessSelectionItemIndicesBetweenIndexes");
// Find first selection item index that points to an index larger or equal
// than aFromIndex
TInt selectedItemIndex = FindFirstSelectedItemIndexBefore( aToIndex );
// Process until finds a selection item index pointing to an item smaller
// than aFromIndex
while ( selectedItemIndex >= 0 &&
iSelectedItemIndices[ selectedItemIndex ] >= aFromIndex )
{
aStrategy.Process( selectedItemIndex );
selectedItemIndex--;
}
}
// -----------------------------------------------------------------------------
// Return first selection item index on or after
// -----------------------------------------------------------------------------
//
inline TInt CGlxNavigableList::FindFirstSelectedItemIndexBefore( TInt aMaxIndex )
{
TRACER( "CGlxNavigableList::FindFirstSelectedItemIndexBefore");
// Find first selection item index that points to an index larger or equal
// than aAtLeastIndex
TInt i = iSelectedItemIndices.Count() - 1;
while ( i >= 0 )
{
if ( iSelectedItemIndices[i] < aMaxIndex )
{
break;
}
i--;
}
// KErrNotFound == -1, which will be returned if the loop did not break
return i;
}
// ---------------------------------------------------------------------------
// Test invariant
// ---------------------------------------------------------------------------
void CGlxNavigableList::__DbgTestInvariant() const
{
TRACER( "CGlxNavigableList::__DbgTestInvariant");
#if 0
//def _DEBUG
// focus index must be KErrNotFound if list is empty
__ASSERT_DEBUG( iFocusIndex == KErrNotFound || iItemList->Count( NGlxListDefs::ECountAll ), Panic( EGlxPanicIllegalState ) );
// focus index must be between 0 and count if list is not empty
__ASSERT_DEBUG( ( iFocusIndex >= 0 &&
iFocusIndex < iItemList->Count( NGlxListDefs::ECountAll ) ) ||
!iItemList->Count( NGlxListDefs::ECountAll ),
Panic( EGlxPanicIllegalState ) );
// max count amount of items can be selected
__ASSERT_DEBUG( iSelectedItemIndices.Count() <= iItemList->Count( NGlxListDefs::ECountAll ), Panic( EGlxPanicIllegalState ) );
// test selection item index array
TInt previousSelectedIndex = -1;
for ( TInt i = 0; i < iSelectedItemIndices.Count(); i++ )
{
// index must be between zero and count
__ASSERT_DEBUG( iSelectedItemIndices[ i ] >= 0 && iSelectedItemIndices[ i ] < iItemList->Count( NGlxListDefs::ECountAll ), Panic( EGlxPanicIllegalState ) );
// index must not be in order
__ASSERT_DEBUG( iSelectedItemIndices[ i ] > previousSelectedIndex, Panic( EGlxPanicIllegalState ) );
// prepare next round
previousSelectedIndex = iSelectedItemIndices[ i ];
}
#endif // _DEBUG
}