pictographs/AknPictograph/src/AknPictographDrawer.cpp
author William Roberts <williamr@symbian.org>
Wed, 30 Jun 2010 12:29:41 +0100
changeset 71 16f8e3c1d75c
parent 0 05e9090e2422
permissions -rw-r--r--
Re-merge fixes for bug 26, bug 2098, NVGRenderStage code and ini file changes

/*
* 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:  Pictograph drawer
*
*/



// INCLUDE FILES
#include "AknPictographDrawer.h"
#include "AknPictographConstants.h"
#include "AknPictographRange.h"
#include "AknPictographFactory.h"
#include "AknPictographAnimator.h"
#include "AknPictographPanic.h"
#include "AknPictographDrawerFlags.h"

#include <gdi.h>
#include <fbs.h>
#include <eikenv.h>
#include <coecntrl.h>

// CONSTANTS

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CAknPictographDrawer::CAknPictographDrawer
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CAknPictographDrawer::CAknPictographDrawer( 
    MAknPictographAnimatorCallBack& aCallBack ) :
        iCallBack( aCallBack ), iDrawingMode(EDrawingModeNormal)
    {
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::ConstructL()
    {
    iAnimator = static_cast<CAknPictographAnimator*>(
        CCoeEnv::Static( KUidAknPictographAnimator ) );

    if ( !iAnimator )
        {
        // adds CCoeStatic in CoeEnv
        iAnimator = CAknPictographAnimator::CreateSingletonL();
        }

    iAnimator->AddClientL( *this );
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CAknPictographDrawer* CAknPictographDrawer::NewL(
    MAknPictographAnimatorCallBack& aCallBack )
    {
    CAknPictographDrawer* self = 
        new( ELeave ) CAknPictographDrawer( aCallBack );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

// Destructor
CAknPictographDrawer::~CAknPictographDrawer()
    {
    // CCoeStatic list is destroyed in Cone before AppUi, so have to check if
    // it still exist here.
    if ( CCoeEnv::Static( KUidAknPictographAnimator ) )
        {
        StopAnimator();
        if ( iAnimator->RemoveClient( *this ) == 0 )
            {
            // Removes CCoeStatic
            delete iAnimator;
            }
        }
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::SupportedPictographCodesL
// -- API method --
// -----------------------------------------------------------------------------
//
HBufC* CAknPictographDrawer::SupportedPictographCodesL() const
    {
    return AknPictographFactory::SupportedPictographCodesL();
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::IsPictograph
// -- API method --
// -----------------------------------------------------------------------------
//
TBool CAknPictographDrawer::IsPictograph( TText aCode ) const
    {
    return ( aCode >= KPictographRangeStart &&
             aCode <= KPictographRangeEnd );
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::ContainsPictographs
// -- API method --
// -----------------------------------------------------------------------------
//
TBool CAknPictographDrawer::ContainsPictographs( const TDesC& aText ) const
    {
    const TText* text = aText.Ptr();
    TInt length( aText.Length() );

    TBool ret( EFalse );

    for ( TInt i = 0 ; i < length ; i++ )
        {
        if ( IsPictograph( text[i] ) )
            {
            ret = ETrue;
            break;
            }
        }

    return ret;
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::DrawPictograph
// -- API method --
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::DrawPictograph(
    CBitmapContext& aGc,
    const TPoint& aTopLeft,
    TText aCode,
    TAknPictographHeight aHeight ) const
    {
    iFlags &= ~KFlagNullBrushSet;

    iCurrentHeight = aHeight;
    TAknPictographData data;

    TInt ret = FetchPictographData( aCode, data );

    if ( ret == KErrNone )
        {
        DoDrawPictograph( aGc, aTopLeft, data, NULL );
        }
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::DrawPictograph
// -- API method --
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::DrawPictograph(
    CBitmapContext& aGc,
    const TRect& aRect,
    TText aCode,
    TAknPictographHeight aHeight ) const
    {
    DrawPictograph(
        aGc,
        aRect,
        aRect,
        aCode,
        aHeight );
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::DrawPictograph
// -- API method --
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::DrawPictograph(
    CBitmapContext& aGc,
    const TRect& aRect,
    const TRect& aClipRect,
    TText aCode,
    TAknPictographHeight aHeight ) const
    {
    iFlags &= ~KFlagNullBrushSet;

    iCurrentHeight = aHeight;
    TAknPictographData data;

    TInt ret = FetchPictographData( aCode, data );

    if ( ret == KErrNone )
        {
        // Center the pictograph bitmap in the given rect.
        // Round to the left and down if required.

        TRect pictoRect( aRect );

        TInt rectWidth = aRect.Width();
        TInt rectHeight = aRect.Height();

        if ( data.iSize.iWidth < rectWidth )
            {
            pictoRect.iTl.iX += ( rectWidth - data.iSize.iWidth ) / 2;
            }
        if ( data.iSize.iHeight < rectHeight )
            {
            // +1 makes rounding down instead of up.
            pictoRect.iTl.iY += ( rectHeight - data.iSize.iHeight + 1 ) / 2;
            }

        pictoRect.iBr.iX = pictoRect.iTl.iX + data.iSize.iWidth;
        pictoRect.iBr.iY = pictoRect.iTl.iY + data.iSize.iHeight;

        // Calculate rect that is drawn in the pictograph bitmap,
        // based on cliprect.

        TRect intersection = pictoRect;
        intersection.Intersection( aClipRect );

        TPoint topLeft = intersection.iTl;

        // change rect coordinates relative to bitmap's top left
        intersection.Move( -pictoRect.iTl.iX, -pictoRect.iTl.iY );

        DoDrawPictograph( aGc, topLeft, data, &intersection );
        }
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::DrawText
// -- API method --
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::DrawText(
    CBitmapContext& aGc,
    const CFont& aFont,
    const TDesC& aText,
    const TPoint& aPosition ) const
    {
    aGc.DrawText( aText, aPosition );
    DrawPictographsInText( aGc, aFont, aText, aPosition );
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::DrawText
// -- API method --
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::DrawText(
    CBitmapContext& aGc,
    const CFont& aFont,
    const TDesC& aText,
    const TRect& aBox,
    TInt aBaselineOffset,
    CGraphicsContext::TTextAlign aAlignment,
    TInt aLeftMargin ) const
    {
    aGc.DrawText( aText, aBox, aBaselineOffset, aAlignment, aLeftMargin );

    DrawPictographsInText( 
        aGc, aFont, aText, aBox, aBaselineOffset, aAlignment, aLeftMargin );
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::DrawPictographsInText
// -- API method --
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::DrawPictographsInText(
    CBitmapContext& aGc,
    const CFont& aFont,
    const TDesC& aText,
    const TPoint& aPosition ) const
    {
    if ( !ContainsPictographs( aText ) )
        {
        return;
        }

    DrawPictographs( aGc, aFont, aText, aPosition, NULL );
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::DrawPictographsInText
// -- API method --
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::DrawPictographsInText(
    CBitmapContext& aGc,
    const CFont& aFont,
    const TDesC& aText,
    const TRect& aBox,
    TInt aBaselineOffset,
    CGraphicsContext::TTextAlign aAlignment,
    TInt aMargin ) const
    {
    if ( !ContainsPictographs( aText ) )
        {
        return;
        }

    // Work out the text layout.
    TPoint p( aBox.iTl );
    p.iY += aBaselineOffset;

    if ( aAlignment == CGraphicsContext::ELeft )
        {
        p.iX += aMargin;
        }
    else
        {
        // measure the text
	    CFont::TMeasureTextInput input;
	    input.iFlags |= CFont::TMeasureTextInput::EFVisualOrder;
	    TInt textWidth = aFont.MeasureText( aText, &input );

        if ( aAlignment == CGraphicsContext::ERight )
            {
            p.iX = aBox.iBr.iX - aMargin - textWidth;
            }
        else if ( aAlignment == CGraphicsContext::ECenter )
            {
            p.iX += aBox.Width() / 2 + aMargin - textWidth / 2;
            }
        }

    DrawPictographs( aGc, aFont, aText, p, &aBox );
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::IsAnimated
// -----------------------------------------------------------------------------
//
TBool CAknPictographDrawer::IsAnimated(
    TText aCode, TAknPictographHeight aHeight ) const
    {
    TAknPictographData data;

    return AknPictographFactory::AnimatedPictographData(
        aCode, aHeight, 0, data ) == KErrNone;
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::SelectPictographHeightForFont
// -----------------------------------------------------------------------------
//
TInt CAknPictographDrawer::SelectPictographHeightForFont(
            const CFont& aFont,
            TAknPictographHeight& aHeight ) const
    {
    return AknPictographFactory::SelectPictographHeightForFont(aFont,aHeight);
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::SetPictographDrawingMode
// -----------------------------------------------------------------------------
//
TAknPictographDrawingMode CAknPictographDrawer::SetPictographDrawingMode(
            TAknPictographDrawingMode aDrawingMode)
    {
    TAknPictographDrawingMode currentDrawingMode = iDrawingMode;
    iDrawingMode = aDrawingMode;
    return currentDrawingMode;
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::DrawPictographs
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::DrawPictographs(
    CBitmapContext& aGc,
    const CFont& aFont,
    const TDesC& aText,
    const TPoint& aPosition,
    const TRect* aClipRect ) const
    {

    // Choose pictograph height based on font.
    if( AknPictographFactory::SelectPictographHeightForFont(
            aFont,
            iCurrentHeight
            ) != KErrNone)

        {
        return;
        }

    TInt topY = aPosition.iY - aFont.AscentInPixels();
    TInt bottomY = aPosition.iY + aFont.DescentInPixels();

    const TText* text = aText.Ptr();
    TInt length( aText.Length() );

    iFlags &= ~KFlagNullBrushSet;
    iFlags |= KFlagDrawingMultiplePictographs;
   

    // Draw the pictographs in loop.
    for ( TInt i = 0 ; i < length ; i++ )
        {
        if ( IsPictograph( text[i] ) )
            {
            TInt x = aPosition.iX;
            x += aFont.TextWidthInPixels( aText.Left( i ) );

            TRect rect( x, topY, x + aFont.CharWidthInPixels( text[i] ), bottomY );
            TRect clipRect = aClipRect ? *aClipRect : rect;

            DrawPictograph(
                aGc, 
                rect,
                clipRect,
                text[i],
                iCurrentHeight );
            }
        }
    iFlags &= ~KFlagDrawingMultiplePictographs;
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::HandleGainingForeground
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::HandleGainingForeground()
    {
    StartAnimatorIfRequired();
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::HandleLosingForeground
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::HandleLosingForeground()
    {
    StopAnimator();
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::DoDrawPictograph
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::DoDrawPictograph( 
    CBitmapContext& aGc,
    const TPoint& aTopLeft,
    const TAknPictographData& aData,
    TRect* aRect ) const
    {
    // First get the index for accessing the correct bitmap
    // This also validates iCurrentHeight 
    TUint bitmapIndex;
    if( AknPictographFactory::GetArrayIndexForHeight(iCurrentHeight,bitmapIndex) != KErrNone )
        {
        return;
        }

    if ( aData.iAnimated )
        {
        // Animated pictographs used
        iFlags |= KFlagAnimatedPictographs;
        StartAnimatorIfRequired();
        }

    TRect rect = aRect ? *aRect : TRect( aData.iSize );

    // Move to the correct x-offset in the pictograph collection bitmap
    rect.Move( aData.iOffset, 0 );

    RPointerArray<CFbsBitmap>* bitmaps = iAnimator->Bitmaps();
    RPointerArray<CFbsBitmap>* masks = iAnimator->Masks();

    CFbsBitmap* bitmap = (*bitmaps)[bitmapIndex];
    CFbsBitmap* mask = (*masks)[bitmapIndex];


    if ( EDrawingModeWhite==iDrawingMode && 
         !(iFlags & KFlagDrawingMultiplePictographs) )
        {
        // Note: EDrawingModeWhite is supported only if 
        // a single pictograph is drawn only per component->Draw() call.
        // This restriction is needed because aGc.BitBltMasked calls are cached
        // in client side as a result only the last pictograph will be drawn
        // in place all other aGc.BitBltMasked calls.
        bitmap = iAnimator->WhiteBitmap();
        CFbsBitGc* maskGc = iAnimator->WhiteBitmapMaskGc();
        maskGc->Clear();
        maskGc->BitBlt(TPoint(0,0), mask, rect);
        mask = iAnimator->WhiteBitmapMask();
        rect.Move( -rect.iTl );
        }
    
    if ( !( iFlags & KFlagNullBrushSet ) )
        {
        // To minimize WS buffer consumption, we set null brush
        // only once per API call.
        aGc.SetBrushStyle( CGraphicsContext::ENullBrush );
        iFlags |= KFlagNullBrushSet;
        }

    aGc.BitBltMasked(
        aTopLeft,
        bitmap,
        rect,
        mask,
        ETrue );
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::AnimationTick
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::AnimationTick()
    {
    // Clear animated pictographs flag before callback to see whether they are
    // needed any more or not (the flag will be updated during the callback).
    iFlags &= ~KFlagAnimatedPictographs;

    iCallBack.DrawPictographArea();

    // If no animation was required during the redraw callback,
    // stop the animator.
    // It will be started again when a request to draw an animated pictograph
    // is detected.
    if ( !( iFlags & KFlagAnimatedPictographs ) )
        {
        StopAnimator();
        }
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::StartAnimatorIfRequired
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::StartAnimatorIfRequired() const
    {
    TInt bits = iFlags & ( KFlagAnimatedPictographs | KFlagTimerStarted );

    // Animated pictographs but timer not started?
    if ( bits == KFlagAnimatedPictographs )
        {
        iAnimator->Start();
        iFlags |= KFlagTimerStarted;
        }
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::StopAnimator
// -----------------------------------------------------------------------------
//
void CAknPictographDrawer::StopAnimator() const
    {
    if ( iFlags & KFlagTimerStarted )
        {
        iAnimator->Stop();
        iFlags &= ~KFlagTimerStarted;
        }
    }

// -----------------------------------------------------------------------------
// CAknPictographDrawer::FetchPictographData
// -----------------------------------------------------------------------------
//
TInt CAknPictographDrawer::FetchPictographData(
    TText aCode,
    TAknPictographData& aData ) const
    {
    TInt ret = AknPictographFactory::StaticPictographData(
        aCode, iCurrentHeight, aData );

    if ( ret != KErrNone )
        {
        ret = AknPictographFactory::AnimatedPictographData(
            aCode, iCurrentHeight, iAnimator->Counter(), aData );
        }

    return ret;
    }

//  End of File