svgtopt/SVG/SVGImpl/src/SVGTextAreaElementImpl.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 07 Jan 2010 16:19:02 +0200
changeset 0 d46562c3d99d
permissions -rw-r--r--
Revision: 200951 Kit: 201001

/*
* Copyright (c) 2003 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:  SVG Implementation source file
 *
*/


#if !defined(__E32BASE_H__)
#include <e32base.h>
#endif

#include <AknBidiTextUtils.h>

#include "SVGTextAreaElementImpl.h"
#include "SVGRectElementImpl.h"
#include "SVGTextElementImpl.h"
#include "SVGElementImpl.h"
#include "SVGDocumentImpl.h"
#include "SVGSchemaData.h"
#include "SVGEngineImpl.h"
#include "GfxAffineTransform.h"

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
CSvgTextAreaElementImpl* CSvgTextAreaElementImpl::NewL(  const TUint8 aElemID,
												CSvgDocumentImpl* aDoc )
{
	CSvgTextAreaElementImpl*self    = new ( ELeave ) CSvgTextAreaElementImpl( aDoc );
	CleanupStack::PushL( self );
	self->ConstructL(  aElemID);
	CleanupStack::Pop();

	return self;
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
CSvgTextAreaElementImpl* CSvgTextAreaElementImpl::NewLC(  const TUint8 aElemID,
												 CSvgDocumentImpl* aDoc )
{
	CSvgTextAreaElementImpl*self    = new ( ELeave ) CSvgTextAreaElementImpl( aDoc );
	CleanupStack::PushL( self );
	self->ConstructL(  aElemID );

	return self;
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
CSvgTextAreaElementImpl::~CSvgTextAreaElementImpl()
{
    if ( iSvgStyleProperties )
		{
			iSvgStyleProperties->Close();
			delete iSvgStyleProperties;
			iSvgStyleProperties = NULL;
		}

		if ( iInnerRectangle )
		{
			this->RemoveChild(iInnerRectangle);
			delete iInnerRectangle;
			iInnerRectangle = NULL;
		}

		if ( iBorderRectangle )
		{
			this->RemoveChild(iBorderRectangle);
			delete iBorderRectangle;
			iBorderRectangle = NULL;
		}

		if (iTextElementsArray)
		{
			iTextElementsArray->Reset();
			iTextElementsArray->Close();
			delete iTextElementsArray;
			iTextElementsArray = NULL;
		}
	if (iAllText)
	{
		delete iAllText;
		iAllText = NULL;
	}

	((CSvgDocumentImpl*)OwnerDocument())->RemoveInternalMouseListener( this );

}

// *******************************************************
// From SVG DOM
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TFloatFixPt CSvgTextAreaElementImpl::X()
{
	if (!iInitialized)
	{
		return 0;
	}

    return iBorderRectangle->X();
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TFloatFixPt CSvgTextAreaElementImpl::Y()
{
	if (!iInitialized)
	{
		return 0;
	}

    return iBorderRectangle->Y();
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TFloatFixPt CSvgTextAreaElementImpl::Width()
{
	if (!iInitialized)
	{
		return 0;
	}

    return iBorderRectangle->Width();
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TFloatFixPt CSvgTextAreaElementImpl::Height()
{
	if (!iInitialized)
	{
		return 0;
	}

    return iBorderRectangle->Height();
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TFloatFixPt CSvgTextAreaElementImpl::Rx()
{
	if (!iInitialized)
	{
		return 0;
	}

    return iBorderRectangle->Rx();
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TFloatFixPt CSvgTextAreaElementImpl::Ry()
{
	if (!iInitialized)
	{
		return 0;
	}

    return iBorderRectangle->Ry();
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TBool CSvgTextAreaElementImpl::IsEditable()
{
    return iEditable;
}


// ---------------------------------------------------------------------------
// For JSR support
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::SetTextAreaDimensions(TFloatFixPt aX, TFloatFixPt aY, TFloatFixPt aWidth, TFloatFixPt aHeight)
{
	if (!iInitialized)
	{
		TRAPD(err, InitializeL());
    	if (err)
    	    {
		    #ifdef _DEBUG
            _LIT( KErrMsg, "CSvgTextAreaElementImpl::SetTextAreaDimensions Error in InitializeL()" );
            RDebug::Print(KErrMsg);
            #endif //_DEBUG
    	    }
	}

	TRAPD(err, iBorderRectangle->SetAttributeFloatL(KAtrX, aX));
	if (err)
	    {
	    #ifdef _DEBUG
        _LIT( KErrMsg, "CSvgTextAreaElementImpl::SetTextAreaDimensions Error in SetAttributeFloatL" );
        RDebug::Print(KErrMsg);
        #endif //_DEBUG
	    }

	TRAP(err, iBorderRectangle->SetAttributeFloatL(KAtrY, aY));
	if (err)
	    {
	    #ifdef _DEBUG
        _LIT( KErrMsg, "CSvgTextAreaElementImpl::SetTextAreaDimensions Error in SetAttributeFloatL" );
        RDebug::Print(KErrMsg);
        #endif //_DEBUG
	    }

	TRAP(err, iBorderRectangle->SetAttributeFloatL(KAtrWidth, aWidth));
	if (err)
	    {
	    #ifdef _DEBUG
        _LIT( KErrMsg, "CSvgTextAreaElementImpl::SetTextAreaDimensions Error in SetAttributeFloatL" );
        RDebug::Print(KErrMsg);
        #endif //_DEBUG
	    }

	TRAP(err, iBorderRectangle->SetAttributeFloatL(KAtrHeight, aHeight));
	if (err)
	    {
	    #ifdef _DEBUG
        _LIT( KErrMsg, "CSvgTextAreaElementImpl::SetTextAreaDimensions Error in SetAttributeFloatL" );
        RDebug::Print(KErrMsg);
        #endif //_DEBUG
	    }
}

// From MXmlElementOpt
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TInt CSvgTextAreaElementImpl::GetAttributeFloat( const TInt aNameId,
                                             TFloatFixPt& aValue )
{
	if (!iInitialized)
	{
		TRAPD(err, InitializeL());
    	if (err)
    	    {
		    #ifdef _DEBUG
            _LIT( KErrMsg, "CSvgTextAreaElementImpl::GetAttributeFloat Error in InitializeL()" );
            RDebug::Print(KErrMsg);
            #endif //_DEBUG
    	    }
	}

    switch ( aNameId )
        {
        case KAtrX:
        case KAtrRefX:
        		aValue = X();
        break;
        case KAtrY:
        case KAtrRefY:
        		aValue = Y();
        break;
        case KAtrWidth:
        		aValue = Width();
        break;
        case KAtrHeight:
        		aValue = Height();
        break;
		// added as a part of get and Set api updation
		case KAtrRx:
				aValue = Rx();
		break;
		case KAtrRy:
				aValue = Ry();
		break;
        default:
        		return CSvgElementImpl::GetAttributeFloat( aNameId, aValue );
        }
    return KErrNone;
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TInt CSvgTextAreaElementImpl::SetAttributeFloatL( const TInt aNameId,
                                              const TFloatFixPt aValue )
{
	if (!iInitialized)
	{
		InitializeL();
	}

    switch ( aNameId )
        {
        case KAtrX:
			iBorderRectangle->SetAttributeFloatL(aNameId, aValue);
			iInnerRectangle->SetAttributeFloatL(aNameId, aValue+TFloatFixPt(2));
		break;
        case KAtrY:
			iBorderRectangle->SetAttributeFloatL(aNameId, aValue);
			iInnerRectangle->SetAttributeFloatL(aNameId, aValue+TFloatFixPt(2));
		break;
		case KAtrRx:
			iBorderRectangle->SetAttributeFloatL(aNameId, aValue);
			//iInnerRectangle->SetAttributeFloatL(aNameId, aValue+TFloatFixPt(2));
		break;
		case KAtrRy:
			iBorderRectangle->SetAttributeFloatL(aNameId, aValue);
			//iInnerRectangle->SetAttributeFloatL(aNameId, aValue+TFloatFixPt(2));
		break;
        case KAtrWidth:
			if(iReqAttrFlag == KAtrSVGRec)
			{
				iReqAttrFlag = KAtrHeight;
			}
			else
			{
				iReqAttrFlag = 0;
			}

			iBorderRectangle->SetAttributeFloatL(aNameId, aValue);
			iInnerRectangle->SetAttributeFloatL(aNameId, aValue-TFloatFixPt(4));
		break;

        case KAtrHeight:
					if(iReqAttrFlag == KAtrSVGRec)
						{
						iReqAttrFlag = KAtrWidth;
						}
					else
						{
						iReqAttrFlag = 0;
						}
					iBorderRectangle->SetAttributeFloatL(aNameId, aValue);
			iInnerRectangle->SetAttributeFloatL(aNameId, aValue-TFloatFixPt(4));
			        break;
        default:
        return CSvgElementImpl::SetAttributeFloatL( aNameId, aValue );
        }
    return KErrNone;
}


// *******************************************************
// From MXmlElement
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TInt CSvgTextAreaElementImpl::SetAttributeL( const TDesC& aName,
                                         const TDesC& aValue )
{
	if (!iInitialized)
	{
		InitializeL();
	}

		CSvgElementImpl::SetAttributeL(aName,aValue);
    return KErrNone;
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TInt CSvgTextAreaElementImpl::GetAttributeDes( const TInt aNameId, TPtrC16& aValue )
    {
    if (!iInitialized)
	{
		return KErrNone;
	}

    switch ( aNameId )
        {
        case KAtrCdata:
        aValue.Set(*iAllText);
        break;
        default:
        return CSvgElementImpl::GetAttributeDes( aNameId, aValue );
        }
    return KErrNone;
    }
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TInt CSvgTextAreaElementImpl::SetAttributeDesL( const TInt aNameId,
                                            const TDesC& aValue )
    {
    switch ( aNameId )
        {
        case KAtrCdata:
        SetTextL( aValue );
        break;

        default:
        return CSvgElementImpl::SetAttributeDesL( aNameId, aValue );
        }
    return KErrNone;
    }


// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::SetEditable( const TDesC& aValue )
{
	if (aValue.FindF(_L("simple")) == KErrNotFound && aValue.FindF(_L("true")) == KErrNotFound)
	{
		iEditable = EFalse;
	}
	else
	{
		iEditable = ETrue;
	}
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::LocalizedArrangeTextL( CGfx2dGc* /* aGc */ )
    {
	if (!iInitialized)
	    {
		return;
	    }

	//every time we call arrange text we will clear the previous text elements
	//very expensive but easiest way to do this
	if (iTextElementsArray)
	    {
		TInt textEleArrayCnt = iTextElementsArray->Count();
		for (TInt i=0; i < textEleArrayCnt; i++)
		    {
			this->RemoveChild( iTextElementsArray->operator[](i) );
			delete iTextElementsArray->operator[](i);
		    }

		iTextElementsArray->Reset();
	    }
    // If there is some text in the text area, we need to do the following operations
    //      a. calculate number of lines that can fit in the inner rectangle of textArea 
    //          i. For this we need to get the dimension of Inner Rectangle
    //          ii. Then get the font corresponding to the current scale. This is obtained
    //              by creating a dummy text element and updating its CTM.
    //
	if (iAllText && iAllText->Length() > 0)
	    {
		TFloatFixPt lInnerRectWidthF, lInnerRectHeightF;
		iInnerRectangle->GetAttributeFloat( KAtrWidth, lInnerRectWidthF );
		iInnerRectangle->GetAttributeFloat( KAtrHeight, lInnerRectHeightF );
        
        // Create a dummy text element to obtain the font
      	CSvgTextElementImpl* lTextElement  = 
      	    (CSvgTextElementImpl*)iOwnerDocument->CreateElementL(KSvgTextElement);
      	CleanupStack::PushL(lTextElement);
        lTextElement->SetAttributeDesL(KAtrCdata,iAllText->Des());
        
        this->AppendChildL(lTextElement);
        CleanupStack::Pop(lTextElement);
        lTextElement->UpdateCTM();
        // Update the text element's font corresponding to the CTM.
        lTextElement->UpdateCurrentScaledFont();

        TFloatFixPt lScale = GetCurrentScale();
        
        //Taken the widest charactor to check against the dimensions of the text area.
        //There is no point in going futher, If the area of text area elelment 
        //is not big enough to fit the single char,
        TChar ch('W');
        if ( lScale == TFloatFixPt( 0 ) ||
             ((TReal32)( lInnerRectWidthF * lScale)) < lTextElement->Font()->CharWidthInPixels( ch ) ||
             ((TReal32)( lInnerRectHeightF * lScale)) < lTextElement->Font()->FontMaxHeight())
            {
            // Scale = 0 , means no drawing will take place.
            // Cleanup the text element
    		this->RemoveChild(lTextElement);
			delete lTextElement;
			lTextElement = NULL;
            return;
            }
        
        // Store the current font's ascent and descent
        TInt lTextAscent = lTextElement->Font()->FontMaxAscent();
        TInt lTextDescent = lTextElement->Font()->FontMaxDescent();

	TReal32 lTextHeight = lTextAscent + lTextDescent;

        if (lTextHeight == 0)
	        {
	    	//no text height so we need to not draw the text
    		this->RemoveChild(lTextElement);
			delete lTextElement;
			lTextElement = NULL;
	    	return;
            }
            
        // Calculate gap between two consecutive lines of text
        // CFont provides FontLineGap() which is Font Height + Line Gap
        
        TInt lLineGap = lTextElement->Font()->FontLineGap();
        // Number of lines of text
	    TInt lNumOflines = (TReal32)( lInnerRectHeightF * lScale)/( lLineGap ) ;

        if( lNumOflines < 1)
            {
        	//text is taller than the rectangle
    		this->RemoveChild(lTextElement);
			delete lTextElement;
			lTextElement = NULL;
            return;
            }
        // Create a list of line widths and line texts to use with ConvertToVisualAndWrapToArrayL
        CArrayFixFlat<TInt>* lLineWidthArray = new ( ELeave ) CArrayFixFlat<TInt>( lNumOflines );
        CleanupStack::PushL(lLineWidthArray);
        
        //one px offset is taken on Left & right ends w.r. to inner rect width.
        lLineWidthArray->AppendL((TInt)( lInnerRectWidthF * lScale ) - 2,lNumOflines);

        CArrayFixFlat< TPtrC >*   lWrappedArray = new ( ELeave ) CArrayFixFlat<TPtrC>( lLineWidthArray->Count() );

        CleanupStack::PushL(lWrappedArray);
        
        //ConvertToVisualAndWrapToArrayL requires KAknBidiExtraSpacePerLine. 
        //Refer the help of AknBidiTextUtils::ConvertToVisualAndWrapToArrayL
        HBufC *lBuf = HBufC::NewLC(iAllText->Length() + (lNumOflines) * KAknBidiExtraSpacePerLine);

        TPtr buf = lBuf->Des();
        buf.Copy(iAllText->Des());
        // Below API fills the lWrappedArray with the lines of text. If no space 
        // is available in textArea, then trailing ellipsis(...) will be displayed.
        AknBidiTextUtils::ConvertToVisualAndWrapToArrayL(buf, *lLineWidthArray, 
            *lTextElement->iBitmapFont, *lWrappedArray, ETrue);
        
        // Dummy text element is no longer required.
		this->RemoveChild(lTextElement);
		delete lTextElement;
		lTextElement = NULL;
		
		// Calculating the y offset for first line of text. 
		// Text is positioned wrt font baseline, hence the position =
		//      Inner Rectangle 's Y Coordinate + Unscaled Font Ascent
		// The scaling will be taken care of by UpdateCTM on the text elements.
        TInt lTextCurrentY = TReal32(iInnerRectangle->Y()) + (lTextAscent / (TReal32)lScale );
		TInt lWrappedArrayCnt = lWrappedArray->Count();
		
		// lWrappedArrayCnt is guaranteed to be less than lNumOfLines.
		// This loop creates Text  elements as many as elements in lWrapppedArray.
        // These elements are appended as child to the text area.
		for( TInt i=0; i < lWrappedArrayCnt ; i++)
		    {
		    lTextElement = (CSvgTextElementImpl*)iOwnerDocument->CreateElementL(KSvgTextElement);
            CleanupStack::PushL( lTextElement );
            iTextElementsArray->Append(lTextElement);
		    
		    TPtrC lineText = lWrappedArray->At(i);
		    lTextElement->SetAttributeDesL(KAtrCdata,lineText);
		    
		    //Text positioned from the inner rectangle's X- coordinate + 1 pixel gap 
	    	lTextElement->SetAttributeFloatL(KAtrX, TFloatFixPt(iInnerRectangle->X() + TFloatFixPt(1)));

            //need to set the next text element up to go down the height here too...
            lTextElement->SetAttributeFloatL(KAtrY, TFloatFixPt(lTextCurrentY));

			lTextElement->SetAttributeFloatL(KAtrWidth, TFloatFixPt(iInnerRectangle->Width() - TFloatFixPt(2)));
			lTextElement->SetAttributeFloatL(KAtrHeight, lTextHeight);

            this->AppendChildL(lTextElement);

            lTextElement->UpdateCTM();
            lTextElement->UpdateCurrentScaledFont();

            //The subsequent position calculated as lTextHeight( Ascent + descent ) / unscaled line gap
            lTextCurrentY +=  (TInt)(( lLineGap )/(TReal32)lScale);

            CleanupStack::Pop( lTextElement );
		    }

		 //delete lBuf;
		 CleanupStack::PopAndDestroy(lBuf);
		 CleanupStack::Pop(lWrappedArray);
		 delete lWrappedArray;
		 CleanupStack::Pop(lLineWidthArray);
		 delete lLineWidthArray;

	}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TInt CSvgTextAreaElementImpl::GetLength()
    {
        return iAllText->Length();
    }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::GetText( TDes& allText )
    {
    if ( iAllText )
        {
    	if ( allText.MaxLength() < iAllText->Length() )
    	    {
    	    allText = iAllText->Left( allText.MaxLength() );
    	    }
    	    else
    	        {
    	        allText = *iAllText;
    	        }
        }
    }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::SetTextL( const TDesC& aText )
    {

   	_LIT( KPreserve, "preserve" );
    _LIT( KDefault, "default");
    if ( iAllText )
        {
        delete iAllText;
        iAllText = NULL;
        }

    iAllText = aText.AllocL();
    TPtr textDes = iAllText->Des();
    TBool def = ETrue;
    
    // Get the xml:space value.
    TBuf<20> lXmlspace;    
   
    if(XMLSpace() == KPreserve)
    	{
    	lXmlspace=KPreserve;
    	def=EFalse;
    	}
    else if(XMLSpace() == KDefault)
    	{
    	lXmlspace=KDefault;
    	}
    // xml:space is inherited if not specified
    if ( lXmlspace!= KPreserve && lXmlspace != KDefault) 
        {
    	// Traverse to the nearest parent element which has the xml:space 
		// If no xml:space then render using xml:space="default"
	    CSvgElementImpl *lRoot=(CSvgElementImpl *)this->ParentNode();
	 	while(lRoot)
	 		{
	 		if ((lRoot->XMLSpace() == KPreserve))
	    		{
	    		def = EFalse;
	    		break;
	        	}
	        else if(lRoot->XMLSpace()==KDefault)
	        	{
	        	break;	
	        	}
	        lRoot=(CSvgElementImpl *)lRoot->ParentNode();
	 		}
        }
    
    _LIT(KSpace, " ");
    // default    
    if ( def && lXmlspace != KPreserve )
        {
        
        // Remove leading, trailing and contiguous spaces
        textDes.TrimAll();
        
        for (TInt i = textDes.Length() - 1; i >= 0; i--)
        	{
                	
        	// Remove new line character
        	if (textDes[i] == '\n') 
       			{
          		textDes.Delete(i,1);
       			}
          	
          	// Tab to be replaced with space.
          	if(textDes[i] == '\t')
          		{
          		textDes.Replace(i,1,KSpace);
          		}           	
        	}
        }
    // preserve
    else
        {
       
        for ( TInt i = 0; i < textDes.Length(); i++ )
            {
            // ms-dos carriage return contains two characters: 13, 10
           	const TInt KCarriageReturn1 = 13;
           	const TInt KCarriageReturn2 = 10;
           	
           	if ( i + 1 < textDes.Length() &&
           	 	(TInt)textDes[i] == KCarriageReturn1 && 
           	 	(TInt)textDes[i+1] == KCarriageReturn2 )
              	{
                textDes[i] = ' ';
                textDes.Delete( i+1, 1 );
       		    }
			
			// New line and tab should be replaced by space character.         
           	if (textDes[i] == '\n' ||textDes[i] == '\t')
              	{
                textDes.Replace(i,1, KSpace);
               	}
            }
        }
         
        iNeedTextRearrange = ETrue;

    }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::InitializeL()
{
	if (!iBorderRectangle && !iInnerRectangle)
	{
		iBorderRectangle = CSvgRectElementImpl::NewL(KSvgRectElement, (CSvgDocumentImpl*)iOwnerDocument);
		this->AppendChildL(iBorderRectangle);
		iInnerRectangle = CSvgRectElementImpl::NewL(KSvgRectElement, (CSvgDocumentImpl*)iOwnerDocument);
		this->AppendChildL(iInnerRectangle);

		iInnerRectangle->SetAttributeFloatL(KAtrRx, 2);
		iInnerRectangle->SetAttributeFloatL(KAtrRy, 2);
		iInnerRectangle->SetPropertyL(KCSS_ATTR_FILL, _L("none"));
		iInnerRectangle->SetPropertyL(KCSS_ATTR_STROKEWIDTH, _L(".02"));
		iInnerRectangle->SetPropertyL(KCSS_ATTR_STROKE, _L("black"));

		iBorderRectangle->SetAttributeFloatL(KAtrRx, 2);
		iBorderRectangle->SetAttributeFloatL(KAtrRy, 2);
		iBorderRectangle->SetPropertyL(KCSS_ATTR_FILL, _L("none"));
		iBorderRectangle->SetPropertyL(KCSS_ATTR_STROKEWIDTH, _L(".1"));
		iBorderRectangle->SetPropertyL(KCSS_ATTR_STROKE, _L("black"));

		iInitialized = ETrue;
	}
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TBool CSvgTextAreaElementImpl::DrawL( CGfx2dGc* aGc, CSvgElementImpl* aElement )
{
	if (iBorderRectangle->Width() <= TFloatFixPt(0) || iBorderRectangle->Height() <= TFloatFixPt(0))
		{
			return ETrue;
		}

    TFloatFixPt scale = GetCurrentScale();
    TFloatFixPt width = iInnerRectangle->Width() * scale;
    TFloatFixPt height = iInnerRectangle->Height() * scale;

    if ( width != iLastBBox.iWidth || height != iLastBBox.iHeight )
    {
        iNeedTextRearrange = ETrue;
        iLastBBox.iWidth = width;
        iLastBBox.iHeight = height;
    }

    DrawTextElements( aGc, aElement );

    return ETrue;
 }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TBool CSvgTextAreaElementImpl::DrawTextElements( CGfx2dGc* aGc, CSvgElementImpl* /*aElement*/ )
{
	//draw text elements...readjust their positions according the width and height of the inner rectangle...
    if (iNeedTextRearrange)
    {
    	//TRAPD(err, ArrangeTextL(aGc));
    	TRAPD(err, LocalizedArrangeTextL(aGc));
    	if (err)
    	    {
		    #ifdef _DEBUG
            _LIT( KErrMsg, "CSvgTextAreaElementImpl::DrawTextElements Error in ArrangeTextL()" );
            RDebug::Print(KErrMsg);
            #endif //_DEBUG
    	    }

    	iNeedTextRearrange = EFalse;
    }

    //draw text until it reaches the inner border then start a new text element on the next line down
    /*if (iTextElementsArray)
    {
    	 for (int i=0; i < iTextElementsArray->Count(); i++)
    	{
			//draw text elements here...
			TRAPD(err, iTextElementsArray->operator[](i)->DrawL(aGc, aElement));
        	if (err)
        	    {
    		    #ifdef _DEBUG
                _LIT( KErrMsg, "CSvgTextAreaElementImpl::DrawTextElements Error in DrawL()" );
                RDebug::Print(KErrMsg);
                #endif //_DEBUG
        	    }

  		}
    }*/

  	return ETrue;
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::ConstructL(  const TUint8 aElemID )
{
		CSvgElementImpl::InitializeL( aElemID );

		iSvgStyleProperties = new(ELeave) RPointerArray<CCssValue>(KCSS_MAX_ATTR );

    	User::LeaveIfError( iSvgStyleProperties->Append( NULL ) );
    	iSvgStyleProperties->Remove( 0 );

		iTextElementsArray = new (ELeave)RPointerArray<CSvgTextElementImpl> (1);
		iTextElementsArray->Reset();

  		iSvgTransformable = CSvgTransformableImpl::NewL();

		iReqAttrFlag=KSVG_RECT_ELEMFLAG;

		iBorderRectangle = NULL;
		iInnerRectangle = NULL;

		iNeedTextRearrange = ETrue;
		iInitialized = EFalse;

		iAllText = HBufC::NewL( 2 );

  		iEditable = EFalse;

  	// Add this as internal event receiver
  	//((CSvgDocumentImpl*)iOwnerDocument)->AddToEventReceiverListL( this, KSvgEventMaskExternalUI);

	((CSvgDocumentImpl*)OwnerDocument())->AddInternalMouseListener( this );

}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------

CSvgTextAreaElementImpl::CSvgTextAreaElementImpl( CSvgDocumentImpl* aDoc )
{
		SetOwnerDocument(aDoc);
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::GetBBox( TGfxRectangle2D& aBbox )
{
	if (!iInitialized)
	{
		TGfxRectangle2D myRect(0,0,0,0);
		aBbox = myRect;
		return;
	}

    iBorderRectangle->GetBBox( aBbox );
}

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::GetUnscaledBBox( TGfxRectangle2D& aBbox )
{
	if (!iInitialized)
	{
		TGfxRectangle2D myRect(0,0,0,0);
		aBbox = myRect;
		return;
	}

    iBorderRectangle->GetUnscaledBBox(aBbox);
}

// ---------------------------------------------------------------------------
// perform a deep clone of this object
// ---------------------------------------------------------------------------
MXmlElement* CSvgTextAreaElementImpl::CloneL(MXmlElement* aParentElement)
{
    // create new text area
	CSvgTextAreaElementImpl* newElement = CSvgTextAreaElementImpl::NewL(  this->ElemID(), ((CSvgDocumentImpl*)iOwnerDocument) );

	newElement->iParentNode = aParentElement;
    // copy everything over
    this->CopyL(newElement);

    return newElement;
}

// ---------------------------------------------------------------------------
// perform a deep copy of this object
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::CopyL( CSvgTextAreaElementImpl* aDestElement )
{
    // copy stuff from superclass
    this->CSvgElementImpl::CopyL(aDestElement);

    // copy iRectangle items special to circle
    aDestElement->iBorderRectangle = (CSvgRectElementImpl*)iBorderRectangle->CloneL((MXmlElement*)iBorderRectangle);
    aDestElement->iInnerRectangle = (CSvgRectElementImpl*)iInnerRectangle->CloneL((MXmlElement*)iInnerRectangle);

    if (iTextElementsArray)
    {
    	TInt textEleArrayCnt = iTextElementsArray->Count();
   	 	for (int i=0; i < textEleArrayCnt; i++)
    	{
    		aDestElement->iTextElementsArray->Append((CSvgTextElementImpl*)iTextElementsArray->operator[](i)->CloneL((MXmlElement*)iTextElementsArray->operator[](i)));
    	}
    }

    // copy the reference to idoc (CSvgDocumentImpl)
    aDestElement->iOwnerDocument = this->iOwnerDocument;
}

/*** FROM MSvgMouseListener ***/
// ---------------------------------------------------------------------------
// mouse entered
// ---------------------------------------------------------------------------
TBool CSvgTextAreaElementImpl::MouseEntered( RPointerArray<CSvgElementImpl>& aElements,
                                    TInt /*aX*/, TInt /*aY*/ )
{
	CSvgEngineImpl* engine  = ( ( CSvgDocumentImpl* ) OwnerDocument() )->Engine();

	TInt eleCnt = aElements.Count();
	for (TInt i = 0; i < eleCnt; i++ )
	{
		if ( aElements[i] == this )
		{
			engine->NotifyTextAreaEntered(this);
			return ETrue;
		}
    }

	return EFalse;
}

// ---------------------------------------------------------------------------
// Notified when the mouse pointer exits a visible svg element.
// ---------------------------------------------------------------------------
TBool CSvgTextAreaElementImpl::MouseExited( RPointerArray<CSvgElementImpl>& aElements,
                                   TInt /*aX*/, TInt /*aY*/ )
{
	CSvgEngineImpl* lEngine  = ( ( CSvgDocumentImpl* ) OwnerDocument() )->Engine();

	TInt eleCnt = aElements.Count();
	for (TInt i = 0; i < eleCnt; i++ )
	{
		if ( aElements[i] == this )
		{
			lEngine->NotifyTextAreaExited(this);
			return ETrue;
		}
    }

	return EFalse;
}

// ---------------------------------------------------------------------------
// Notified when the mouse pointer is pressed down on visible svg element.
// ---------------------------------------------------------------------------
TBool CSvgTextAreaElementImpl::MouseMoved( RPointerArray<CSvgElementImpl>& /*aElements*/,
                                    TInt /*aX*/, TInt /*aY*/ )
{
    return EFalse;
}
// ---------------------------------------------------------------------------
// Notified when the mouse pointer is pressed down on visible svg element.
// ---------------------------------------------------------------------------
TBool CSvgTextAreaElementImpl::MousePressed( RPointerArray<CSvgElementImpl>& /*aElements*/,
                                    TInt /*aX*/, TInt /*aY*/ )
{
	return EFalse;
}

// ---------------------------------------------------------------------------
// Notified when the mouse pointer is released on on visible svg element.
// ---------------------------------------------------------------------------
TBool CSvgTextAreaElementImpl::MouseReleased( RPointerArray<CSvgElementImpl>& aElements,
                                    TInt /*aX*/, TInt /*aY*/ )
{
	CSvgEngineImpl* lEngine  = ( ( CSvgDocumentImpl* ) OwnerDocument() )->Engine();

	TInt eleCnt = aElements.Count();
	for (TInt i = 0; i < eleCnt; i++ )
	{
		if ( aElements[i] == this )
		{
			lEngine->NotifyTextAreaActivated(this);
			return ETrue;
		}
    }

    return EFalse;
}

// ---------------------------------------------------------------------------
// void CSvgTextAreaElementImpl::Print()
// ---------------------------------------------------------------------------
void CSvgTextAreaElementImpl::Print( TBool aIsEncodeOn )
{
	if (!aIsEncodeOn)
	{
		#ifdef _DEBUG
		RDebug::Printf("<textarea x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" editable=\"%d\" >hmm</textarea>", (int)X(), (int)Y(), (int)Width(), (int)Height(), (int)iEditable/*, iAllText*/);
		#endif
	}
}