EngSrc/IEImageList.cpp
author jkauppin
Fri, 15 Oct 2010 10:18:29 +0900
changeset 3 93fff7023be8
permissions -rw-r--r--
Initial version

/*
* Copyright (c) 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: Juha Kauppinen, Mika Hokkanen
* 
* Description: Photo Browser
*
*/

// INCLUDE FILES

#include <eikenv.h>
#include <exifread.h>
#include <f32file.h>
#include <s32file.h>
#include <bautils.h>
#include "IEImageList.h"
#include "IEImageData.h"
#include "IEEngineImp.h"
#include "IEEngineUtils.h"
#include "ImageMonitorAO.h"
#include "IEFileLoader.h"
#ifdef _S60_5x_ACCELEROMETER_
#include "IESensorMonitor.h"
#endif

#define LATETHUMBCHECK
//#define GROUP_FOLDERS_BY_NAME
#define CHECK_IF_IMAGE_IS_VISIBLE

_LIT(KDatabaseFileName, "photobrowser.db");
_LIT8(KDatabaseId, "IMGC0008");
const TInt KNumOfDrives = 3;

EXPORT_C CIEImageList* CIEImageList::NewL(        
        RArray<CImageData*>& aImageData, 
        CIEFileLoader* aCallback)
    {
    CIEImageList* self = new (ELeave) CIEImageList(aImageData, aCallback);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

CIEImageList::CIEImageList(
        RArray<CImageData*>& aImageData, 
        CIEFileLoader* aCallback) :
    iCallback(aCallback),
    iImageDataList(aImageData),
    iGridMode(EGridModeTime)	
    {
    for (TInt i = 0;i < KNumOfDrives;i++)
        iDatabaseChanged[i] = EFalse; 
    }

void CIEImageList::ConstructL() 
    {
    User::LeaveIfError(iCritical.CreateLocal());
    }
        
EXPORT_C CIEImageList::~CIEImageList()
    {
    iCritical.Close();
    }
        
EXPORT_C void CIEImageList::SetGridMode(TGridMode aGridMode)
    {
    if (iGridMode != aGridMode) 
        {
        iGridMode = aGridMode;
        iCritical.Wait();
        Rearrange(0);
        iCritical.Signal();
        }
    }

EXPORT_C TGridMode CIEImageList::GetGridMode() const
    {
    return iGridMode; 
    }

EXPORT_C void CIEImageList::SetChanged(TDesC& aPath)
    {
    TImageListDrive drive = EImageListDriveC;
    TRAPD(err, drive = GetPathDriveL(aPath));
    if (err == KErrNone)
        {
        iDatabaseChanged[drive] = ETrue;
        }
    }

void CIEImageList::SetChanged(CImageData* aImageData)
    {
    TFileName fileName;
    aImageData->GetFileName(fileName, EFullSize);
    SetChanged(fileName);
    }

CImageData* CIEImageList::CreateImageDataL(
        const TFileName& aFileName, 
        const TTime& aTime,
        const TReal orientation)
    {
    DP0_IMAGIC(_L("CIEImageList::CreateImageDataL++"));
    
    // Create new image data instance
    CImageData* imageData = CImageData::NewL(
#ifdef LATETHUMBCHECK            
            /*EFullSize|*/ESize512x512|ESize128x128|ESize32x32
#endif            
            );
    
    imageData->SetCreatedTime(aTime);
    imageData->SetFileNameL(aFileName);
    imageData->SetOrientation(orientation);    
    
#ifdef _S60_5x_ACCELEROMETER_
    // Portrait
    if(iCallback->DeviceOrientation() == TSensrvOrientationData::EOrientationDisplayUp)
        {
        imageData->iGridData.iTargetRotationAngle = 90 + orientation;
        }
    // Landscape
    else//(iCallback->DeviceOrientation() == TSensrvOrientationData::EOrientationDisplayRightUp)
        {
        imageData->iGridData.iTargetRotationAngle = orientation;
        }
    imageData->iGridData.iRotationAngle = orientation;
#else
    imageData->iGridData.iRotationAngle = orientation;
    imageData->iGridData.iTargetRotationAngle = orientation;
#endif
    
    DP1_IMAGIC(_L("CIEImageList::AddImageL - filename: %S"), &aFileName);

#ifndef LATETHUMBCHECK    
    //Check and mark to imageData which thumbnails exists
    CheckCreatedThumbnails(*imageData);
#endif
    
    DP0_IMAGIC(_L("CIEImageList::CreateImageDataL--"));
    return imageData;
    }
    
TBool CIEImageList::IsImageBefore(CImageData* aNewImageData, TInt aIndex) const
    {
    if (aIndex >= iImageDataList.Count())
        return ETrue;
    
    // Use folder grouping
    if (iGridMode != EGridModeTime)      
        {
        TFileName newPath, path;
        aNewImageData->GetPath(newPath);
        iImageDataList[aIndex]->GetPath(path);

#ifdef GROUP_FOLDERS_BY_NAME
        // Folders are sorted by name
        if (newPath > path)      // TODO: should trim drive + base path (e.g. C:\data\)
            return ETrue;
        if (newPath < path)
            return EFalse;
#else

        if (iGridMode == EGridModePeople)
            {
            return (iImageDataList[aIndex]->iPersonId > 
                    aNewImageData->iPersonId);
            }
        else if (iGridMode == EGridModeFolder && aIndex > 0)
            {
            // Current image path is not same
            if (path != newPath)
                {
                TFileName prevPath;
                iImageDataList[aIndex - 1]->GetPath(prevPath);
                  
                // Previous image path is same, add after that
                if (newPath == prevPath)
                     return ETrue;
                
                // Compare only against the first image in the folder
                if (path == prevPath)
                     return EFalse;
                }
            }
#endif
        }
    
    // Compare times
    return (aNewImageData->GetCreatedTime() > iImageDataList[aIndex]->GetCreatedTime());
    }

TInt CIEImageList::GetNewImageIndex(CImageData* aImageData) const
    {
    TInt index = 0; 
    while(index < iImageDataList.Count())
        {
        if(IsImageBefore(aImageData, index))
            {
            break;
            }
        index++;
        }
    return index;
    }

void CIEImageList::Rearrange(TInt aStartIndex)
    {
    for (TInt i = aStartIndex;i < iImageDataList.Count();i++)
        {
        CImageData* imageData = iImageDataList[i];            
        iImageDataList.Remove(i);
        TInt newIndex = GetNewImageIndex(imageData);
        iImageDataList.Insert(imageData, newIndex);
        }
    }

EXPORT_C void CIEImageList::AddImage(CImageData* aImageData)
    {
    DP0_IMAGIC(_L("CIEImageList::AddImageL++"));
    
    iCritical.Wait();  
    
    // Insert image to list
    TInt index = GetNewImageIndex(aImageData);
    iImageDataList.Insert(aImageData, index);
        
    // Need to resort all items if use time based folder sort
#ifndef GROUP_FOLDERS_BY_NAME
    if (iGridMode != EGridModeTime)      
        {
        Rearrange(index + 1);
        }
#endif        

    // Image is not added as last image
    if (index < iImageDataList.Count() - 1)
        {
        // Mark database as changed
        SetChanged(aImageData);
        
        // Inform UI
        //iCallback->ImageListChanged(index, ETrue);
        }
    
    iCallback->ImageListChanged(index, ETrue);
    
    iCritical.Signal();
    
    DP0_IMAGIC(_L("CIEImageList::AddImageL-- tmpImageData"));
    }

#ifdef IMAGIC_DATABASE

void CIEImageList::GetDatabaseFileName(TFileName& aFileName, TImageListDrive aDrive)
    {
    switch (aDrive)
        {
        case EImageListDriveC:
            aFileName.Copy(PathInfo::PhoneMemoryRootPath());
            break;
            
        case EImageListDriveE:
            aFileName.Copy(PathInfo::MemoryCardRootPath());
            break;
            
        case EImageListDriveF:           
            aFileName.Copy(KRootPathFDrive);
            break;
           
        default:
            return;
        }
    
    aFileName.Append(KDatabaseFileName);
    }

EXPORT_C void CIEImageList::ReadDatabaseL()
    {  
    DP0_IMAGIC(_L("CIEImageList::ReadDatabaseL++"));

    RFileReadStream readStreams[KNumOfDrives];
    TBool openStreams[KNumOfDrives];
    CImageData* imageDatas[KNumOfDrives];
    RFs fs;
    
    User::LeaveIfError(fs.Connect());
    CleanupClosePushL(fs);
    
    // Open databases
    for (TInt i = 0;i < KNumOfDrives;i++)
        {
        openStreams[i] = EFalse;
        imageDatas[i] = NULL;        
        
        TFileName databaseFileName;
        GetDatabaseFileName(databaseFileName, TImageListDrive(i));
        if (readStreams[i].Open(fs, databaseFileName, EFileShareAny) == KErrNone)
            {
            // Check file validity and version
            TUint8 buf[8];
            TPtr8 ptr(buf, sizeof(buf));
            readStreams[i].ReadL(ptr, KDatabaseId.iTypeLength);
            if (ptr.Compare(KDatabaseId) != 0)
                readStreams[i].Close();
            else
                openStreams[i] = ETrue;
            }
        }
    
    // Read databases
    while(iCallback->ImageFinderState() == CIEFileLoader::EImageFinderRunning)
        {
        // Read image datas from each database
        TBool endOfData = ETrue;
        for(TInt i = 0;i < KNumOfDrives;i++)
            {
            // Database is open and no image data is left
            if (imageDatas[i] == NULL && openStreams[i])
                {
                TRAPD(err, imageDatas[i] = ReadImageDataL(readStreams[i], fs));
                if (err != KErrNone || imageDatas[i] == NULL) 
                    {
                    openStreams[i] = EFalse;
                    readStreams[i].Close();
                    }
                }
            
            if (imageDatas[i])
                endOfData = EFalse;
            }
        
        if (endOfData)
            break;
        
        // Pick the most leftmost image 
        TInt index = -1;
        for (TInt i = 0;i < KNumOfDrives;i++)
            {
            if (imageDatas[i] && 
                    (index < 0 || 
                     IsImageBefore(imageDatas[i], index)))
                index = i;
            }
            
        // Add image to list
        if (index >= 0) 
            {
            AddImage(imageDatas[index]);
            imageDatas[index] = NULL;
            }
        }
    
    CleanupStack::Pop();
    fs.Close();

    DP0_IMAGIC(_L("CIEImageList::ReadDatabaseL--"));
    }

CImageData* CIEImageList::ReadImageDataL(RFileReadStream& readStream, RFs& aFs)
    {
    TUint8 buf[KMaxFileName * 2];
    TPtr8 ptr(buf, sizeof(buf));
    TFileName fileName;
    TTime fileTime, createdTime;
    TSize size;
    TInt faces;
    TUint16 orientation;
    CImageData* imageData = NULL;
           
    // Read until get valid image data
    while (imageData == NULL) {
    
        // Read file name (1 byte length, unicode name)
        TInt len = readStream.ReadUint8L();
    
        // End of list
        if (len == 0)
            return NULL;

        readStream.ReadL(ptr, len * 2);
        TPtrC16 ptr16((const TUint16*)buf, len);
        fileName.Copy(ptr16);
            
        // Read file time
        readStream.ReadL(ptr, sizeof(TTime));
        fileTime = *(TTime*)ptr.Ptr();

        // Read created time
        readStream.ReadL(ptr, sizeof(TTime));
        createdTime = *(TTime*)ptr.Ptr();                    

        // Read orientation (in 90 degrees angles)
        orientation = readStream.ReadUint8L() * 90L;
            
        // Read resolution
        size.iWidth = readStream.ReadUint32L();
        size.iHeight = readStream.ReadUint32L();

        // Read number of faces
        faces = readStream.ReadInt8L();
        
        TInt personId = readStream.ReadInt32L();

        // Check that no multiple entries
        if ((imageData = GetImageData(fileName)) != NULL) 
            {
            imageData = NULL;
            continue;
            }
        
        // Check if image exist and not be hidden
        TInt error = KErrNone;
        TBool visible = ETrue;
#ifdef CHECK_IF_IMAGE_IS_VISIBLE        

        TRAP(error, visible = IsImageViewableL(fileName, aFs));
#endif        
        if (error == KErrNone && visible)
            {
            // Create image data object
            imageData = CreateImageDataL(
                    fileName, 
                    createdTime, 
                    orientation);

            if (imageData) 
                {
                imageData->SetFileTime(fileTime);
                imageData->SetSize(size);
                imageData->SetNumberOfFaces(faces);
                imageData->iPersonId = personId;
                //imageData->SetImageReady(EFullSize, ETrue);
                }        
            }
        else
            {
            // Delete thumbnails if file could not be read
            if (error != KErrNone)
                CIEEngineUtils::DeleteThumbnails(fileName, aFs);
            SetChanged(fileName);
            }
        }
    
    return imageData;
    }
    
EXPORT_C void CIEImageList::WriteDatabaseL() 
    {
    DP0_IMAGIC(_L("CIEImageList::WriteDatabaseL++"));

    RFs fs;
    User::LeaveIfError(fs.Connect());
    CleanupClosePushL(fs);
    
    for (TInt i = 0;i < KNumOfDrives;i++) 
        {
        if (iDatabaseChanged[i])
            {
            TRAP_IGNORE(WriteDatabaseL(TImageListDrive(i), fs)); 
            iDatabaseChanged[i] = EFalse;
            }
        }
    
    CleanupStack::Pop(); // fs
    fs.Close();    
    
    DP0_IMAGIC(_L("CIEImageList::WriteDatabaseL--"));    
    }
        
void CIEImageList::WriteDatabaseL(TImageListDrive aDrive, RFs& aFs)
    {
    TUint8 buf[sizeof(TUint32)];
    TPtr8 ptr(buf, sizeof(buf));
    RFile f;

    TFileName path, fileName;
    GetDatabaseFileName(fileName, aDrive);
    
    TParse parser;
    parser.Set(fileName, NULL, NULL);
    path = parser.DriveAndPath();
    TRAP_IGNORE(BaflUtils::EnsurePathExistsL(aFs, path));    
    
    if (f.Replace(
            aFs, 
            fileName, 
            EFileWrite) != KErrNone) 
        return;
        
    CleanupClosePushL(f);
    
    f.SetAtt(KEntryAttHidden, 0);
    
    RFileWriteStream writeStream(f); 
    CleanupClosePushL(writeStream); 
    
    writeStream.WriteL(KDatabaseId);

    for (TInt32 i = 0;i < iImageDataList.Count();i++)
        {
        TFileName fileName;
        TImageListDrive drive = EImageListDriveC;
        iImageDataList[i]->GetFileName(fileName, EFullSize);
        
        // Write only files that belong to this drive
        TRAPD(err, drive = GetPathDriveL(fileName));
        if (err != KErrNone || drive != aDrive)
            continue;

        // Write file name
        writeStream.WriteUint8L(fileName.Length());
        writeStream.WriteL(fileName, fileName.Length());
          
        // Write file time
        TTime fileTime = iImageDataList[i]->GetFileTime();
        TPtrC8 fileTimeptr((const TUint8 *)&fileTime, sizeof(TTime));
        writeStream.WriteL(fileTimeptr);

        // Write created time
        TTime createdTime = iImageDataList[i]->GetCreatedTime();
        TPtrC8 createdTimeptr((const TUint8 *)&createdTime, sizeof(TTime));
        writeStream.WriteL(createdTimeptr);

        // Write orientation (in 90 degrees)
        writeStream.WriteUint8L(iImageDataList[i]->GetOrientation() / 90);
                
        // Write size
        writeStream.WriteUint32L(iImageDataList[i]->GetSize().iWidth);
        writeStream.WriteUint32L(iImageDataList[i]->GetSize().iHeight);

        // Write number of faces
        writeStream.WriteInt8L(iImageDataList[i]->GetNumberOfFaces());
        writeStream.WriteInt32L(iImageDataList[i]->iPersonId);
        }

    // End of stream notification
    writeStream.WriteUint8L(0);
    
    writeStream.Close();
    
    CleanupStack::PopAndDestroy(); // write stream
    CleanupStack::PopAndDestroy(); // f
    }
#endif

EXPORT_C TBool CIEImageList::IsImageViewableL(TDesC& aFileName, RFs& aFs) const 
    {
    TUint att;
    //if(!IsFileExist(fileName))
    TInt error = aFs.Att(aFileName, att);
    if (error != KErrNone)
        User::Leave(error);
    
    return ((att & KEntryAttHidden) == KEntryAttHidden) ? EFalse : ETrue;  
    }

EXPORT_C TInt CIEImageList::GetImageIndex(CImageData* aImageData)
    {
    for (TInt i = 0;i < iImageDataList.Count();i++) 
        {
        if (aImageData == iImageDataList[i])
            return i;
        }
    return -1;
    }

EXPORT_C CImageData* CIEImageList::GetImageData(const TFileName& aFileName)
    {
    CImageData* imageData = NULL;
    iCritical.Wait();
    
    for (TInt i = 0;i < iImageDataList.Count();i++) 
        {
        TFileName fileName;
        iImageDataList[i]->GetFileName(fileName, EFullSize);
        if (fileName.Compare(aFileName) == 0)
            {
            imageData = iImageDataList[i];
            break;
            }
        }
    
    iCritical.Signal();
        
    return imageData;
    }

EXPORT_C void CIEImageList::RemoveNonExistImagesL(TDesC* aPath, RFs& aFs)
    {
    DP0_IMAGIC(_L("CIEImageList::RemoveNonExistImagesL++"));
    
    TInt i = 0;
    while (i < iImageDataList.Count()) 
        {
        iCritical.Wait();
        
        // File may not exist
        TBool bRemove = !iImageDataList[i]->IsImageReady(EFullSize); 
        
        // Start of path must be same
        if (bRemove && aPath) {
            TFileName path;
            iImageDataList[i]->GetPath(path);
            bRemove = (aPath->Compare(path) == 0);
        }

        iCritical.Signal();
        
        // Remove from list
        if (bRemove)
            {
            Remove(i, aFs);
            if (aPath)
                SetChanged(*aPath);
            }
        else
            {
            i++;
            }
        }
    
    DP0_IMAGIC(_L("CIEImageList::RemoveNonExistImagesL--"));
    }

CIEImageList::TImageListDrive CIEImageList::GetPathDriveL(TDesC& aPath)
    {
    TParse parser;
    parser.Set(aPath, NULL, NULL);
    TPtrC drive = parser.Drive();
    const TPtrC drives[] = { _L("C:"), _L("E:"), _L("F:") };
    
    for (TInt i = 0;i < sizeof(drives) / sizeof(TPtrC);i++)
        {
        if (drive.Compare(drives[i]) == 0)
            {
            return TImageListDrive(i);
            }
        }
    
    User::Leave(KErrArgument);
    return EImageListDriveC;
    }

EXPORT_C void CIEImageList::Remove(TInt aIndex, RFs& aFs)
    {
    TFileName fileName;
    
    if (aIndex < 0 || aIndex >= iImageDataList.Count())
        return;
    
    // Delete thumbnails if original file doesn't exist anymore
    iImageDataList[aIndex]->GetFileName(fileName, EFullSize);
    if(!BaflUtils::FileExists(aFs, fileName))
        CIEEngineUtils::DeleteThumbnails(fileName, aFs);
    
    iCritical.Wait();
    
    // Remove from the list
    CImageData* pRemovedImageData = iImageDataList[aIndex];
    iImageDataList.Remove(aIndex);
    delete pRemovedImageData;
    
    iCritical.Signal();
    
    SetChanged(fileName);
    
    iCallback->ImageListChanged(aIndex, EFalse);
    }