engine/collectionframework/thumbnailcreator/src/glxtnvolumedatabase.cpp
changeset 23 74c9f037fd5d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/engine/collectionframework/thumbnailcreator/src/glxtnvolumedatabase.cpp	Fri Mar 19 09:28:59 2010 +0200
@@ -0,0 +1,820 @@
+/*
+* 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:   Implementation of CGlxtnVolumeDatabase
+*
+*/
+
+
+
+/**
+ * @internal reviewed 31/07/2007 by Simon Brooks
+ */
+
+#include "glxtnvolumedatabase.h"
+
+#include <glxtracer.h>
+#include <glxpanic.h>
+#include <s32file.h>
+
+#include "glxtnfileinfo.h"
+#include "mglxtnvolumedatabaseobserver.h"
+
+#include <glxlog.h>
+_LIT(KGlxCreateTableIds, "CREATE TABLE Ids (MediaId UNSIGNED INTEGER NOT NULL, ThumbId UNSIGNED INTEGER NOT NULL)");
+_LIT(KGlxCreateTableItems, "CREATE TABLE Items (Uri VARCHAR NOT NULL, ThumbId UNSIGNED INTEGER NOT NULL, FileSize INTEGER, ModTime TIME)");
+_LIT(KGlxCreateTableThumbnails, "CREATE TABLE Thumbnails (ThumbId UNSIGNED INTEGER NOT NULL, Width INTEGER NOT NULL, Height INTEGER NOT NULL, Format INTEGER NOT NULL, ImageData LONG VARBINARY NOT NULL)");
+_LIT(KGlxCreateIndexIds, "CREATE UNIQUE INDEX IdIndex ON Ids (MediaId)");
+_LIT(KGlxCreateIndexItems, "CREATE UNIQUE INDEX ItemIndex ON Items (ThumbId ASC)");
+_LIT(KGlxCreateIndexThumbnails, "CREATE UNIQUE INDEX ThumbnailIndex ON Thumbnails (ThumbId, Width, Height)");
+
+_LIT(KGlxTableIds, "Ids");
+_LIT(KGlxTableItems, "Items");
+_LIT(KGlxTableThumbnails, "Thumbnails");
+_LIT(KGlxIndexItems, "ItemIndex");
+
+const TInt KGlxColIdMediaId = 1;
+const TInt KGlxColIdThumbId = 2;
+
+const TInt KGlxColItemUri = 1;
+const TInt KGlxColItemId = 2;
+const TInt KGlxColItemFileSize = 3;
+const TInt KGlxColItemModTime = 4;
+
+const TInt KGlxColThumbnailId = 1;
+const TInt KGlxColThumbnailWidth = 2;
+const TInt KGlxColThumbnailHeight = 3;
+const TInt KGlxColThumbnailFormat = 4;
+const TInt KGlxColThumbnailData = 5;
+
+_LIT(KGlxQueryThumbIdFromIds, "SELECT * FROM Ids WHERE MediaId = ");
+_LIT(KGlxQueryThumbIdFromItems, "SELECT * FROM Items WHERE Uri = ");
+_LIT(KGlxQueryThumbnail, "SELECT * FROM Thumbnails WHERE ThumbId = %d AND Width = %d AND Height = %d");
+_LIT(KGlxQueryAvailable, "SELECT ThumbId, Width, Height FROM Thumbnails WHERE ThumbId = %d AND Width = %d AND Height = %d");
+
+_LIT(KGlxDeleteId, "DELETE FROM Ids WHERE MediaId = %d");
+_LIT(KGlxDeleteThumbnails, "DELETE FROM Thumbnails WHERE ThumbId = %d");
+_LIT(KGlxDeleteItem, "DELETE FROM Items WHERE ThumbId = %d");
+
+const TInt KGlxTIntMaxDigits = 11;
+
+const TUint KGlxFirstThumbnailId = 1;
+
+// -----------------------------------------------------------------------------
+// NewL
+// -----------------------------------------------------------------------------
+//
+CGlxtnVolumeDatabase* CGlxtnVolumeDatabase::NewLC(
+        MGlxtnVolumeDatabaseObserver& aObserver, RFs& aFs, const TDesC& aPath)
+    {
+    TRACER("CGlxtnVolumeDatabase* CGlxtnVolumeDatabase::NewLC( MGlxtnVolumeDatabaseObserver& aObserver, RFs& aFs, const TDesC& aPath)");
+    CGlxtnVolumeDatabase* self = 
+                        new (ELeave) CGlxtnVolumeDatabase(aObserver, aFs);
+    CleanupStack::PushL(self);
+    self->ConstructL(aPath);
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// Constructor
+// -----------------------------------------------------------------------------
+//
+CGlxtnVolumeDatabase::CGlxtnVolumeDatabase(
+                                    MGlxtnVolumeDatabaseObserver& aObserver, 
+                                    RFs& aFs)
+    : CActive(EPriorityStandard), 
+      iObserver(aObserver), iFs(aFs), iState(EStateIdle)
+    {
+    TRACER("CGlxtnVolumeDatabase::CGlxtnVolumeDatabase( MGlxtnVolumeDatabaseObserver& aObserver, RFs& aFs)");
+    }
+
+// -----------------------------------------------------------------------------
+// ConstructL
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::ConstructL(const TDesC& aPath)
+    {
+    TRACER("void CGlxtnVolumeDatabase::ConstructL(const TDesC& aPath)");
+    iDrive = aPath.Left(KMaxDriveName);
+
+    TRAPD(error, OpenDbL(iFs, aPath));
+    if ( KErrNone != error )
+        {
+        iDatabase.Close();
+        delete iStore;
+        iStore = NULL;
+        CreateDbL(iFs, aPath);
+        }
+
+    CActiveScheduler::Add(this);
+    }
+
+// -----------------------------------------------------------------------------
+// Destructor
+// -----------------------------------------------------------------------------
+//
+CGlxtnVolumeDatabase::~CGlxtnVolumeDatabase() 
+    {
+    TRACER("CGlxtnVolumeDatabase::~CGlxtnVolumeDatabase()");
+    Cancel();
+    iView.Close();
+    iTable.Close();
+    iDbUpdater.Close();
+    iDatabase.Close();
+    delete iStore;
+    }
+
+// -----------------------------------------------------------------------------
+// Drive
+// -----------------------------------------------------------------------------
+//
+const TDesC& CGlxtnVolumeDatabase::Drive() const
+    {
+    TRACER("TDesC& CGlxtnVolumeDatabase::Drive()");
+    return iDrive;
+    }
+
+// -----------------------------------------------------------------------------
+// GetThumbnailIdL
+// Look up thumbnail ID from Ids table
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::GetThumbnailIdL( const TGlxMediaId& aMediaId )
+    {
+    TRACER("void CGlxtnVolumeDatabase::GetThumbnailIdL( const TGlxMediaId& aMediaId )");
+    if ( EStateIdle != iState )
+        {
+        User::Leave(KErrNotReady);
+        }
+
+    GLX_LOG_INFO1("CGlxtnVolumeDatabase::GetThumbnailIdL() Media Id= %d",aMediaId.Value());
+    HBufC* sql = HBufC::NewLC(
+                    KGlxQueryThumbIdFromIds().Length() + KGlxTIntMaxDigits );
+    *sql = KGlxQueryThumbIdFromIds;
+    sql->Des().AppendNum( aMediaId.Value() );
+
+    EvaluateQueryL( *sql );
+    iState = EStateGettingIdFromMediaId;
+
+    CleanupStack::PopAndDestroy(sql);
+    }
+
+// -----------------------------------------------------------------------------
+// GetThumbnailIdL
+// Look up thumbnail ID from Items table.  If not found, add new record.
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::GetThumbnailIdL(const CGlxtnFileInfo* aInfo)
+    {
+    TRACER("void CGlxtnVolumeDatabase::GetThumbnailIdL(const CGlxtnFileInfo* aInfo)");
+    if ( EStateIdle != iState )
+        {
+        User::Leave(KErrNotReady);
+        }
+
+    iInfo = aInfo;
+    HBufC* uri = QuoteSqlStringLC(iInfo->FilePath());
+    HBufC* sql = HBufC::NewLC(
+                    KGlxQueryThumbIdFromItems().Length() + uri->Length());
+    *sql = KGlxQueryThumbIdFromItems;
+    sql->Des().Append(*uri);
+
+    EvaluateQueryL( *sql );
+    iState = EStateGettingIdFromFilename;
+
+    CleanupStack::PopAndDestroy(sql);
+    CleanupStack::PopAndDestroy(uri);
+    }
+
+// -----------------------------------------------------------------------------
+// StoreThumbnailIdL
+// Add record to Ids table
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::StoreThumbnailIdL( const TGlxMediaId& aMediaId,
+                                        const TGlxtnThumbnailId& aThumbId )
+    {
+    TRACER("void CGlxtnVolumeDatabase::StoreThumbnailIdL( const TGlxMediaId& aMediaId, const TGlxtnThumbnailId& aThumbId )");
+    if ( EStateIdle != iState )
+        {
+        User::Leave(KErrNotReady);
+        }
+    GLX_LOG_INFO1("StoreThumbnailIdL():- Media Id = %d",aMediaId.Value());
+    GLX_LOG_INFO1("StoreThumbnailIdL():- aThumbId = %d",aThumbId.Value());
+    RDbTable table;
+    CleanupClosePushL(table);
+    User::LeaveIfError( table.Open(
+                        iDatabase, KGlxTableIds, RDbRowSet::EInsertOnly ) );
+
+    table.InsertL();
+    table.SetColL( KGlxColIdMediaId, aMediaId.Value() );
+    table.SetColL( KGlxColIdThumbId, aThumbId.Value() );
+    table.PutL();
+
+    CleanupStack::PopAndDestroy(&table);
+
+    iObserver.HandleThumbnailIdStoredL();
+    }
+
+// -----------------------------------------------------------------------------
+// GetThumbnailL
+// Look up thumbnail from Thumbnails table
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::GetThumbnailL( const TGlxtnThumbnailId& aThumbId,
+                                            const TSize& aSize )
+    {
+    TRACER("void CGlxtnVolumeDatabase::GetThumbnailL( const TGlxtnThumbnailId& aThumbId, const TSize& aSize )");
+    if ( EStateIdle != iState )
+        {
+        User::Leave(KErrNotReady);
+        }
+
+    GLX_LOG_INFO1("GetThumbnailL():- ThumbId Id = %d",aThumbId.Value());
+    GLX_LOG_INFO2("GetThumbnailL Width=%d, Height=%d", aSize.iWidth, aSize.iHeight);
+    HBufC* sql = HBufC::NewLC( KGlxQueryThumbnail().Length()
+                                + 3 * KGlxTIntMaxDigits);
+    sql->Des().Format( KGlxQueryThumbnail,
+                        aThumbId.Value(), aSize.iWidth, aSize.iHeight );
+
+    EvaluateQueryL( *sql );
+    iState = EStateGettingThumbnail;
+
+    CleanupStack::PopAndDestroy(sql);
+    }
+
+// -----------------------------------------------------------------------------
+// CheckAvailableL
+// Check if thumbnail is in Thumbnails table
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::CheckAvailableL( const TGlxtnThumbnailId& aThumbId,
+                                            const TSize& aSize )
+    {
+    TRACER("void CGlxtnVolumeDatabase::CheckAvailableL( const TGlxtnThumbnailId& aThumbId, const TSize& aSize )");
+    if ( EStateIdle != iState )
+        {
+        User::Leave(KErrNotReady);
+        }
+    GLX_LOG_INFO1("CheckAvailableL():- ThumbId = %d", aThumbId.Value());
+
+    HBufC* sql = HBufC::NewLC( KGlxQueryAvailable().Length()
+                                + 3 * KGlxTIntMaxDigits);
+    sql->Des().Format( KGlxQueryAvailable,
+                        aThumbId.Value(), aSize.iWidth, aSize.iHeight );
+
+    EvaluateQueryL( *sql );
+    iState = EStateCheckingAvailability;
+
+    CleanupStack::PopAndDestroy(sql);
+    }
+
+// -----------------------------------------------------------------------------
+// StoreThumbnailL
+// Add record to Thumbnails table
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::StoreThumbnailL( const TGlxtnThumbnailId& aThumbId,
+                                           const TSize& aSize,
+                                           TGlxImageDataFormat aFormat,
+                                           const TDesC8& aData )
+    {
+    TRACER("void CGlxtnVolumeDatabase::StoreThumbnailL( const TGlxtnThumbnailId& aThumbId, const TSize& aSize, TGlxImageDataFormat aFormat, const TDesC8& aData )");
+    if ( EStateIdle != iState )
+        {
+        User::Leave(KErrNotReady);
+        }
+    GLX_LOG_INFO1("StoreThumbnailL():- aThumbId = %d",aThumbId.Value());
+    GLX_LOG_INFO2("StoreThumbnailL Width=%d, Height=%d", aSize.iWidth, aSize.iHeight);
+
+    RDbTable table;
+    CleanupClosePushL(table);
+    User::LeaveIfError( table.Open(
+                iDatabase, KGlxTableThumbnails, RDbRowSet::EInsertOnly ) );
+
+    table.InsertL();
+    table.SetColL( KGlxColThumbnailId, aThumbId.Value() );
+    table.SetColL( KGlxColThumbnailWidth, aSize.iWidth );
+    table.SetColL( KGlxColThumbnailHeight, aSize.iHeight );
+    table.SetColL( KGlxColThumbnailFormat, aFormat );
+    table.SetColL( KGlxColThumbnailData, aData );
+    table.PutL();
+
+    CleanupStack::PopAndDestroy(&table);
+
+    iObserver.HandleThumbnailStored();
+    }
+
+// -----------------------------------------------------------------------------
+// DeleteFromIdsL
+// Delete from IDs table
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::DeleteIdL( const TGlxMediaId& aMediaId )
+    {
+    TRACER("void CGlxtnVolumeDatabase::DeleteIdL( const TGlxMediaId& aMediaId )");
+    if ( EStateIdle != iState )
+        {
+        User::Leave(KErrNotReady);
+        }
+
+    GLX_LOG_INFO1("DeleteIdL():- Media Id = %d",aMediaId.Value());
+    HBufC* sql = HBufC::NewLC( KGlxDeleteId().Length() + KGlxTIntMaxDigits);
+    
+    sql->Des().Format( KGlxDeleteId, aMediaId.Value() );
+    
+    UpdateDataL( *sql );
+    iState = EStateDeletingId;
+    
+    CleanupStack::PopAndDestroy(sql);
+    }
+    
+// -----------------------------------------------------------------------------
+// DeleteThumbnailsL
+// Delete Thumbnails from Thumbnail table
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::DeleteThumbnailsL(
+                                        const TGlxtnThumbnailId& aThumbId )
+    {
+    TRACER("void CGlxtnVolumeDatabase::DeleteThumbnailsL( const TGlxtnThumbnailId& aThumbId )");
+    if ( EStateIdle != iState )
+        {
+        User::Leave(KErrNotReady);
+        }
+    GLX_LOG_INFO1("DeleteThumbnailsL():- aThumbId = %d",aThumbId.Value());
+
+    HBufC* sql = HBufC::NewLC(KGlxDeleteThumbnails().Length() + 
+                                 KGlxTIntMaxDigits);
+    
+    sql->Des().Format( KGlxDeleteThumbnails, aThumbId.Value() );
+    
+    UpdateDataL( *sql );
+    iState = EStateDeletingThumbnails;
+    
+    CleanupStack::PopAndDestroy(sql);
+    }
+    
+// -----------------------------------------------------------------------------
+// DeleteItemL
+// Delete Item from Items table
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::DeleteItemL( const TGlxtnThumbnailId& aThumbId )
+    {
+    TRACER("void CGlxtnVolumeDatabase::DeleteItemL( const TGlxtnThumbnailId& aThumbId )");
+    if ( EStateIdle != iState )
+        {
+        User::Leave(KErrNotReady);
+        }
+
+    GLX_LOG_INFO1("DeleteItemL():- aThumbId = %d",aThumbId.Value());
+    HBufC* sql = HBufC::NewLC( KGlxDeleteItem().Length() + KGlxTIntMaxDigits);
+    
+    sql->Des().Format( KGlxDeleteItem, aThumbId.Value() );
+    
+    UpdateDataL( *sql );
+    iState = EStateDeletingItem;
+    
+    CleanupStack::PopAndDestroy(sql);
+    }
+
+// -----------------------------------------------------------------------------
+// CleanupDatabaseL
+// Clean from database entries that do not have a corresponding media file
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::CleanupDatabaseL()
+    {
+    TRACER("void CGlxtnVolumeDatabase::CleanupDatabaseL()");
+    if ( EStateIdle != iState )
+        {
+        User::Leave(KErrNotReady);
+        }
+    User::LeaveIfError( iTable.Open(
+                        iDatabase, KGlxTableItems, RDbRowSet::EUpdatable ) );
+    iTable.BeginningL();
+
+    SetActive();
+    TRequestStatus* ts = &iStatus;
+    User::RequestComplete(ts, KErrNone);
+
+    iState = EStateCleaning;
+    }
+
+    
+// -----------------------------------------------------------------------------
+// CleanupRowL
+// Test entry in one row in Items table to see if file is available. If it is
+// not, delete this row and start process of deleting corresponding entries 
+// from Ids table and Thumbnails table
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::CleanupRowL()
+    {
+    TRACER("void CGlxtnVolumeDatabase::CleanupRowL()");
+    // Get filename
+    iTable.GetL();
+    TPtrC filename = iTable.ColDes(KGlxColItemUri);
+   
+    // does file exist
+    TTime time;
+    TInt err = iFs.Modified(filename, time);
+    if(err == KErrNone)
+        {
+        //yes, file exists, so complete to go to next row
+        SetActive();
+        TRequestStatus* ts = &iStatus;
+        User::RequestComplete(ts, KErrNone);
+        iState = EStateCleaning;
+        }
+    else
+        {
+        // Problem accessing file, so delete database entries
+        // First delete row in Items table
+        TUint thumbId = iTable.ColUint( KGlxColItemId );
+        //Delete the row (entry in Items)
+        iTable.DeleteL();
+
+        // Now delete from Thumbnails
+        HBufC* sql = HBufC::NewLC(KGlxDeleteThumbnails().Length() + 
+                                     KGlxTIntMaxDigits);
+
+        sql->Des().Format( KGlxDeleteThumbnails, thumbId );
+        UpdateDataL( *sql );
+        iState = EStateCleaningDeletingThumbnails;
+        CleanupStack::PopAndDestroy(sql);
+        }
+    }
+    
+// -----------------------------------------------------------------------------
+// DoCancel
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::DoCancel()
+    {
+    TRACER("void CGlxtnVolumeDatabase::DoCancel()");
+    iView.Cancel();
+    iTable.Close();
+    iDbUpdater.Close();
+    iState = EStateIdle;
+    }
+
+// -----------------------------------------------------------------------------
+// RunL
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::RunL()
+    {
+    TRACER("void CGlxtnVolumeDatabase::RunL()");
+    User::LeaveIfError(iStatus.Int());
+
+    // Continue any database operations which aren't finished
+    switch ( iState )
+        {
+        case EStateGettingIdFromMediaId:
+        case EStateGettingIdFromFilename:
+        case EStateGettingThumbnail:
+        case EStateCheckingAvailability:
+            if ( iView.Unevaluated() )
+                {
+                iView.Evaluate(iStatus);
+                SetActive();
+                return;
+                }
+            break;
+        case EStateDeletingId:
+        case EStateDeletingThumbnails:
+        case EStateDeletingItem:
+        case EStateCleaningDeletingThumbnails:
+            if(iStatus.Int() != 0)
+                {
+                iDbUpdater.Next(iStatus);
+                SetActive();
+                return;
+                }
+            break;
+        case EStateCleaning:
+        // do nothing
+            break;
+        default:
+            Panic(EGlxPanicIllegalState);
+            break;
+        }
+
+    // Handle results of database operation
+    switch ( iState )
+        {
+        case EStateGettingIdFromMediaId:
+            {
+            TGlxtnThumbnailId thumbId;
+            if ( iView.FirstL() )
+                {
+                iView.GetL();
+                thumbId = iView.ColUint(KGlxColIdThumbId);
+                GLX_LOG_INFO1("RunL - EStateGettingIdFromMediaId  IF (iView.FirstL()): ThumbId = %d", thumbId.Value());
+                }
+            iView.Close();
+            iState = EStateIdle;
+            GLX_LOG_INFO1("RunL - EStateGettingIdFromMediaId - aThumbId = %d", thumbId.Value());
+            iObserver.HandleThumbnailIdFromMediaIdL(thumbId);
+            }
+            break;
+
+        case EStateGettingIdFromFilename:
+            {
+            TGlxtnThumbnailId thumbId;
+            if ( iView.FirstL() )
+                {
+                iView.GetL();
+                thumbId = iView.ColUint(KGlxColItemId);
+                iView.Close();
+                }
+            else
+                {
+                iView.Close();
+                thumbId = DoAddItemL();
+                }
+            iState = EStateIdle;
+            iInfo = NULL;
+            GLX_LOG_INFO1("RunL - EStateGettingIdFromFilename - aThumbId = %d", thumbId.Value());
+            iObserver.HandleThumbnailIdFromFilenameL(thumbId);
+            }
+            break;
+
+        case EStateGettingThumbnail:
+            if ( iView.FirstL() )
+                {
+                iView.GetL();
+                TGlxImageDataFormat format = static_cast<TGlxImageDataFormat>(
+                                        iView.ColInt(KGlxColThumbnailFormat));
+                TInt size = iView.ColSize(KGlxColThumbnailData);
+
+                RDbColReadStream stream;
+                stream.OpenLC(iView, KGlxColThumbnailData);
+                HBufC8* data = HBufC8::NewLC(size);
+                TPtr8 ptr(data->Des());
+                // Need to specify amount to read, as the HBufC8 can be bigger
+                // than requested
+                stream.ReadL(ptr, size);    
+
+                CleanupStack::Pop(data);
+                CleanupStack::PopAndDestroy(&stream);
+
+                iView.Close();
+                iState = EStateIdle;
+                iObserver.HandleThumbnail(format, data);
+                }
+            else
+                {
+                iView.Close();
+                iState = EStateIdle;
+                GLX_LOG_INFO("RunL: HandleDatabaseError - KErrNotFound");
+                iObserver.HandleDatabaseError(KErrNotFound);
+                }
+            break;
+            
+        case EStateDeletingId:
+            iState = EStateIdle;
+            iDbUpdater.Close();
+            iObserver.HandleMediaIdDeletedL();
+            break;
+            
+        case EStateDeletingThumbnails:
+            iState = EStateIdle;
+            iDbUpdater.Close();
+            iObserver.HandleThumbnailsDeletedL();
+            break;
+            
+        case EStateDeletingItem:
+            iState = EStateIdle;
+            iDbUpdater.Close();
+            iObserver.HandleItemDeletedL();
+            break;
+
+        case EStateCheckingAvailability:
+            {
+            TInt result = KGlxThumbnailAvailable;
+            if ( iView.IsEmptyL() )
+                {
+                result = KGlxThumbnailNotAvailable;
+                }
+            iView.Close();
+            iState = EStateIdle;
+            GLX_LOG_INFO1("RunL - EStateCheckingAvailability - result = %d", result);
+            iObserver.HandleAvailabilityChecked(result);
+            }
+            break;
+            
+        case EStateCleaningDeletingThumbnails:
+            iState = EStateCleaning;
+            iDbUpdater.Close();      // deliberate fall through to next row
+        case EStateCleaning:
+            if(iTable.NextL())
+                {
+                // next row
+                CleanupRowL();
+                }
+            else
+                {
+                // no more rows
+                iTable.Close();
+                iState = EStateIdle;
+                iObserver.HandleDatabaseCleanedL();
+                }
+            break;
+        default:
+            Panic(EGlxPanicIllegalState);
+            break;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// RunError
+// -----------------------------------------------------------------------------
+//
+TInt CGlxtnVolumeDatabase::RunError(TInt aError)
+    {
+    TRACER("TInt CGlxtnVolumeDatabase::RunError(TInt aError)");
+    iTable.Close();
+    iView.Close();
+    iDbUpdater.Close();
+    iState = EStateIdle;
+    iInfo = NULL;
+    GLX_LOG_INFO1("RunL: HandleDatabaseError - error=%d", aError);
+    iObserver.HandleDatabaseError(aError);
+
+    return KErrNone;
+    }
+
+// -----------------------------------------------------------------------------
+// OpenDbL
+// Open an existing database.
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::OpenDbL(RFs& aFs, const TDesC& aFilename)
+    {
+    GLX_LOG_ENTRY_EXIT("void CGlxtnVolumeDatabase::OpenDbL(RFs& aFs, const TDesC& aFilename)");
+    iStore = CFileStore::OpenL(aFs, aFilename, EFileRead | EFileWrite);
+    iDatabase.OpenL(iStore, iStore->Root());
+
+    // Get next available thumbnail ID (synchronous)
+    RDbTable table;
+    CleanupClosePushL(table);
+    User::LeaveIfError( table.Open(
+                        iDatabase, KGlxTableItems, RDbRowSet::EReadOnly ) );
+    User::LeaveIfError(table.SetIndex(KGlxIndexItems));
+
+    // Find highest thumbnail ID in use.  New entries are added to the Items
+    // table first, and deleted from the Items table last, so all IDs in use
+    // will always be found there.
+    // Thumbnail IDs are only unique within a volume.
+    if ( table.LastL() )
+        {
+        table.GetL();
+        iNextThumbId = table.ColUint(KGlxColItemId) + 1;
+        }
+    else
+        {
+        // Database is empty
+        iNextThumbId = KGlxFirstThumbnailId;
+        }
+
+    CleanupStack::PopAndDestroy(&table);
+    }
+    
+// -----------------------------------------------------------------------------
+// CreateDbL
+// Create a new database.
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::CreateDbL(RFs& aFs, const TDesC& aFilename)
+    {
+    TRACER("void CGlxtnVolumeDatabase::CreateDbL(RFs& aFs, const TDesC& aFilename)");
+    // Create database, overwriting any existing file
+    TInt err = aFs.MkDirAll(aFilename);
+    if ( err != KErrAlreadyExists )
+        {
+        User::LeaveIfError(err);
+        }
+    iStore = CPermanentFileStore::ReplaceL(aFs, aFilename,
+                                            EFileRead | EFileWrite);
+    iStore->SetTypeL(KPermanentFileStoreLayoutUid);
+    iStore->SetRootL(iDatabase.CreateL(iStore));
+    iStore->CommitL();
+
+    User::LeaveIfError(iDatabase.Execute(KGlxCreateTableIds));
+    User::LeaveIfError(iDatabase.Execute(KGlxCreateTableItems));
+    User::LeaveIfError(iDatabase.Execute(KGlxCreateTableThumbnails));
+    User::LeaveIfError(iDatabase.Execute(KGlxCreateIndexIds));
+    User::LeaveIfError(iDatabase.Execute(KGlxCreateIndexItems));
+    User::LeaveIfError(iDatabase.Execute(KGlxCreateIndexThumbnails));
+    
+    iNextThumbId = KGlxFirstThumbnailId;
+    }
+
+// -----------------------------------------------------------------------------
+// DoAddItemL
+// -----------------------------------------------------------------------------
+//
+TGlxtnThumbnailId CGlxtnVolumeDatabase::DoAddItemL()
+    {
+    TRACER("TGlxtnThumbnailId CGlxtnVolumeDatabase::DoAddItemL()");
+    __ASSERT_ALWAYS(iInfo, Panic(EGlxPanicNullPointer));
+    RDbTable table;
+    CleanupClosePushL(table);
+    User::LeaveIfError(table.Open(iDatabase, KGlxTableItems, RDbRowSet::EInsertOnly));
+    TGlxtnThumbnailId thumbId( iNextThumbId );
+
+    table.InsertL();
+    table.SetColL( KGlxColItemUri, iInfo->FilePath() );
+    table.SetColL( KGlxColItemId, thumbId.Value() );
+    table.SetColL( KGlxColItemFileSize, iInfo->iFileSize );
+    table.SetColL( KGlxColItemModTime, iInfo->iFileTime );
+    table.PutL();
+
+    iNextThumbId++;
+    CleanupStack::PopAndDestroy(&table);
+
+    return thumbId;
+    }
+
+// -----------------------------------------------------------------------------
+// EvaluateQueryL
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::EvaluateQueryL( const TDbQuery &aQuery )
+    {
+    TRACER("void CGlxtnVolumeDatabase::EvaluateQueryL( const TDbQuery &aQuery )");
+    User::LeaveIfError( iView.Prepare(
+                                iDatabase, aQuery, RDbRowSet::EReadOnly ) );
+    iView.Evaluate( iStatus );
+    SetActive();
+    }
+
+// -----------------------------------------------------------------------------
+// UpdateDataL
+// -----------------------------------------------------------------------------
+//
+void CGlxtnVolumeDatabase::UpdateDataL( const TDesC& aSql )
+    {
+    TRACER("void CGlxtnVolumeDatabase::UpdateDataL( const TDesC& aSql )");
+    TInt result = iDbUpdater.Execute( iDatabase, aSql );
+
+    if ( result < KErrNone )
+        {
+        iDbUpdater.Close();
+        User::Leave( result );
+        }
+
+    // According to documentation a result of 0 should indicate complete
+    // but this does not seem to be the case
+    iDbUpdater.Next( iStatus );
+    SetActive();
+    }
+
+// -----------------------------------------------------------------------------
+// QuoteSqlStringLC
+// -----------------------------------------------------------------------------
+//
+HBufC* CGlxtnVolumeDatabase::QuoteSqlStringLC(const TDesC& aText)
+    {
+    TRACER("HBufC* CGlxtnVolumeDatabase::QuoteSqlStringLC(const TDesC& aText)");
+    const TText quote('\'');
+    TInt length = aText.Length() + 2;
+
+    for ( TInt i = 0; i < aText.Length(); i++ )
+        {
+        if ( quote == aText[i] )
+            {
+            length++;
+            }
+        }
+
+    HBufC* text = HBufC::NewLC(length);
+    TPtr ptr(text->Des());
+
+    ptr.Append(quote);
+    for ( TInt i = 0; i < aText.Length(); i++ )
+        {
+        TText chr = aText[i];
+        ptr.Append(chr);
+        if ( quote == chr )
+            {
+            ptr.Append(quote);
+            }
+        }
+    ptr.Append(quote);
+
+    return text;
+    }