/*
* Copyright (c) 1997-1999 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:
*
*/


#include <eiklabel.h>
#include <coeutils.h>
#include <eikenv.h>
#include <eikpanic.h>
#include <barsread.h>
#include <uikon.hrh>
#include "laflabel.h"

#include <gulcolor.h>
#include <AknUtils.h>

#include <bidivisual.h> 
#include <biditext.h>
#include <AknBidiTextUtils.h>

#include <AknPictographInterface.h>
#include <AknPictographDrawerInterface.h>
#include <AknLayoutFont.h>

const TInt KTextEmphasisMask		= 0x30;
const TInt KTextBufferReserveLength = 0x40;


/* This class needs to be in .cpp file so that we
   do not accidentally make it derivable; that would
   destroy all the binary compability advantages this
   class has..
   */
NONSHARABLE_CLASS(CEikLabelExtension) : public CBase
	{
public: // enums
    enum {
        ECropText,
        EUseDynamicBidiTextAlignment,
        EUseBrushStyle
        };
public:
	static CEikLabelExtension* NewL();
	~CEikLabelExtension();
	void SetTextLengthL(TInt aLength);

private:
	CEikLabelExtension();
	void ConstructL();
public: //data
	HBufC* iLabelLine;
    TBitFlags iFlags;
    CWindowGc::TBrushStyle iBrushStyle;
    CAknPictographInterface* iPictographInterface; // not owned
	};

CEikLabelExtension* CEikLabelExtension::NewL()
	{
	CEikLabelExtension* self = new(ELeave) CEikLabelExtension;
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CEikLabelExtension::CEikLabelExtension()
	{
    iFlags.ClearAll();
	}

CEikLabelExtension::~CEikLabelExtension()
	{
	delete iLabelLine;
	}

void CEikLabelExtension::ConstructL()
	{
	iLabelLine = HBufC::NewL(0);
	}

void CEikLabelExtension::SetTextLengthL(TInt aLength)
	{
    aLength += TBidiLogicalToVisual::KMinCharAvailable;
	if (aLength <= iLabelLine->Des().MaxLength())
		return;

	HBufC* tempBuf = HBufC::NewL(aLength);
	delete iLabelLine;
	iLabelLine = tempBuf;
	}

/**
 * Gets the list of logical colors employed in the drawing of the control,
 * paired with an explanation of how they are used. Appends the list to aColorUseList.
 *
 * @since ER5U 
 */
EXPORT_C void CEikLabel::GetColorUseListL(CArrayFix<TCoeColorUse>& aColorUseList) const
	{
	CCoeControl::GetColorUseListL(aColorUseList);

	TInt commonAttributes = TCoeColorUse::ESurrounds|TCoeColorUse::ENormal|TCoeColorUse::ENeutral;
	TCoeColorUse colorUse;

	colorUse.SetLogicalColor(EColorLabelText);
	colorUse.SetUse(TCoeColorUse::EFore|TCoeColorUse::EActive|commonAttributes);
	aColorUseList.AppendL(colorUse);

	colorUse.SetLogicalColor(EColorControlBackground);
	colorUse.SetUse(TCoeColorUse::EBack|TCoeColorUse::EActive|commonAttributes);
	aColorUseList.AppendL(colorUse);

	colorUse.SetLogicalColor(EColorLabelDimmedText);
	colorUse.SetUse(TCoeColorUse::EFore|TCoeColorUse::EDimmed|commonAttributes);
	aColorUseList.AppendL(colorUse);

	TInt highlightAttributes = TCoeColorUse::EHighlights|TCoeColorUse::ENormal|TCoeColorUse::ENeutral;

	colorUse.SetLogicalColor(EColorLabelTextEmphasis);
	colorUse.SetUse(TCoeColorUse::EFore|TCoeColorUse::EActive|highlightAttributes);
	aColorUseList.AppendL(colorUse);

	colorUse.SetLogicalColor(EColorLabelHighlightFullEmphasis);
	colorUse.SetUse(TCoeColorUse::EBack|TCoeColorUse::EActive|highlightAttributes);
	aColorUseList.AppendL(colorUse);

	colorUse.SetLogicalColor(EColorLabelHighlightPartialEmphasis);
	colorUse.SetUse(TCoeColorUse::EBack|TCoeColorUse::EDimmed|highlightAttributes);
	aColorUseList.AppendL(colorUse);
	}

/**
 * Handles a change to the control's resources of type aType
 * which are shared across the environment, e.g. colors or fonts.
 *
 * @since ER5U 
 */
EXPORT_C void CEikLabel::HandleResourceChange(TInt aType)
	{
	CCoeControl::HandleResourceChange(aType);
	}

EXPORT_C CEikLabel::~CEikLabel()
	{
	delete iText;
	delete iExtension;
 	}

EXPORT_C CEikLabel::CEikLabel()	: 
    iLabFlags( EUseLogicalToVisualConversion ),
    iGapBetweenLines( LafLabel::DefaultGapBetweenLines() )
	{
	__DECLARE_NAME(_S("CEikLabel"));
	iFont=iCoeEnv->NormalFont();
	}

EXPORT_C void CEikLabel::SetLabelAlignment(TInt aAlignment)
	{
    if (CheckAndCreateExtension())
        iExtension->iFlags.Assign(CEikLabelExtension::EUseDynamicBidiTextAlignment, (aAlignment == ELayoutAlignBidi));

    switch(aAlignment)
        {
        case ELayoutAlignBidi: // alignment handled above in extension above
            break;
        default:
            SetAlignment(AknLayoutUtils::GulAlignFromId(aAlignment));
            break;
        }
	}

EXPORT_C void CEikLabel::SetBrushStyle(CWindowGc::TBrushStyle aBrushStyle)
    {
    if (CheckAndCreateExtension())
        {
        iExtension->iBrushStyle = aBrushStyle;
        iExtension->iFlags.Set(CEikLabelExtension::EUseBrushStyle);
        }
    }

EXPORT_C void CEikLabel::SetBrushStyleFromContext()
    {
    if (CheckAndCreateExtension()) // can't be sure creation above passed
        {
        iExtension->iFlags.Clear(CEikLabelExtension::EUseBrushStyle);
        }
    }

EXPORT_C void CEikLabel::ConstructFromResourceL(TResourceReader& aReader)
	{
	TInt flags=aReader.ReadInt16();
	SetUnderlining(flags&EikLabelUnderline);
	SetStrikethrough(flags&EikLabelStrikeThrough);
	iAlignment.SetHAlignment((TGulHAlignment)aReader.ReadInt16());
	iAlignment.SetVAlignment((TGulVAlignment)aReader.ReadInt16());
	TInt standardFont=aReader.ReadInt16();
	switch (standardFont)
		{
	case EEikLabelFontLegend:
		iFont=iEikonEnv->LegendFont();
		break;
	case EEikLabelFontAnnotation:
		iFont=iEikonEnv->AnnotationFont();
		break;
	default:
		break;
		}
	TInt length=aReader.ReadInt16();
	if (length)
		SetBufferReserveLengthL(length);
	SetTextL(aReader.ReadTPtrC());
	}
       
EXPORT_C void CEikLabel::SetTextL(const TDesC& aText)
	{
	__ASSERT_DEBUG(&aText,Panic(EEikPanicLabelNullText));
	
	if (iLabFlags&KTextBufferReserveLength || 
		(iText && iText->Des().MaxLength() >= aText.Length()))
		{
        *iText=aText; // will panic if not long enough
		}
    else
        {
        User::LeaveIfError( AknBidiTextUtils::PrepareRunInfoArray( aText ) );

        HBufC* newText=aText.AllocL();
	    delete(iText); // after the AllocL succeeds
	    iText=newText;

		if ( !iExtension )
			{
			iExtension = CEikLabelExtension::NewL();
			}
		iExtension->SetTextLengthL(iText->Length());
        }
    if (iExtension)
        iExtension->iFlags.Clear(CEikLabelExtension::ECropText); // reset ECropText
    iNumberOfLines=1;
    TPtrC ptr=(*iText);
    FOREVER
        {
        const TInt offset=ptr.Locate('\n');
        if (offset<0 || offset==ptr.Length()-1)
            break;
        iNumberOfLines++;
        ptr.Set(ptr.Mid(offset+1));
        }
	return ;
 	}

EXPORT_C void CEikLabel::CropText()
	{
	if ( iExtension ) // if no extension do nothing as SetTextL has not yet been called
		iExtension->iFlags.Set(CEikLabelExtension::ECropText);
	}
	
TInt CEikLabel::HeightInPixels() const
    {
    return(iMargin.iTop+iMargin.iBottom + iGapBetweenLines*(iNumberOfLines-1) + iNumberOfLines*iFont->FontMaxHeight());
    }

TInt CEikLabel::WidthInPixels(TPtrC& aText) const
    {
    TInt maxLineWidth=0;

    FOREVER
        {
        const TInt offset=aText.Locate('\n');
        TPtrC left=aText;
        if (offset>=0)
            left.Set(aText.Left(offset));

        CFont::TMeasureTextInput input;
        if ( !(iLabFlags & EUseLogicalToVisualConversion) )
            {
            input.iFlags = CFont::TMeasureTextInput::EFVisualOrder;
            }
        TInt thisWidth = iFont->MeasureText( left, &input );

        if (thisWidth>maxLineWidth)
            maxLineWidth=thisWidth;
        if (offset<0 || offset==aText.Length()-1)
            break;
        aText.Set(aText.Mid(offset+1));
        }
    return(iMargin.iLeft + iMargin.iRight + maxLineWidth);
    }

TInt CEikLabel::HeightInPixels(const TDesC& aText) const
    {
    // Calculate amount of lines in aText
    TInt numberOfLines = 1;
    TPtrC ptr=aText;
    FOREVER
        {
        const TInt offset=ptr.Locate('\n');
        if (offset<0 || offset==ptr.Length()-1)
            break;
        numberOfLines++;
        ptr.Set(ptr.Mid(offset+1));
        }

    // Calculate height.
    return(iMargin.iTop+iMargin.iBottom + iGapBetweenLines*(numberOfLines-1) + numberOfLines*iFont->HeightInPixels());
    }

EXPORT_C void CEikLabel::Draw( const TRect& /*aRect*/ ) const
    {
    CWindowGc& gc = SystemGc();
    DrawToContext( gc, NULL );
    gc.SetOpaque( EFalse ); // transparency back on
    }


void CEikLabel::DrawToContext( CBitmapContext& aContext, 
        const TRgb* aOverrideColor ) const
    {
    __ASSERT_ALWAYS(iText,Panic(EEikPanicLabelNullText));
    TInt textTooLong = KErrNone;
    if (iExtension->iLabelLine->Des().MaxLength() < iText->Length() + TBidiLogicalToVisual::KMinCharAvailable)
        {
        TRAP(textTooLong, iExtension->SetTextLengthL(iText->Length()));
        } // textTooLong condition is handled later in this function, tries to recover as much as possible

    TBool outlineEffectOn = EFalse;
    const CAknLayoutFont* layoutFont = CAknLayoutFont::AsCAknLayoutFontOrNull( iFont ); 
    if (layoutFont) 
        {
        outlineEffectOn = layoutFont->FontSpecification().IsOutlineEffectOn(); 
        }

    CBitmapContext& gc = aContext;
    if (IsStrikethrough())
        {
        gc.SetStrikethroughStyle(EStrikethroughOn);
        }
    else
        {
        gc.SetStrikethroughStyle(EStrikethroughOff);
        }

    if (IsUnderlined())
        {
        gc.SetUnderlineStyle(EUnderlineOn);
        }
    else
        {
        gc.SetUnderlineStyle(EUnderlineOff);
        }
    
    if ( aOverrideColor )
        {
        gc.SetPenColor(*aOverrideColor);
        gc.SetBrushColor(*aOverrideColor);
        }
    else if ((iLabFlags&KTextEmphasisMask) != ENoEmphasis)
        {
        SetupGcForEmphasis(gc);
        }
    else
        {
        TRgb penColor;
        TRgb brushColor;
        if (IsDimmed()) 
            {
            penColor = iEikonEnv->ControlColor(EColorLabelDimmedText,*this); 
            brushColor = iEikonEnv->ControlColor(EColorControlDimmedBackground,*this); 
            }
        else
            {
            penColor = iEikonEnv->ControlColor(EColorLabelText,*this); 
            brushColor = iEikonEnv->ControlColor(EColorControlBackground,*this); 
            }
        if(outlineEffectOn)
            {
            ConvertColorsForOutlineEffect(penColor, brushColor); 
            }
        gc.SetPenColor(penColor); 
        gc.SetBrushColor(brushColor); 
        }

    
    gc.UseFont(iFont);

    if (iExtension->iFlags.IsSet(CEikLabelExtension::EUseBrushStyle))
        {
        gc.SetBrushStyle(iExtension->iBrushStyle);
        }
    
    const TRect rect = Rect();

    const TInt deltaHeight=rect.Height() - HeightInPixels();
    TInt preHeight=iMargin.iTop; // used on first line
    TInt postHeight=iMargin.iBottom; // used on last line
    if (deltaHeight>0)
        {
        switch(iAlignment.VAlignment())
            {
            case EVTop:
                postHeight+=deltaHeight;
                break;
            case EVBottom:
                preHeight+=deltaHeight;
                break;
            case EVCenter:
                TInt postInc=deltaHeight/2; // give odd pixel to pre-Height
                preHeight+=deltaHeight-postInc;
                postHeight+=postInc;
            }
        }
    TInt lineNo=0;
    
    TPtrC ptr=(*iText);
    TRect box=rect;
    TBool clippedAtBottom=EFalse;
    TInt textPaneTopToBaseline(0);
    TInt textPaneHeight(0);
    
    if ( layoutFont )
        {
        textPaneTopToBaseline = layoutFont->TextPaneTopToBaseline();
        textPaneHeight = layoutFont->TextPaneHeight();
        }
    else
        {
        textPaneTopToBaseline = iFont->FontMaxAscent();
        textPaneHeight = iFont->FontMaxHeight();
        }
    
    // iGapBetweenLines is defined as baseline separation - CFont::HeightInPixels
    // Since we are using the better metrics above, we have to actually use something which is 
    // derived from iGapBetweenLines, but is e.g. reduced if textpaneheight > heightInPixels
    TInt gapBetweenTextPanes( iGapBetweenLines - (textPaneHeight - iFont->FontMaxHeight() ) );

    FOREVER
        {
        TInt ascent=textPaneTopToBaseline+preHeight;
        TInt lineHeight=textPaneHeight+preHeight;
        if (++lineNo==iNumberOfLines)
            {
            lineHeight+=postHeight;
            }
        
        TInt off=ptr.Locate('\n');
        TPtrC left=ptr;
        if (off>=0)
            {
            left.Set(ptr.Left(off));
            }
        
        box.iBr.iY=box.iTl.iY+lineHeight;
        if (box.iBr.iY>rect.iBr.iY)
            {
            box.iBr.iY=rect.iBr.iY;
            clippedAtBottom=ETrue;
            }
        
        CGraphicsContext::TTextAlign align=iAlignment.TextAlign();
        TInt margin = iMargin.iLeft;
        
        TBidiText::TDirectionality labelDirectionality = TBidiText::TextDirectionality(*iText);
        if (iExtension->iFlags.IsSet(CEikLabelExtension::EUseDynamicBidiTextAlignment))
            {
            switch(labelDirectionality)
                {
                case TBidiText::ELeftToRight:
                    align = CGraphicsContext::ELeft;
                    break;
                case TBidiText::ERightToLeft:
                    align = CGraphicsContext::ERight;
                    break;
                default:
                    break; // do nothing
                }
            }
        
        if (align==CGraphicsContext::ERight)
            {
            margin = iMargin.iRight;
            }
        else if (align==CGraphicsContext::ECenter)
            {
            margin -= iMargin.iRight;
            }
        
        TRect adjustedBox( box ) ;  // create a new box to accommodate underline if present (without affecting layout)
        if ( IsUnderlined() )
            {
            TAknTextDecorationMetrics decoration(iFont);
            TInt underlineWidth = decoration.UnderlineHeight();
            TInt underlineSpace = decoration.BaselineToUnderlineOffset();
            
            if (  ( textPaneTopToBaseline + underlineSpace ) > textPaneHeight )
                {
                adjustedBox.iBr.iY += (  textPaneTopToBaseline + underlineSpace - textPaneHeight ) ;
                }
            }
        
        TPtr text = iExtension->iLabelLine->Des();
        TInt textMaxLength = text.MaxLength();
        TBool dontDoComplicatedDraw = EFalse;
        
        TInt usableLength = 0;
        if (textTooLong == KErrNone || textMaxLength >= left.Length() + TBidiLogicalToVisual::KMinCharAvailable)
            {
            usableLength = left.Length(); // no problem with memory
            }
        else
            { // problem with lack of memory so find a usable length.
            if (labelDirectionality == TBidiText::ELeftToRight || !(iLabFlags & EUseLogicalToVisualConversion))
                { // most likely western text so just display logically (no truncation)
                dontDoComplicatedDraw = ETrue;
                }
            else
                { // most likely NOT western text so display as much visually as possible
                if (textMaxLength > TBidiLogicalToVisual::KMinCharAvailable)
                    {
                    usableLength = textMaxLength - TBidiLogicalToVisual::KMinCharAvailable;
                    }
                else // there is no buffer so give up and draw logically
                    {
                    dontDoComplicatedDraw = ETrue;
                    }
                }
            }
        
        if (dontDoComplicatedDraw)
            {
            gc.DrawText( left, adjustedBox, ascent, align, margin );
            if ( iExtension->iPictographInterface )
                {
                iExtension->iPictographInterface->Interface()->DrawPictographsInText(
                    gc, *iFont, left, adjustedBox, ascent, align, margin );
                }
            }
        else
            { // we have some buffer space to try and draw text correctly
            TPtrC usableText=left.Left(usableLength);
            TInt adjustedBoxWidth = adjustedBox.Size().iWidth;
            
            if (iLabFlags & EUseLogicalToVisualConversion)
                {
                if (iExtension->iFlags.IsSet(CEikLabelExtension::ECropText))
                    {
                    AknBidiTextUtils::ConvertToVisualAndClip(usableText, text, *iFont, adjustedBoxWidth, adjustedBoxWidth);
                    }
                else
                    {
                    const TUint KEmptyChar = TUint(0xFFFF);
                    AknBidiTextUtils::ConvertToVisualAndClip(usableText, text, *iFont, adjustedBoxWidth, adjustedBoxWidth, AknBidiTextUtils::EImplicit, KEmptyChar );
                    }
                }
            else
                {
                text = usableText; // copy
                }
            gc.DrawText( text, adjustedBox, ascent, align, margin );
            if ( iExtension->iPictographInterface )
                {
                iExtension->iPictographInterface->Interface()->DrawPictographsInText(
                    gc, *iFont, text, adjustedBox, ascent, align, margin );
                }
            }
        
        
        if (off<0 || off==ptr.Length()-1 || clippedAtBottom)
            break;
        
        ptr.Set(ptr.Mid(off+1));
        box.iTl.iY=box.iBr.iY;
        
        preHeight=gapBetweenTextPanes;
        }
    
    gc.DiscardFont();
    }

EXPORT_C void CEikLabel::SetBufferReserveLengthL(TInt aLength)
	{
	if (iText)
		iText=iText->ReAllocL(aLength);
	else
		iText=HBufC::NewL(aLength);
	iLabFlags|=KTextBufferReserveLength;
	iReserveLength=aLength;
	if ( !iExtension )
		{
		iExtension = CEikLabelExtension::NewL();
		}
	iExtension->SetTextLengthL(iText->Length());
	}

EXPORT_C void CEikLabel::SetFont(const CFont* aFont) 
	{
	iFont=aFont;
	}

EXPORT_C TSize CEikLabel::CalcMinimumSize(TPtrC& aText) const
    {
    // HeightInPixels does not modify aText.
    const TInt height = HeightInPixels(aText);

    // Note that WidthInPixels modifies aText and so
    // it must be called after HeightInPixels.
    const TInt width = WidthInPixels(aText);
    return TSize( width, height );
    }

EXPORT_C void CEikLabel::SetEmphasis(TTextEmphasis aEmphasis)
	{
    iLabFlags&=(~KTextEmphasisMask);
    iLabFlags|=aEmphasis;
	}

EXPORT_C TSize CEikLabel::MinimumSize()
	{
	__ASSERT_ALWAYS(iText,Panic(EEikPanicLabelNullText));
	TPtrC ptr=(*iText);
	iSize=TSize(WidthInPixels(ptr),HeightInPixels());
	return(iSize);
	}

EXPORT_C void CEikLabel::SetPixelGapBetweenLines(TInt aGap)
	{
	iGapBetweenLines=aGap;
	}

EXPORT_C TInt CEikLabel::PixelGapBetweenLines() const
	{
	return iGapBetweenLines;
	}

EXPORT_C void CEikLabel::SetUnderlining(TBool aUnderLining)
	{
	if (aUnderLining) {iLabFlags|=EUnderlining;}
        else iLabFlags&=(~EUnderlining);
	}

EXPORT_C void CEikLabel::SetStrikethrough(TBool aStrikethrough)
	{
	if (aStrikethrough) {iLabFlags|=EStrikethrough;}
        else iLabFlags&=(~EStrikethrough);
	}

EXPORT_C TInt CEikLabel::BufferReserveLength() const
	{
	return (iLabFlags&KTextBufferReserveLength? iReserveLength : 0);
	}

EXPORT_C void CEikLabel::UseLogicalToVisualConversion( TBool aUseConversion )
    {
    if ( aUseConversion )
        {
        iLabFlags |= EUseLogicalToVisualConversion;
        }
    else
        {
        iLabFlags &= (~EUseLogicalToVisualConversion);
        }
    }

EXPORT_C TBool CEikLabel::LogicalToVisualConversionUsed() const
    {
    return iLabFlags & EUseLogicalToVisualConversion;
    }

EXPORT_C void CEikLabel::EnablePictographsL( CAknPictographInterface& aInterface )
    {
    if ( !iExtension )
        {
        iExtension = CEikLabelExtension::NewL();
        }

    iExtension->iPictographInterface = &aInterface;
    }

EXPORT_C void CEikLabel::DisablePictographs()
    {
    if ( iExtension )
        {
        iExtension->iPictographInterface = NULL;
        }
    }

void CEikLabel::SetupGcForEmphasis(CGraphicsContext& aGc) const
	{
    TRgb penColor;
    TRgb brushColor; 

    penColor = (iEikonEnv->ControlColor(EColorLabelTextEmphasis,*this)); 
    if ((iLabFlags&KTextEmphasisMask) == EFullEmphasis) 
        {
        brushColor = (iEikonEnv->ControlColor(EColorLabelHighlightFullEmphasis,*this)); 
        }
    else
        {
        brushColor = (iEikonEnv->ControlColor(EColorLabelHighlightPartialEmphasis,*this)); 
        }
    
    aGc.SetPenColor(penColor); 
    aGc.SetBrushColor(brushColor);
    aGc.SetBrushStyle(CGraphicsContext::ESolidBrush); 
    }

TBool CEikLabel::CheckAndCreateExtension()
    {
	if (!iExtension)
		{
		TRAP_IGNORE(iExtension = CEikLabelExtension::NewL());
		}
    return (iExtension != NULL);
    }

/**
 * Writes the internal state of the control and its components to aStream.
 * Does nothing in release mode.
 * Designed to be overidden and base called by subclasses.
 *
 * @internal
 * @since App-Framework_6.1
 */
#ifndef _DEBUG
EXPORT_C void CEikLabel::WriteInternalStateL(RWriteStream&) const
	{}
#else
EXPORT_C void CEikLabel::WriteInternalStateL(RWriteStream& aWriteStream) const
	{
	_LIT(KLitEikLblCtlStart, "<CEiklabel>");
	_LIT(KLitEikLblCtlEnd, "<\\CEikLabel>");

	_LIT(KLitEikLblTxt,"<iText>");
	_LIT(KLitEikLblTxtEnd,"<\\iText>");
	_LIT(KLitEikLblFnt,"<iFont>");
	_LIT(KLitEikLblFntEnd,"<\\iFont>");
	_LIT(KLitEikLblNoLns,"<iNumberOfLines>");
	_LIT(KLitEikLblNoLnsEnd,"<\\iNumberOfLines>");
	_LIT(KLitEikLblFlgs,"<iLabFlags>");
	_LIT(KLitEikLblFlgsEnd,"<\\iLabFlags>");
	_LIT(KLitEikLblGpBtwnLns,"<iGapBetweenLines>");
	_LIT(KLitEikLblGpBtwnLnsEnd,"<\\iGapBetweenLines>");
	_LIT(KLitEikLblResLen,"<iReserveLength>");
	_LIT(KLitEikLblResLenEnd,"<\\iReserveLength>");

	aWriteStream << KLitEikLblCtlStart;
	aWriteStream << KLitEikLblTxt;
	if(iText)
		aWriteStream.WriteL(*iText);
	aWriteStream << KLitEikLblTxtEnd;
	aWriteStream << KLitEikLblFnt;
	const TFontSpec& spec=iFont->FontSpecInTwips();
	aWriteStream << spec;
	aWriteStream << KLitEikLblFntEnd;
	aWriteStream << KLitEikLblNoLns;
	aWriteStream.WriteInt32L(iNumberOfLines);
	aWriteStream << KLitEikLblNoLnsEnd;
	aWriteStream << KLitEikLblFlgs;
	aWriteStream.WriteInt32L(iLabFlags);
	aWriteStream << KLitEikLblFlgsEnd;
	aWriteStream << KLitEikLblGpBtwnLns;
	aWriteStream.WriteInt32L(iGapBetweenLines);
	aWriteStream << KLitEikLblGpBtwnLnsEnd;
	aWriteStream << KLitEikLblResLen;
	aWriteStream.WriteInt32L(iReserveLength);
	aWriteStream << KLitEikLblResLenEnd;
	CEikAlignedControl::WriteInternalStateL(aWriteStream);
	aWriteStream << KLitEikLblCtlEnd;
	}
#endif

EXPORT_C void CEikLabel::HandlePointerEventL(const TPointerEvent& aPointerEvent) 
    { 
    CEikAlignedControl::HandlePointerEventL(aPointerEvent); 
    }

EXPORT_C void* CEikLabel::ExtensionInterface( TUid /*aInterface*/ )
    {
    return NULL;
    }

EXPORT_C void CEikLabel::Reserved_2()
	{}

// @todo use some utility 
#define max(a, b) a > b ? a : b; 
#define min(a, b) a < b ? a : b; 
#define abs(a) a < 0 ? -a : a; 

TInt CalculateLuminance(const TRgb& aColor) 
    { 
    TInt R = aColor.Red(); 
    TInt G = aColor.Green(); 
    TInt B = aColor.Blue(); 
    // Should be scaled to 0-255? 
    TInt Y = (((66 * R) + (129 * G) + (25 * B) + 128) >> 8) + 16; 
    return Y; 
    } 
    
void CEikLabel::ConvertColorsForOutlineEffect(TRgb& aPenColor, TRgb& aBrushColor) const 
    { 
    // the caller intends the brush color to be the background or outline, 
    // and the pen color to be the foreground color 
    TRgb innerColor = aPenColor; 
    TRgb outerColor = aBrushColor; 

    // @todo maybe there is already some conversion utility in multimedia image library? 
    TInt innerLum = CalculateLuminance(innerColor); 
    TInt outerLum = CalculateLuminance(outerColor); 
    
    // if the defined colours are the same, then change the outline color so that 
    // it's either black or white, contrasting according to the font label text color's brightness 
    TInt difference = outerLum - innerLum; 
    difference = abs(difference); 
    TInt half = (0xff / 2); // @todo figure out hlsMax somehow 
    TInt threshold = (0xff / 3); // @todo figure out hlsMax somehow, and decide on the threshold! 
    if(difference < threshold) 
        { 
        // if inner luminance is low, the outer luminance should be high, i.e. white 
        outerColor = (innerLum < half) ? KRgbWhite : KRgbBlack; 
        } 
    
    // for outline effect, we must set the color we want as the outline to the pen color, and 
    // the color we want as the fill to the brush color, so swap them round 
    aPenColor = outerColor; 
    aBrushColor = innerColor; 
    } 


