photosgallery/viewframework/medialists/src/glxitemlist.cpp
changeset 0 4e91876724a2
child 20 d1bdfdf534bd
equal deleted inserted replaced
-1:000000000000 0:4e91876724a2
       
     1 /*
       
     2 * Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). 
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:    List of media items
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 
       
    20 
       
    21 // my include
       
    22 #include "glxitemlist.h"
       
    23 
       
    24 // system includes
       
    25 #include <glxassert.h>
       
    26 #include <glxtracer.h>
       
    27 #include <mpxcollectionpath.h>
       
    28 
       
    29 // user includes
       
    30 #include "mglxitemlistobserver.h"
       
    31 #include "mglxmediapool.h"
       
    32 
       
    33 using namespace NGlxItemList;
       
    34 
       
    35 namespace NGlxItemList
       
    36     {
       
    37     // -----------------------------------------------------------------------------
       
    38     // Remove item from list and remove linking
       
    39     // -----------------------------------------------------------------------------
       
    40     //
       
    41     void RemoveItem( TInt aIndex, RArray< TGlxMedia >& aList, 
       
    42             MGlxMediaUser& aMediaUser )
       
    43         {
       
    44         // Remove link between TGlxMedia and CGlxMedia
       
    45         aList[ aIndex ].SetMedia( NULL, aMediaUser );
       
    46         // Remove from list
       
    47         aList.Remove( aIndex ); 
       
    48         }        
       
    49     
       
    50     /** 
       
    51      * Class to contain change processing current state and difference length
       
    52      *
       
    53      * @author Aki Vanhatalo
       
    54      */
       
    55     NONSHARABLE_CLASS ( TDifferenceInfo )
       
    56         {
       
    57     public:
       
    58         /** Constructor */
       
    59         TDifferenceInfo()
       
    60             {
       
    61             iSourceIndex = 0;
       
    62             iTargetIndex = 0;
       
    63             iCount = 0;
       
    64             }
       
    65         
       
    66         /// current index on source list. Modification strategy can change this
       
    67         /// to skip items in the source list.
       
    68         TInt iSourceIndex;
       
    69         
       
    70         /// current index on target list. Modification strategy can change this
       
    71         /// to skip items in the target list.
       
    72         TInt iTargetIndex;
       
    73         
       
    74         /// lenght of change: number of items that would need to be added or removed
       
    75         /// to eliminate difference between source and target list. It is up to 
       
    76         /// the modification strategy to decide if it actually eliminates the 
       
    77         /// difference or simply observes it. Any change of this variable by 
       
    78         /// a strategy is ignored.
       
    79         TInt iCount;
       
    80         };
       
    81 
       
    82     /** 
       
    83      * Modification strategy interface
       
    84      *
       
    85      * This interface will be called when differences are found in source and 
       
    86      * target list, and instructs how the differences should be solved,
       
    87      *
       
    88      * Use of modification strategies allows the same difference evaluation 
       
    89      * algorithm to be used for both calculating required target array space, and 
       
    90      * for doing the actual changes to into the target array.
       
    91      * 
       
    92      * @author Aki Vanhatalo
       
    93      */
       
    94     class MListModificationStrategy
       
    95         {
       
    96     public:
       
    97         /**
       
    98          * There is a difference in lists that requires removing items for 
       
    99          * the difference to be eliminated
       
   100          * @param aInfo iTargetIndex index from which to remove items
       
   101          *              iCount number of items that would need to be removed
       
   102          *              iSourceIndex index in source list (to allow adjustement)
       
   103          */
       
   104         virtual void Remove( TDifferenceInfo& aInfo ) = 0;
       
   105         
       
   106         /**
       
   107          * There is a difference in lists that requires adding items from source 
       
   108          * list to target lits for the difference to be eliminated
       
   109          * @param aInfo iTargetIndex index in target list at which to insert
       
   110          *              iCount number of items that would need to be copied
       
   111          *              iSourceIndex index in source list from which to copy 
       
   112          */
       
   113         virtual void Insert( TDifferenceInfo& aInfo ) = 0;
       
   114         };
       
   115         
       
   116     /** 
       
   117      * Modification strategy that calculates how much slack space is required for 
       
   118      * differences in lists to be fixed (i.e., TChangeListStategy to be executed)
       
   119      * 
       
   120      * @author Aki Vanhatalo
       
   121      */
       
   122     NONSHARABLE_CLASS( TCalculateRequiredSpaceStrategy ) : 
       
   123             public MListModificationStrategy
       
   124         {
       
   125     public:
       
   126         /** Constructor */
       
   127         TCalculateRequiredSpaceStrategy( const RArray< TGlxMedia >& aTargetList )
       
   128             {
       
   129             // Pick up initial required space
       
   130             iRequiredSpace = aTargetList.Count();
       
   131             // Set initial value
       
   132             iCurrentLengthDifference = 0;
       
   133             }
       
   134      
       
   135         // from MListModificationStrategy
       
   136         void Remove( TDifferenceInfo& aInfo )
       
   137             {
       
   138             // Currently required space is reduced
       
   139             iCurrentLengthDifference -= aInfo.iCount;
       
   140             
       
   141             // Skip over "removed" items (items were not removed, simply counted)
       
   142             aInfo.iTargetIndex += aInfo.iCount;
       
   143             }
       
   144             
       
   145         // from MListModificationStrategy
       
   146         void Insert( TDifferenceInfo& aInfo )
       
   147             {
       
   148             // Currently required space is increased
       
   149             iCurrentLengthDifference += aInfo.iCount;
       
   150             // Check if the currently required space is the maximum space
       
   151             iRequiredSpace = Max( iCurrentLengthDifference, iRequiredSpace );
       
   152             
       
   153             // Skip over source items, but not target items, since nothing was 
       
   154             // actually inserted
       
   155             aInfo.iSourceIndex += aInfo.iCount;
       
   156             }
       
   157         
       
   158     public: // allow public since internal to CGlxItemList implementation
       
   159         /// space required in item list for TChangeListStategy to be 
       
   160         /// executable without fail
       
   161         TInt iRequiredSpace; 
       
   162         /// current difference in space needed; iRequiredSpace is a max of this
       
   163         TInt iCurrentLengthDifference;
       
   164         };
       
   165 
       
   166     /** 
       
   167      * Modification strategy that fixes the differences between source and target 
       
   168      * list. 
       
   169      *
       
   170      * Note: Implementation assumes that there is enough slack space in target 
       
   171      * list for insertion operations to always succeed. (I.e., 
       
   172      * TCalculateRequiredSpaceStrategy needs to be used first to find out how much 
       
   173      * slack is required)
       
   174      * 
       
   175      * @author Aki Vanhatalo
       
   176      */
       
   177     NONSHARABLE_CLASS( TChangeListStategy ) : public MListModificationStrategy
       
   178         {
       
   179     public:
       
   180         /** Constructor */
       
   181         TChangeListStategy( const CMPXCollectionPath& aSourceList, 
       
   182             const TGlxIdSpaceId& aIdSpaceId, RArray< TGlxMedia >& aTargetList, 
       
   183                 const MGlxMediaPool& aMediaPool, MGlxMediaUser& aMediaUser, 
       
   184                     MGlxItemListObserver& aObserver )
       
   185                         : iSource( aSourceList ), iTarget( aTargetList ),
       
   186                             iMediaPool( aMediaPool ), iIdSpaceId( aIdSpaceId ), 
       
   187                                 iMediaUser( aMediaUser ), iObserver( aObserver )
       
   188             {
       
   189             // do nothing (more)
       
   190             }
       
   191         
       
   192         // from MListModificationStrategy
       
   193         void Remove( TDifferenceInfo& aInfo )
       
   194             {
       
   195             // Remove block from target list, start from end and work backwards (quicker)
       
   196             for ( TInt i = aInfo.iTargetIndex + aInfo.iCount - 1; 
       
   197                   i >= aInfo.iTargetIndex; 
       
   198                   i-- )
       
   199                 {
       
   200                 RemoveItem( i, iTarget, iMediaUser );
       
   201                 }
       
   202             
       
   203             // Notify observer    
       
   204             iObserver.HandleItemsRemoved( aInfo.iTargetIndex, aInfo.iCount );
       
   205             }
       
   206             
       
   207         // from MListModificationStrategy
       
   208         void Insert( TDifferenceInfo& aInfo )
       
   209             {
       
   210             // Insert all items to target list
       
   211             InsertBlock( aInfo );
       
   212             
       
   213             // Notify observer    
       
   214             iObserver.HandleItemsAdded( aInfo.iTargetIndex, aInfo.iCount );
       
   215             
       
   216             // Skip over inserted and source items
       
   217             aInfo.iTargetIndex += aInfo.iCount;
       
   218             aInfo.iSourceIndex += aInfo.iCount;
       
   219             }
       
   220         
       
   221     private:
       
   222         /**
       
   223          * Copy a block of items from source to target
       
   224          * @param aInfo See @ref MListModificationStrategy::Insert
       
   225          */
       
   226         inline void InsertBlock( const TDifferenceInfo& aInfo )
       
   227             {
       
   228             TInt sourceIndex = aInfo.iSourceIndex;
       
   229 
       
   230             // Copy items from source list to target list
       
   231             TInt untilTargetIndex = aInfo.iTargetIndex + aInfo.iCount;
       
   232             for ( TInt targetIndex = aInfo.iTargetIndex; 
       
   233                     targetIndex < untilTargetIndex; targetIndex++ )
       
   234                 {
       
   235                 // IdOfIndex will not return "invalid id", since the code is only 
       
   236                 // asking for ids within the range of the list
       
   237                 TGlxMedia item( TGlxMediaId( iSource.IdOfIndex( sourceIndex ) ) );
       
   238                 // set media, assumes that media is either NULL, or has an 
       
   239                 // allocation made for a new user. Builds a link to CGlxMedia 
       
   240                 // object, and from CGlxMedia object to iMediaUser
       
   241                 item.SetMedia( iMediaPool.Media( iIdSpaceId, item.Id() ), iMediaUser );
       
   242                 // add to item list
       
   243                 // ignore error, cannot fail since reservation made
       
   244                 iTarget.Insert( item, targetIndex ); 
       
   245                 
       
   246                 sourceIndex++;
       
   247                 }
       
   248             }    
       
   249         
       
   250     private:
       
   251         /// Source list to copy ids from. 
       
   252         const CMPXCollectionPath& iSource;
       
   253         /// Target list to which to copy / from which to remove
       
   254         RArray< TGlxMedia >& iTarget;
       
   255         /// Provider of media objects
       
   256         const MGlxMediaPool& iMediaPool;
       
   257         /// Id space id, for looking up items from media provider
       
   258         TGlxIdSpaceId iIdSpaceId;
       
   259         /// User of the new media objects, if any TGlxMedia-CGlxMedia links are added
       
   260         MGlxMediaUser& iMediaUser;
       
   261         /// Observer for changes in list
       
   262         MGlxItemListObserver& iObserver;
       
   263         };
       
   264         
       
   265     } // namespace NGlxItemList
       
   266     
       
   267 // -----------------------------------------------------------------------------
       
   268 // CGlxItemList implementation
       
   269 // -----------------------------------------------------------------------------
       
   270 
       
   271 // -----------------------------------------------------------------------------
       
   272 // Two-phase constructor
       
   273 // -----------------------------------------------------------------------------
       
   274 //
       
   275 CGlxItemList* CGlxItemList::NewL( const TGlxIdSpaceId& aIdSpaceId, 
       
   276         MGlxItemListObserver& aObserver, MGlxMediaUser& aMediaUser )
       
   277     {
       
   278     TRACER("CGlxItemList::NewL");    
       
   279     // No ConstructL function currently, so simply return an instance
       
   280     return new (ELeave) CGlxItemList( aIdSpaceId, aObserver, aMediaUser );
       
   281     }
       
   282             
       
   283 // -----------------------------------------------------------------------------
       
   284 // Constructor
       
   285 // -----------------------------------------------------------------------------
       
   286 //
       
   287 CGlxItemList::CGlxItemList( const TGlxIdSpaceId& aIdSpaceId, 
       
   288     MGlxItemListObserver& aObserver, MGlxMediaUser& aMediaUser )
       
   289         : iIdSpaceId( aIdSpaceId ), iMediaUser( aMediaUser ), 
       
   290             iObserver( aObserver )
       
   291     {
       
   292     TRACER("CGlxItemList::Default Constructor");
       
   293     
       
   294     __TEST_INVARIANT;
       
   295     }
       
   296 
       
   297 // -----------------------------------------------------------------------------
       
   298 // Destructor
       
   299 // ------------------------------------------------------------------------- ----
       
   300 //
       
   301 CGlxItemList::~CGlxItemList()
       
   302     {
       
   303     TRACER( "CGlxItemList::~CGlxItemList" );
       
   304 
       
   305     // Remove user of media objects
       
   306     TInt count = iItems.Count();
       
   307     for ( TInt i = 0; i < count; i++ )
       
   308         {
       
   309         iItems[ i ].SetMedia( NULL, iMediaUser );
       
   310         }    
       
   311     
       
   312     iItems.Close();
       
   313     }
       
   314 
       
   315 // -----------------------------------------------------------------------------
       
   316 // Synchronise contents of the list with the collection path
       
   317 // -----------------------------------------------------------------------------
       
   318 //
       
   319 void CGlxItemList::SetContentsL( const CMPXCollectionPath& aSource, 
       
   320         const MGlxMediaPool& aMediaPool )
       
   321     {
       
   322     TRACER( "CGlxItemList::SetContentsL" );
       
   323     __TEST_INVARIANT;
       
   324     
       
   325     // calculate how much space needs to be reserved in the items array
       
   326     // before doing any modifications. This allows the update to
       
   327     // be completed successfully or not at all if leave occurs
       
   328     TCalculateRequiredSpaceStrategy calculateDeltaLengthStrategy ( iItems );
       
   329     ProcessDifferences ( aSource, calculateDeltaLengthStrategy ); 
       
   330     
       
   331     // make reservation so that paths can be syncronised without fail
       
   332     iItems.ReserveL( iItems.Count() + 
       
   333         calculateDeltaLengthStrategy.iRequiredSpace );
       
   334     
       
   335     // now implement the changes (notifies observer)
       
   336     TChangeListStategy changeListStrategy( aSource, iIdSpaceId, iItems, 
       
   337         aMediaPool, iMediaUser, iObserver );
       
   338     ProcessDifferences ( aSource, changeListStrategy ); 
       
   339     
       
   340     // Remove slack space from list
       
   341     iItems.Compress();
       
   342   
       
   343     __TEST_INVARIANT;
       
   344     }
       
   345 
       
   346 // -----------------------------------------------------------------------------
       
   347 // Find differences in new and old path, and ask strategy to act on them
       
   348 // -----------------------------------------------------------------------------
       
   349 //
       
   350 void CGlxItemList::ProcessDifferences( const CMPXCollectionPath& aSource, 
       
   351         MListModificationStrategy& aModificationStrategy )
       
   352     {
       
   353     TRACER("CGlxItemList::ProcessDifferences");
       
   354     
       
   355     TDifferenceInfo info;
       
   356     
       
   357     // Find the indexes of the first items that are the same on both
       
   358     // source and target list, until there are no more items in either list.
       
   359     // (If either one of the lists reaches its end, return the next index 
       
   360     // from the last one, i.e., count.)
       
   361     // Ask the strategy object to handle cases in which items 
       
   362     // have been inserted to the list or removed from the list.
       
   363     // 
       
   364     // Note: not using a temporary variable for "target count", since count can change
       
   365     // during the loop, as items may get added/removed.
       
   366     TInt sourceCount = aSource.Count();
       
   367     while ( info.iSourceIndex < sourceCount || info.iTargetIndex < Count() )
       
   368         {
       
   369         // find next matching source and target indexes
       
   370         TInt sourceMatchIndex = KErrNotFound;
       
   371         TInt targetMatchIndex = KErrNotFound;
       
   372         FindMatchingItems( info, aSource, sourceMatchIndex, targetMatchIndex );
       
   373         
       
   374         // Process differences
       
   375         ProcessRemove( targetMatchIndex, info, aModificationStrategy );
       
   376         ProcessInsert( sourceMatchIndex, info, aModificationStrategy );
       
   377         
       
   378         // go to next items
       
   379         info.iSourceIndex++;
       
   380         info.iTargetIndex++;
       
   381         }
       
   382     }
       
   383 
       
   384 // -----------------------------------------------------------------------------
       
   385 // Find the next matching item in source and target path
       
   386 // inline in cpp file only, so will be inlined in arm compiler
       
   387 // -----------------------------------------------------------------------------
       
   388 //
       
   389 inline void CGlxItemList::FindMatchingItems( const TDifferenceInfo& aInfo, 
       
   390         const CMPXCollectionPath& aSource, TInt& aSourceMatchIndex, 
       
   391         TInt& aTargetMatchIndex )
       
   392     {
       
   393     TRACER("CGlxItemList::FindMatchingItems");
       
   394     
       
   395     // For each remaining source item, test each remaining target item, until 
       
   396     // match found
       
   397     TInt sourceCount = aSource.Count();
       
   398     TInt targetCount = Count();
       
   399     TInt sourceIndex = aInfo.iSourceIndex;
       
   400 
       
   401     // The most common case is that the next items match (no items changed
       
   402     // at the indexes being examined). The loop is optimised for that case.
       
   403     // The loop (including the inner loop) terminates on the first round, 
       
   404     // since matching items are immediately found.
       
   405     // The case optimised next is the "items were removed from middle" case,
       
   406     // as this is likely to happen in an active view. In that case, the upper
       
   407     // loop is run only once, and the inner loop finds the match.
       
   408     // The slowest cases are when items were inserted or removed from the end.
       
   409     // In that case both loops may run almost to end, so that is slow.
       
   410     // However, that case should be a rare case (while view is active),
       
   411     // so from performance perspective this loop should be ok, even 
       
   412     // though it has N*M complexity.
       
   413     while ( sourceIndex < sourceCount )
       
   414         {
       
   415         // get next source item id
       
   416         // IdOfIndex will not return "invalid id", since the code is only 
       
   417         // asking for ids within the range of the list, but even if it did,
       
   418         // the logic would still work
       
   419         TGlxMediaId sourceId ( aSource.IdOfIndex( sourceIndex ) ); 
       
   420         
       
   421         // try to find a target item that matches the source item
       
   422         // (cannot use RArray::Find, since it does not allow specifying
       
   423         //  start index)
       
   424         TInt targetIndex = aInfo.iTargetIndex;
       
   425         while ( targetIndex < targetCount)
       
   426             {
       
   427             if ( sourceId == iItems[ targetIndex ].Id() )
       
   428                 {
       
   429                 // Found match, store return values
       
   430                 aTargetMatchIndex = targetIndex;
       
   431                 aSourceMatchIndex = sourceIndex;
       
   432                 return;
       
   433                 }
       
   434             targetIndex++;
       
   435             }
       
   436         sourceIndex++;
       
   437         }
       
   438     
       
   439     // No match found
       
   440     aTargetMatchIndex = Count();
       
   441     aSourceMatchIndex = aSource.Count();
       
   442     }
       
   443     
       
   444 // -----------------------------------------------------------------------------
       
   445 // Process the need to remove items to eliminate differences
       
   446 // inline in cpp file only, so will be inlined in arm compiler
       
   447 // -----------------------------------------------------------------------------
       
   448 //
       
   449 inline void CGlxItemList::ProcessRemove( TInt aTargetMatchIndex, 
       
   450         TDifferenceInfo& aInfo, MListModificationStrategy& aStrategy )
       
   451     {
       
   452     TRACER("CGlxItemList::ProcessRemove");
       
   453     
       
   454     aInfo.iCount = aTargetMatchIndex - aInfo.iTargetIndex;
       
   455     if ( aInfo.iCount > 0 )
       
   456         {
       
   457         aStrategy.Remove( aInfo );
       
   458         }
       
   459     }
       
   460 
       
   461 // -----------------------------------------------------------------------------
       
   462 // Process the need to insert items to eliminate differences
       
   463 // inline in cpp file only, so will be inlined in arm compiler
       
   464 // -----------------------------------------------------------------------------
       
   465 //
       
   466 inline void CGlxItemList::ProcessInsert( TInt aSourceMatchIndex, 
       
   467         TDifferenceInfo& aInfo, MListModificationStrategy& aStrategy )
       
   468     {
       
   469     TRACER("CGlxItemList::ProcessInsert");
       
   470     
       
   471     aInfo.iCount = aSourceMatchIndex - aInfo.iSourceIndex;
       
   472     if ( aInfo.iCount > 0 )
       
   473         {
       
   474         aStrategy.Insert( aInfo );
       
   475         }
       
   476     }
       
   477     
       
   478 // -----------------------------------------------------------------------------
       
   479 // Remove an item from the list
       
   480 // -----------------------------------------------------------------------------
       
   481 //
       
   482 void CGlxItemList::Remove( const TGlxIdSpaceId& aIdSpaceId, const TGlxMediaId& aItemId )
       
   483     {
       
   484     TRACER("CGlxItemList::Remove");
       
   485     
       
   486     // Assume id space id does not have to be checked for performance reasons, i.e., id 
       
   487     // space id is usually, if not always, correct
       
   488     TInt index = Index(aIdSpaceId, aItemId ); 
       
   489     if ( KErrNotFound != index && iIdSpaceId == aIdSpaceId )    
       
   490         {
       
   491         // Remove item from the list
       
   492         RemoveItem( index, iItems, iMediaUser );
       
   493         // Notify observer    
       
   494         iObserver.HandleItemsRemoved( index, 1 );
       
   495         }
       
   496     __TEST_INVARIANT;
       
   497     }
       
   498     
       
   499 // -----------------------------------------------------------------------------
       
   500 // Remove any pointers to the media object at the specified index
       
   501 // -----------------------------------------------------------------------------
       
   502 //
       
   503 void CGlxItemList::RemoveReference( TInt aIndex )
       
   504     {
       
   505     TRACER( "CGlxItemList::RemoveReference" );
       
   506     __TEST_INVARIANT;
       
   507 
       
   508     GLX_ASSERT_DEBUG( 0 <= aIndex && aIndex < iItems.Count(), 
       
   509         Panic(EGlxPanicIllegalArgument), "removing reference for item out of bounds");
       
   510 
       
   511     // remove the reference to the media
       
   512     (*this)[ aIndex ].SetMedia( NULL, iMediaUser );
       
   513     
       
   514     __TEST_INVARIANT;
       
   515     }
       
   516     
       
   517 // -----------------------------------------------------------------------------
       
   518 // Return index by id
       
   519 // -----------------------------------------------------------------------------
       
   520 //
       
   521 TInt CGlxItemList::Index( const TGlxIdSpaceId& aIdSpaceId, const TGlxMediaId& aId ) const
       
   522     {
       
   523     TRACER("CGlxItemList::Index");
       
   524     
       
   525     if ( iIdSpaceId == aIdSpaceId )
       
   526         {
       
   527         // set up comparison functor
       
   528         TIdentityRelation<TGlxMedia> match ( &TGlxMedia::MatchById );
       
   529         // create dummy object to compare against
       
   530         TGlxMedia mediaToCompare( aId );
       
   531         // try to find; may return KErrNotFound
       
   532         return iItems.Find( mediaToCompare, match );
       
   533         }
       
   534         
       
   535     return KErrNotFound;
       
   536     }
       
   537 
       
   538 // ---------------------------------------------------------------------------
       
   539 // Test invariant
       
   540 // ---------------------------------------------------------------------------
       
   541 void CGlxItemList::__DbgTestInvariant() const
       
   542     {
       
   543     #ifdef _DEBUG
       
   544     
       
   545     __ASSERT_DEBUG( &iObserver , Panic( EGlxPanicIllegalState ) ); // Null observer
       
   546     __ASSERT_DEBUG( iIdSpaceId != KGlxIdNone , Panic( EGlxPanicIllegalState ) ); // No id space
       
   547 
       
   548     // Make sure the list contains no duplication
       
   549     TInt count = iItems.Count();
       
   550     for ( TInt indexA = 0; indexA < count; ++indexA )
       
   551         {
       
   552         for ( TInt indexB = indexA + 1; indexB < count; ++indexB )
       
   553             {
       
   554             __ASSERT_DEBUG( !TGlxMedia::MatchById( iItems[indexA], iItems[indexB] ), 
       
   555                 Panic( EGlxPanicIllegalState ) ); // Duplicate item
       
   556             }
       
   557         }
       
   558 
       
   559     #endif // _DEBUG
       
   560     }