// EIKCAPC.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <eikcapc.h>
#include <coemain.h>
#include <barsread.h>
#include <eikenv.h>
#include <eikdialg.hrh>
#include <eikbctrl.h>
#include <eiksfont.h>
#include <eikcolor.h>
    
enum
    {
    EUsesEars       =0x01,
    ELeftEarDown    =0x02,
    ERightEarDown   =0x04,
    ELeftEarGrab    =0x08,
    ERightEarGrab   =0x10,
    ECurrent        =0x20,
	ESeparatorAfter =0x40,
	EExtraAscent	=0x80,
	ELatent			=0x100,
	ELglf			=0x200,
	ETakesEnterKey	=0x400,
	EOfferAllHotKeys=0x800,
	ETrailerAfterEar=0x1000
    };

const TInt KDynamicEarMask=(ELeftEarDown|ERightEarDown|ELeftEarGrab|ERightEarGrab);

const TInt KCapCHorzEdgeSpacing=8;
const TInt KCapCVertEdgeSpacing=0;//3;
const TInt KCapCCenterSpacing=15;
const TInt KControlTrailerSpacing=2;
const TInt KCapCEarHeightTopHalf=6;
const TInt KCapCEarHeightBottomHalf=6;
const TInt KCapCEarHeight=KCapCEarHeightTopHalf+KCapCEarHeightBottomHalf;
const TInt KCapCEarWidth=12;
const TInt KCapCEarSpacing=5;
const TInt KCapCVertCaptionOffset=2;
const TInt KCapCExtraAscent=KCapCVertCaptionOffset+1;
const TInt KTrailCVertCaptionOffset=4;
const TInt KCapCSeparatorAfterSpace=4;

const TInt KCapCInitialEarRepeat=600000;	// 6 tenths of a second
const TInt KCapCEarRepeat=100000; // one tenth of a second

EXPORT_C CEikCaptionedControl::CEikCaptionedControl()
    {
	__DECLARE_NAME(_S("CEikCaptionedControl"));
    }

EXPORT_C CEikCaptionedControl::~CEikCaptionedControl()
    {
    delete(iControl);
    delete(iCaption);
	delete(iTrailer);
    }

EXPORT_C void CEikCaptionedControl::SetUsesEars()
    {
    iCapCFlags|=EUsesEars;
    }

EXPORT_C void CEikCaptionedControl::SetExtraAscent()
    {
    iCapCFlags|=EExtraAscent;
    }

EXPORT_C TSize CEikCaptionedControl::MinimumSize()
    {
	if (iMinSize.iWidth)
		return(iMinSize);
	TSize size=iControl->MinimumSize();
    if (iCaption)
        {
		if (iCapCFlags&EExtraAscent)
			size.iHeight+=KCapCExtraAscent;
        TSize capSize=iCaption->MinimumSize();
		capSize.iHeight+=KCapCVertCaptionOffset;
        if (capSize.iHeight>size.iHeight)
            size.iHeight=capSize.iHeight;
        iCaptionWidth=capSize.iWidth+KCapCCenterSpacing;
        }
	if (iTrailer)
		{
		TSize trailSize=iTrailer->MinimumSize();
		trailSize.iHeight+=KTrailCVertCaptionOffset;
        if (trailSize.iHeight>size.iHeight)
            size.iHeight=trailSize.iHeight;
		size.iWidth+=trailSize.iWidth+KControlTrailerSpacing;
		}
    if (iCapCFlags&EUsesEars)
        {
		size.iWidth+=KCapCEarWidth+KCapCEarSpacing;
		iCaptionWidth+=KCapCEarWidth+KCapCEarSpacing;
		}
    size.iHeight+=2*KCapCVertEdgeSpacing;
	size.iWidth+=iCaptionWidth+2*KCapCHorzEdgeSpacing;
	if (iCapCFlags&ESeparatorAfter)
		size.iHeight+=KCapCSeparatorAfterSpace;
	iMinSize=size;
    return(size);
	}

EXPORT_C void CEikCaptionedControl::SizeChangedL()
    {
	TRect rect=Rect();

	if(rect.Width()<MinimumSize().iWidth)
		SquashComponentsL();
	else
		StretchComponentsL();
    }

void CEikCaptionedControl::StretchComponentsL()
	{
	TRect rect=Rect();
	rect.Shrink(KCapCHorzEdgeSpacing,KCapCVertEdgeSpacing);
	if (iCapCFlags&ESeparatorAfter)
		rect.iBr.iY-=KCapCSeparatorAfterSpace;
	if (iCaption)
        {
		TPoint capPos=rect.iTl;
		capPos.iY+=KCapCVertCaptionOffset;
		iCaption->SetExtentL(capPos,iCaption->Size());
		if (iCapCFlags&EExtraAscent)
			rect.iTl.iY+=KCapCExtraAscent;
		}
    rect.iTl.iX+=iCaptionWidth;
	if ((iCapCFlags&EUsesEars) && !(iCapCFlags&ETrailerAfterEar))
		rect.iBr.iX-=(KCapCEarWidth+KCapCEarSpacing);
	if (iTrailer)
		{
		TSize trailSize=iTrailer->MinimumSize();
		TInt trailPosX=rect.iBr.iX-trailSize.iWidth;
		TInt trailPosY=rect.iTl.iY+KTrailCVertCaptionOffset;
		iTrailer->SetExtentL(TPoint(trailPosX,trailPosY),trailSize);
		rect.iBr.iX=trailPosX-KControlTrailerSpacing;
		}
	if ((iCapCFlags&EUsesEars) && (iCapCFlags&ETrailerAfterEar))
		rect.iBr.iX-=(KCapCEarWidth+KCapCEarSpacing);
	iControl->SetRectL(rect);
	}

TInt CEikCaptionedControl::SquashComponentWidth(TInt& aTotalWidth,const TInt aComponentWidthHint)
	{
	if (aTotalWidth<=0)
		return 0;
	
	TInt componentWidth=aComponentWidthHint;
	if (componentWidth<aTotalWidth)
		aTotalWidth-=componentWidth;
	else
		{
		componentWidth=aTotalWidth;
		aTotalWidth=0;
		}

	return componentWidth;
	}

void CEikCaptionedControl::SquashComponentsL()
	{
	TRect rect=Rect();

	// Adjust widths
	TInt width=rect.Width();
	TInt leftEarWidth=0;
	if (iCapCFlags&EUsesEars)
		leftEarWidth=SquashComponentWidth(width,KCapCEarSpacing+KCapCEarWidth+KCapCCenterSpacing);
	TInt controlWidth=SquashComponentWidth(width,iControl->MinimumSize().iWidth);
	TInt captionWidth=0;
	if (iCaption)
		captionWidth=SquashComponentWidth(width,iCaption->MinimumSize().iWidth);
	TInt trailerWidth=0;
	if (iTrailer)
		trailerWidth=SquashComponentWidth(width,iTrailer->MinimumSize().iWidth);
	
	// Layout components 
	rect.Shrink(KCapCHorzEdgeSpacing,KCapCVertEdgeSpacing);
	if (iCapCFlags&ESeparatorAfter)
		rect.iBr.iY-=KCapCSeparatorAfterSpace;

	if (iCaption)
        {
		TPoint capPos=rect.iTl;
		capPos.iY+=KCapCVertCaptionOffset;
		TSize capSize=iCaption->Size();
		capSize.iWidth=captionWidth;
		iCaption->SetExtentL(capPos,capSize);
		if (iCapCFlags&EExtraAscent)
			rect.iTl.iY+=KCapCExtraAscent;
		}
	rect.iTl.iX+=captionWidth;
	rect.iTl.iX+=leftEarWidth;
	if ((iCapCFlags&EUsesEars) && !(iCapCFlags&ETrailerAfterEar))
		rect.iBr.iX-=(KCapCEarSpacing+KCapCEarWidth);
	if (iTrailer)
		{
		TSize trailSize=iTrailer->MinimumSize();
		trailSize.iWidth=trailerWidth;
		TInt trailPosX=rect.iBr.iX-trailSize.iWidth;
		TInt trailPosY=rect.iTl.iY+KTrailCVertCaptionOffset;
		iTrailer->SetExtentL(TPoint(trailPosX,trailPosY),trailSize);
		rect.iBr.iX=trailPosX-KControlTrailerSpacing;
		}
	if ((iCapCFlags&EUsesEars) && (iCapCFlags&ETrailerAfterEar))
		rect.iBr.iX-=(KCapCEarWidth+KCapCEarSpacing);
	iControl->SetRectL(rect);
	}

EXPORT_C void CEikCaptionedControl::FocusChanged(TDrawNow aDrawNow)
    {
    TBool focused=IsFocused();
    iControl->SetFocus(focused,aDrawNow);
    if (iCaption)
        {
        CEikLabel::TTextEmphasis emphasis=CEikLabel::EFullEmphasis;
        if (!focused)
            emphasis=(iCapCFlags&ECurrent? CEikLabel::EPartialEmphasis: CEikLabel::ENoEmphasis);
        iCaption->SetEmphasis(emphasis);
        iCaption->DrawNow();
        }
    if (aDrawNow && iCapCFlags&EUsesEars && IsReadyToDraw())
        DrawEarsNow(EBothEars);
    }

void CEikCaptionedControl::DrawEarsNow(TWhichEars aEar) const
    {
    ActivateGc();
    DrawEars(aEar);
    DeactivateGc();
    }

void CEikCaptionedControl::DrawEars(TWhichEars aEar) const
    {
    if (aEar&ELeftEar)
        DrawSingleEar(ELeftEar,iCapCFlags&ELeftEarDown);
    if (aEar&ERightEar)
        DrawSingleEar(ERightEar,iCapCFlags&ERightEarDown);
    }

void CEikCaptionedControl::DrawSingleEar(TWhichEars aEar,TBool aPressed) const
    {
    TRect rect=EarRect(aEar);
    CGraphicsContext& gc=SystemGc();
    ResetGc(); // !! overkill - but need some way to set up the background
    if (!IsFocused())
		{
	    gc.SetPenStyle(CGraphicsContext::ENullPen);
	    gc.DrawRect(rect);
        return;
		}
	const CFont* font=iEikonEnv->SymbolFont();
	gc.UseFont(font);
	gc.SetPenStyle(CGraphicsContext::ESolidPen);
	gc.SetPenColor(iEikonEnv->ControlColor(EEikColorWindowText, *this)); // KEikEarColor
    gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	const TInt offset=Max(font->AscentInPixels(),
						(rect.Height() - font->HeightInPixels())/2 + font->AscentInPixels());
	const TInt charCode=(aEar==ELeftEar? (aPressed? ESymFontDownLeftEar : ESymFontUpLeftEar) :
										(aPressed? ESymFontDownRightEar : ESymFontUpRightEar));
	const CGraphicsContext::TTextAlign align=(aEar==ELeftEar? CGraphicsContext::ELeft : CGraphicsContext::ERight);
	TBuf<1> buf;
	buf.Append(TChar(charCode));
	gc.DrawText(buf,rect,offset,align);
	gc.UseFont(iEikonEnv->NormalFont());
    }

TRect CEikCaptionedControl::EarRect(TWhichEars aEar) const
    {
    TPoint tl=iControl->Position();
	TSize size=iControl->Size();
    if (aEar==ELeftEar)
        tl.iX-=KCapCEarWidth+KCapCEarSpacing;
    else
        {
		tl.iX+=size.iWidth+KCapCEarSpacing;
		if (iTrailer && !(iCapCFlags&ETrailerAfterEar))
			tl.iX+=iTrailer->Size().iWidth;
		}
	size.iWidth=KCapCEarWidth;
    return(TRect(tl,size));
    }

EXPORT_C TInt CEikCaptionedControl::CountComponentControls() const
    {
	TInt count=1;
	if (iCaption)
		++count;
	if (iTrailer)
		++count;
	return count;
    }

EXPORT_C CCoeControl* CEikCaptionedControl::ComponentControl(TInt aIndex) const
    {
	switch (aIndex)
		{
	case 0:
		return (iCaption? iCaption : iControl);
	case 1:
		return (iCaption? iControl : iTrailer);
	case 2:
		return iTrailer;
	default:
		return NULL;
		}
    }

EXPORT_C void CEikCaptionedControl::Draw(const TRect& /*aRect*/) const
    {
    CGraphicsContext& gc=SystemGc();
    gc.SetPenStyle(CGraphicsContext::ENullPen);
	TRect rect=Rect();
    gc.DrawRect(rect);
    if (iCapCFlags&EUsesEars)
        DrawEars(EBothEars);
	if (iCapCFlags&ESeparatorAfter)
		{
		TPoint separatorStartPt(rect.iTl.iX+KCapCHorzEdgeSpacing/2,rect.iBr.iY-1);
		TPoint separatorEndPt(separatorStartPt.iX+(iFullWidth-KCapCHorzEdgeSpacing), separatorStartPt.iY);
		gc.SetPenStyle(CGraphicsContext::ESolidPen);
		gc.SetPenColor(iEikonEnv->ControlColor(EEikColorWindowText, *this));
		gc.DrawLine(separatorStartPt, separatorEndPt);
		}
    }

EXPORT_C TKeyResponse CEikCaptionedControl::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
	{
	return iControl->OfferKeyEventL(aKeyEvent,aType);
	}

EXPORT_C void CEikCaptionedControl::HandlePointerEventL(const TPointerEvent& aPointerEvent)
    {
	if (!IsNonFocusing())
		{
		TWhichEars ear=ENoEar;
		if (aPointerEvent.iType!=TPointerEvent::EButton1Down)
			{
			if (iCapCFlags&ELeftEarGrab)
				ear=ELeftEar;
			else if (iCapCFlags&ERightEarGrab)
				ear=ERightEar;
			if (ear)
				{
				TInt oldDrawFlags=iCapCFlags&(ELeftEarDown|ERightEarDown);
				if (aPointerEvent.iType==TPointerEvent::EButton1Up)
					iCapCFlags&=(~KDynamicEarMask);
				else
					{
					iCapCFlags&=(~(ELeftEarDown|ERightEarDown));
					if (EarRect(ear).Contains(aPointerEvent.iPosition))
						{
						FireEarL(ear, KCapCEarRepeat);
						return;
						}
					}
				if (oldDrawFlags!=(iCapCFlags&(ELeftEarDown|ERightEarDown)))
					DrawEarsNow(ear);
				return;
				}
			}
		else if (iCapCFlags&EUsesEars)
			{
			iCapCFlags&=(~KDynamicEarMask);
			if (EarRect(ELeftEar).Contains(aPointerEvent.iPosition))
				ear=ELeftEar;
			else if (EarRect(ERightEar).Contains(aPointerEvent.iPosition))
				ear=ERightEar;
			if (ear)
				{
				FireEarL(ear, KCapCInitialEarRepeat);
				return;
				}
			}
		}
	CCoeControl::HandlePointerEventL(aPointerEvent);
    }

void CEikCaptionedControl::FireEarL(TWhichEars aEar, TInt aEarRepeat)
    {
	Window().RequestPointerRepeatEvent(aEarRepeat, EarRect(aEar));
    TKeyEvent key;
    key.iModifiers=0;
    if (aEar==ELeftEar)
        {
        key.iCode=EKeyLeftArrow;
        iCapCFlags|=ELeftEarDown|ELeftEarGrab;
        }
    else
        {
        key.iCode=EKeyRightArrow;
        iCapCFlags|=ERightEarDown|ERightEarGrab;
        }
    DrawEarsNow(aEar);
    iControl->OfferKeyEventL(key,EEventKey);
    }

EXPORT_C void CEikCaptionedControl::SetCaptionL(const TDesC& aText)
    {
    if (!iCaption)
        {
		if (!aText.Length())
			return;
        iCaption=new(ELeave) CEikLabel;
	    iCaption->SetContainerWindowL(*this);
		iCaption->SetAllMarginsTo(1);
        iCaption->SetNonFocusing();
        }
    iCaption->SetTextL(aText);
    }

EXPORT_C void CEikCaptionedControl::SetTrailerL(const TDesC& aText)
	{
	if (!aText.Length())
		return;
    if (!iTrailer)
        {
		iTrailer=new(ELeave) CEikLabel;
		iTrailer->SetFont(iEikonEnv->LegendFont());
		iTrailer->SetContainerWindowL(*this);
		iTrailer->CopyControlContextFrom(this);
		iTrailer->SetAllMarginsTo(1);
		iTrailer->SetNonFocusing();
        }
	iTrailer->SetTextL(aText);
	}

EXPORT_C void CEikCaptionedControl::SetCurrent(TBool aSelected)
    {
    if (aSelected)
        iCapCFlags|=ECurrent;
    else
        iCapCFlags&=(~ECurrent);
    SetFocus(aSelected,EDrawNow);
    }

EXPORT_C void CEikCaptionedControl::ConstructFromResourceL(TResourceReader& aReader)
    {
    SetCaptionL(aReader.ReadTPtrC());
    iId=aReader.ReadInt16();
	TInt itemFlags=aReader.ReadInt16();
	TInt divider=itemFlags&EEikDlgItemSeparatorMask;
	if (divider==EEikDlgItemSeparatorAfter)
		{
		iCapCFlags|=ESeparatorAfter;
		SetCanDrawOutsideRect();
		}
    iControl->CopyControlContextFrom(this);
	iControl->ConstructFromResourceL(aReader);
	if (itemFlags&EEikDlgItemNoBorder)
		{
		((CEikBorderedControl*)iControl)->SetBorder(TEikBorder::ENone);
		SetExtraAscent();
		}
	if (itemFlags&EEikDlgItemNonFocusing)
		{
		SetNonFocusing();
		iControl->SetNonFocusing();
		iCapCFlags&=(~EUsesEars);
		}
	SetTrailerL(aReader.ReadTPtrC());
	if (itemFlags&EEikDlgItemLatent)
		SetLatent(ETrue);
	if (itemFlags&EEikDlgItemLglf)
		iCapCFlags|=ELglf;
	if (itemFlags&EEikDlgItemTakesEnterKey)
		iCapCFlags|=ETakesEnterKey;
	if (itemFlags&EEikDlgItemOfferAllHotKeys)
		iCapCFlags|=EOfferAllHotKeys;
	if (itemFlags&EEikDlgItemTrailerAfterEar)
		iCapCFlags|=ETrailerAfterEar;
    }

EXPORT_C void CEikCaptionedControl::CheckDimmedDisplayState()
	{
	TBool lineDimmed=(iControl->IsDimmed() || !iControl->IsVisible());
	SetDimmed(lineDimmed);
	if (iCaption)
		{
		iCaption->SetDimmed(lineDimmed);
		iCaption->DrawNow();
		}
	if (iTrailer)
		{
		TBool controlVisible=iControl->IsVisible();
		if (iTrailer->IsVisible()!=controlVisible)
			iTrailer->MakeVisible(controlVisible);
		iTrailer->SetDimmed(lineDimmed);
		iTrailer->DrawNow();
		}
	}

EXPORT_C void CEikCaptionedControl::ResetMinimumSizes()
	{
	iMinSize.iWidth=0;
	iCaptionWidth=0;
	iFullWidth=0;
	}

EXPORT_C TBool CEikCaptionedControl::IsLatent() const
	{
	return(iCapCFlags&ELatent);
	}

EXPORT_C void CEikCaptionedControl::SetLatent(TBool aLatent)
	{
	TBool visible=ETrue;
	iCapCFlags&=(~ELatent);
	if (aLatent)
		{
		iCapCFlags|=ELatent;
		visible=EFalse;
		}
	iControl->MakeVisible(visible);
	if (iCaption)
		iCaption->MakeVisible(visible);
	if (iTrailer)
		iTrailer->MakeVisible(visible);
	MakeVisible(visible);
	}

EXPORT_C TBool CEikCaptionedControl::LatentGroupLineFollows() const
	{
	return(iCapCFlags&ELglf);
	}

EXPORT_C void CEikCaptionedControl::SetLatentGroupLineFollows(TBool aLglf)
	{
	if (aLglf)
		iCapCFlags|=ELglf;
	else
		iCapCFlags&=(~ELglf);
	}

EXPORT_C TBool CEikCaptionedControl::DividerAfter() const
	{
	return(iCapCFlags&ESeparatorAfter);
	}

EXPORT_C void CEikCaptionedControl::SetDividerAfter(TBool aDividerAfter)
	{
	if (aDividerAfter)
		iCapCFlags|=ESeparatorAfter;
	else
		iCapCFlags&=(~ESeparatorAfter);
	}

EXPORT_C TBool CEikCaptionedControl::TakesEnterKey() const
	{
	return(iCapCFlags&ETakesEnterKey);
	}

EXPORT_C void CEikCaptionedControl::SetTakesEnterKey(TBool aTakesEnter)
	{
	if (aTakesEnter)
		iCapCFlags|=ETakesEnterKey;
	else
		iCapCFlags&=(~ETakesEnterKey);
	}

EXPORT_C TBool CEikCaptionedControl::OfferHotKeys() const
	{
	return iCapCFlags&EOfferAllHotKeys;
	}

EXPORT_C void CEikCaptionedControl::SetOfferHotKeys(TBool aOffer)
	{
	if (aOffer)
		iCapCFlags|=EOfferAllHotKeys;
	else
		iCapCFlags&=~EOfferAllHotKeys;
	}

EXPORT_C void CEikCaptionedControl::Reserved_1()
	{}
EXPORT_C void CEikCaptionedControl::Reserved_2()
	{}
