/*
* Copyright (c) 2002 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:
* GMS picture viewer control which is compatible with Editor Base.
* It is based very much on the CMmsImageControl.
* %version: bh1cfmsg#16 %
*
*/
// INCLUDE FILES
#include "CGmsPictureControl.h"
#include <coeutils.h> // for ConeUtils::FileExists
#include <AknUtils.h>
#include <imageconversion.h>
#include <aknlayoutscalable_apps.cdl.h>
#include <MIHLScaler.h>
// CONSTANTS
// Mime type of the image, required by the decoder.
_LIT8( KGmsOtaMimeType, "image/x-ota-bitmap");
_LIT(KSmEditorGmsPic,"smeditor gmsPic");
// ==========================================================
/**
* Helper class which allows CGmsPictureControl to receive a callback
* to it's callback method when an asynchronous operation has finished.
* CAsyncCallBack needed tweaking since it is ment for callback only, and
* it completes the task immediately.
*/
class CGmsAOCallBack: public CAsyncCallBack
{
public:
CGmsAOCallBack( const TCallBack &aCallBack, TInt aPriority )
: CAsyncCallBack( aCallBack, aPriority )
{
}
void SetItActive()
{
SetActive();
}
protected:
void RunL()
{
iCallBack.CallBack();
}
};
// MEMBER FUNCTIONS
EXPORT_C CGmsPictureControl* CGmsPictureControl::NewL(
const CCoeControl* aParent)
{
CGmsPictureControl* self = new(ELeave) CGmsPictureControl();
CleanupStack::PushL(self);
self->ConstructL(aParent);
CleanupStack::Pop(self);
return self;
}
EXPORT_C CGmsPictureControl::~CGmsPictureControl()
{
delete iBitmap;
iBitmap = NULL;
delete iScaledBitmap;
iScaledBitmap = NULL;
delete iConverter;
iConverter = NULL;
delete iScaler;
delete iConversionAO;
delete iScalingAO;
iFs.Close();
}
EXPORT_C void CGmsPictureControl::LoadImageL(
const TFileName& aImageFile,
TUint aMaxHeight /* = 0 */)
{
__ASSERT_DEBUG((aImageFile.Length() > 0), Panic( ENoImageFile ) );
if (!ConeUtils::FileExists(aImageFile))
{
User::Leave(KErrNotFound);
}
if (aMaxHeight)
{
iMaxHeight = aMaxHeight;
}
Reset();
iConverter = CImageDecoder::FileNewL( iFs, aImageFile, KGmsOtaMimeType );
__ASSERT_ALWAYS( iConverter->FrameCount() == 1, Panic(EBadImageContent) );
TFrameInfo frameInfo = iConverter->FrameInfo( 0 );
iBitmap = new( ELeave ) CFbsBitmap;
User::LeaveIfError( iBitmap->Create( frameInfo.iOverallSizeInPixels,
EGray2 ) );
iConverter->Convert( &(iConversionAO->iStatus), *iBitmap );
iConversionAO->SetItActive();
}
EXPORT_C TSize CGmsPictureControl::OriginalImageResolution() const
{
TSize size(0,0);
if ( iConverter )
{
size = iConverter->FrameInfo( 0 ).iOverallSizeInPixels;
}
return size;
}
TRect CGmsPictureControl::CurrentLineRect()
{
return Rect();
}
void CGmsPictureControl::SetAndGetSizeL(TSize& aSize)
{
// Size proposed in aSize parameter is used, only height is
// proposed by this control.
if (AknLayoutUtils::ScalableLayoutInterfaceAvailable())
{
TAknWindowComponentLayout layout =
AknLayoutScalable_Apps::mce_image_pane_g3();
TAknLayoutRect layoutRect;
layoutRect.LayoutRect( Rect(), layout.LayoutLine() );
aSize.iHeight = layoutRect.Rect().Height();
// Adjust max height if needed
if ( iMaxHeight > 0 && (TUint)aSize.iHeight > iMaxHeight )
{
aSize.iHeight = iMaxHeight;
}
}
else
{ // use old sizing functionality if scalable IF isn't available:
aSize = CalculateControlSize(aSize);
}
SetSizeWithoutNotification(aSize);
}
/**
* This internal function is the old version and is used ONLY if scalable
* layout interface is NOT available. This should be removed when it is always
* available.
*/
const TInt KGmsImageHeightMax = 58;
const TInt KBaseLineDelta = 17;
TSize CGmsPictureControl::CalculateControlSize(TSize& aProposedSize)
{
// These lines below, although they look a bit strange, are required to
// conform with old sizing functionality when scalable layout IF is
// not available:
if ( iMaxHeight == 0 )
{ // max height hasn't been set by the client:
iMaxHeight = KGmsImageHeightMax;
}
if ( (TUint)aProposedSize.iHeight > iMaxHeight )
{ // proposed size is greater than max size -> adjust:
aProposedSize.iHeight = iMaxHeight;
}
TSize trgSize(aProposedSize.iWidth, 0);
if (!iConverter)
{
return trgSize;
}
TSize bmpSize = OriginalImageResolution();
bmpSize.SetSize(bmpSize.iWidth*2, bmpSize.iHeight*2);
if (bmpSize.iHeight < aProposedSize.iHeight)
{
trgSize.iHeight = bmpSize.iHeight;
}
else
{
trgSize.iHeight = aProposedSize.iHeight;
}
// For "wide" images height may be too big - check aspect ratio
TReal widthRatio = (TReal)bmpSize.iWidth / (TReal)trgSize.iWidth;
TReal heightRatio = (TReal)bmpSize.iHeight / (TReal)trgSize.iHeight;
if (widthRatio > heightRatio)
{
// image must be scaled more in vertical size
trgSize.iHeight = (TInt)(bmpSize.iHeight / widthRatio);
}
if (trgSize.iHeight%KBaseLineDelta)
{
trgSize.iHeight = trgSize.iHeight-trgSize.iHeight%KBaseLineDelta+KBaseLineDelta;
}
return trgSize;
}
void CGmsPictureControl::ClipboardL(TMsgClipboardFunc /*aFunc*/)
{
User::Leave(KErrNotSupported); // Image control does not support clipboard
}
void CGmsPictureControl::EditL(TMsgEditFunc /*aFunc*/)
{
User::Leave(KErrNotSupported); // Image control is not editable
}
TBool CGmsPictureControl::IsFocusChangePossible(TMsgFocusDirection /*aDirection*/) const
{
return ETrue;
}
TBool CGmsPictureControl::IsCursorLocation(TMsgCursorLocation /*aLocation*/) const
{
return ETrue;
}
TUint32 CGmsPictureControl::EditPermission() const
{
return EMsgEditNone;
}
void CGmsPictureControl::Reset()
{
delete iBitmap;
iBitmap = NULL;
delete iScaledBitmap;
iScaledBitmap = NULL;
delete iConverter;
iConverter = NULL;
SetModified( ETrue );
}
void CGmsPictureControl::NotifyViewEvent(TMsgViewEvent /*aEvent*/,
TInt /*aParam*/)
{
}
TInt CGmsPictureControl::ConversionReady( TAny* aPtr )
{
__ASSERT_ALWAYS(aPtr, User::Panic(KSmEditorGmsPic, ENullPtr) );
static_cast<CGmsPictureControl*>( aPtr )->DoConversionReady();
return 0;
}
TInt CGmsPictureControl::ScalingReady( TAny* aPtr )
{
__ASSERT_ALWAYS(aPtr, User::Panic(KSmEditorGmsPic, ENullPtr) );
static_cast<CGmsPictureControl*>( aPtr )->DoScalingReady();
return 0;
}
void CGmsPictureControl::PrepareForReadOnly(TBool /*aReadOnly*/)
{
}
TInt CGmsPictureControl::CountComponentControls() const
{
// This method is required by the base class
return 0;
}
CCoeControl* CGmsPictureControl::ComponentControl(TInt /*aIndex*/) const
{
// This method is required by the base class
return NULL;
}
void CGmsPictureControl::FocusChanged( TDrawNow /*aDrawNow*/ )
{
// This method is required by the base class
}
void CGmsPictureControl::SizeChanged()
{
iBitmapRect = Rect();
if (AknLayoutUtils::ScalableLayoutInterfaceAvailable())
{
TAknWindowComponentLayout layout =
AknLayoutScalable_Apps::mce_image_pane_g3();
TAknLayoutRect layoutRect;
layoutRect.LayoutRect( Rect(), layout.LayoutLine() );
iBitmapRect = layoutRect.Rect();
// Adjust max height if needed, this is not the usual case but
// implemented here just because of supporting the existing
// public LoadImageL() method. Usually the size of imageRect
// should be fully determined by avkon layout!
if ( iMaxHeight > 0 && (TUint)iBitmapRect.Size().iHeight > iMaxHeight )
{
iBitmapRect.SetHeight(iMaxHeight);
}
TInt yAxisOffset = Rect().iTl.iY - iBitmapRect.iTl.iY;
iBitmapRect.Move(0,yAxisOffset);
// If leave occurs then scaling is not performed
TRAP_IGNORE( ScaleIfPossibleL( iBitmapRect.Size() ) );
}
else
{ // Use old sizing functionality if scalable IF isn't available:
// Note that usage of image size and image rect is not identical
// to scalable version!
TSize imageSize(CalculateImageSize());
// If leave occurs then scaling is not performed
TRAP_IGNORE( ScaleIfPossibleL( imageSize ) );
}
}
void CGmsPictureControl::Draw(const TRect& /*aRect*/) const
{
// Ensure that scaled bitmap exists and that no scaling is
// currently ongoing.
if ( iScaledBitmap &&
iScaledBitmap->Handle() &&
! iScalingAO->IsActive() )
{
CWindowGc& gc = SystemGc();
gc.DrawBitmap(iBitmapRect,iScaledBitmap);
}
}
/**
* This internal function is the old version and is used ONLY if scalable
* layout interface is NOT available. This should be removed when it is always
* available.
*/
TSize CGmsPictureControl::CalculateImageSize() const
{
TSize trgSize(0,0);
TSize bmpSize = OriginalImageResolution();
bmpSize.SetSize(bmpSize.iWidth*2, bmpSize.iHeight*2);
TRect rect(Rect());
// Width is basically static during the session, but it's always
// calculated anyway.
if (bmpSize.iWidth < rect.Width())
{
// "narrow" image - request only what is needed.
trgSize.iWidth = bmpSize.iWidth;
}
else
{
// "wide" image - request all we can get.
trgSize.iWidth = rect.Width();
}
// Height is dynamic, i.e. changes when moving up&down in the message.
// Request maximum height possible.
// Note: Either max size allowed by spec or the size of the image itself
// is considered here, i.e. not the given rect.
if ( iMaxHeight > 0 && (TUint)bmpSize.iHeight > iMaxHeight )
{
trgSize.iHeight = iMaxHeight;
}
else
{
trgSize.iHeight = bmpSize.iHeight;
}
return trgSize;
}
TBool CGmsPictureControl::ScaleIfPossibleL( TSize aSize )
{
if ( !iBitmap )
{
// Image hasn't been loaded, cannot scale
return EFalse;
}
else if ( iConversionAO->IsActive() )
{
// Conversion is ongoing, but not ready yet,
// image will be scaled after conversion finishes.
return EFalse;
}
else
{
if ( !iScalingAO->IsActive() )
{
// Scaling is possible, it is done asynchronously
delete iScaledBitmap;
iScaledBitmap = NULL;
iScaledBitmap = new( ELeave ) CFbsBitmap;
User::LeaveIfError( iScaler->Scale( iScalingAO->iStatus,
*iBitmap,
TRect(iBitmap->SizeInPixels()),
*iScaledBitmap,
TRect(aSize) ) );
iScalingAO->SetItActive();
return ETrue;
}
}
return EFalse;
}
void CGmsPictureControl::DoConversionReady()
{
__ASSERT_DEBUG( iConversionAO->iStatus == KErrNone,
Panic(EConversionError) );
if ( iConversionAO->iStatus == KErrNone )
{
SetModified( ETrue );
// We want to scale the image now:
SetRect( Rect() );
//we must delete the converter here because it is holding the
//converted imagefile so that it cant be replaced when FORWARD:ing
delete iConverter;
iConverter = NULL;
}
}
void CGmsPictureControl::DoScalingReady()
{
__ASSERT_DEBUG( iScalingAO->iStatus == KErrNone,
Panic(EScalingError) );
if ( iScalingAO->iStatus == KErrNone )
{
SetModified( ETrue );
// We want to draw the image now:
DrawNow();
}
}
void CGmsPictureControl::Panic(TGmsPicControlPanic aCode)
{
User::Panic(KSmEditorGmsPic, aCode);
}
void CGmsPictureControl::ConstructL(const CCoeControl* aParent)
{
__ASSERT_DEBUG( aParent, Panic(ENoParent) );
User::LeaveIfError( iFs.Connect() );
iControlType = EMsgImageControl;
SetContainerWindowL(*aParent);
TRect rect(aParent->Rect());
if (rect.Height() == 0 || rect.Width() == 0)
{
// We have been called before view construction.
// Set rect to screen size.
rect.SetRect (
TPoint(0,0),
iEikonEnv->EikAppUi()->ClientRect().Size());
}
SetPosition(rect.iTl);
SetSizeWithoutNotification(rect.Size());
iConversionAO = new( ELeave ) CGmsAOCallBack(
TCallBack( ConversionReady, this ), CActive::EPriorityStandard );
iScaler = IHLScaler::CreateL();
iScalingAO = new( ELeave ) CGmsAOCallBack(
TCallBack( ScalingReady, this ), CActive::EPriorityStandard );
}
CGmsPictureControl::CGmsPictureControl()
{
// By default iMaxHeight is not used:
iMaxHeight = 0;
}
// End of File