// EIKCHLST.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <e32base.h>
#include <e32keys.h>
#include <bamatch.h>
#include <barsread.h>
#include <badesca.h>
#include <eikchlst.h>
#include <eikchlst.hrh>
#include <eikenv.h>
#include <eiktxlbx.h>
#include <eiktxlbm.h>
#include <eiklbi.h>
#include <eikpanic.h>
#include <eikbordr.h>
#include <eikcmbut.h>						 
#include <eikcolor.h>
#include <eikscrlb.h>

const TInt KDefaultTextWidthInPixels = 12;

//
// class CEikChoiceListBase::CChoiceListExtension
//

class CEikChoiceListBase::CChoiceListExtension : public CBase
	{
public:
	inline void SetCursorPos(TInt aPos);
	inline void SetFont(const CFont* aFont);
	inline TInt CursorPos() const;
	inline const CFont* Font() const;
private:
	TInt iCursorPos;
	const CFont* iFont;
	};

inline void CEikChoiceListBase::CChoiceListExtension::SetCursorPos(TInt aPos)
	{ iCursorPos=aPos; }
inline void CEikChoiceListBase::CChoiceListExtension::SetFont(const CFont* aFont)
	{ iFont=aFont; }
inline TInt CEikChoiceListBase::CChoiceListExtension::CursorPos() const
	{ return iCursorPos; }
inline const CFont* CEikChoiceListBase::CChoiceListExtension::Font() const
	{ return iFont; }

//
//Class CEikChoiceListBase
//

EXPORT_C CDesCArray* CEikChoiceListBase::DesCArray() const
    {
    return((CDesCArray*)iArray);
    }

EXPORT_C void CEikChoiceListBase::DestroyArray()
    {
    if (!(iChoiceListFlags & EArrayOwnedExternally))
    	delete(iArray);
    }

EXPORT_C CEikChoiceListBase::CEikChoiceListBase()
    : CEikBorderedControl(TEikBorder(TEikBorder::ESingleGray))
    {
	__DECLARE_NAME(_S("CEikChoiceListBase"));
    }

EXPORT_C CEikChoiceListBase::~CEikChoiceListBase()
	{
	delete(iMatchBuf);
	DestroyArray();
	delete iChLstExtension;
	}

EXPORT_C void CEikChoiceListBase::SetIncrementalMatching(TBool aIncrementalMatching)
	{
    if (aIncrementalMatching)
        iChoiceListFlags |= EIncrementalMatching;
    else
        iChoiceListFlags &= (~EIncrementalMatching);
	}

EXPORT_C void CEikChoiceListBase::SetArrayExternalOwnership(TBool aArrayOwnedExternally)
	{
    if (aArrayOwnedExternally)
        iChoiceListFlags |= EArrayOwnedExternally;
    else
        iChoiceListFlags &= (~EArrayOwnedExternally);
	}

EXPORT_C TInt CEikChoiceListBase::CalculateTextWidth() const
    {
    const CFont* font=Font();
	if (iMaxDisplayChar)
    	return(iMaxDisplayChar*font->MaxNormalCharWidthInPixels());
	TInt maxTextWidth=0;
    if (iArray)
        {
	    TInt i=iArray->MdcaCount();
	    while (i--)
		    {
		    TPtrC tempBuf=iArray->MdcaPoint(i);
		    TInt thisWidth=font->TextWidthInPixels(tempBuf);
		    if (thisWidth>maxTextWidth)
			    maxTextWidth=thisWidth;
		    }
        }
	else
		maxTextWidth=KDefaultTextWidthInPixels; 
    return(maxTextWidth);
    }

EXPORT_C TInt CEikChoiceListBase::CurrentItem() const
    {
    return(iCurrentItem);
    }

EXPORT_C void CEikChoiceListBase::SetCurrentItem(TInt aItem)
    {
    iCurrentItem=aItem;
	ResetMatchBuf();
    }

EXPORT_C void CEikChoiceListBase::SetArrayL(TInt aResourceId)
    {
    CDesCArray* array=iCoeEnv->ReadDesCArrayResourceL(aResourceId);
    SetArrayL(array);
    }

EXPORT_C void CEikChoiceListBase::SetArrayL(MDesCArray *aArray)
	{
    DestroyArray();
    iArray=aArray;
	ResetMatchBuf();
	iCurrentItem=0;
	if ((iChoiceListFlags & EIncrementalMatching) && !iMatchBuf && iArray)
	    iMatchBuf=new(ELeave)RIncrMatcherBuf<KEikMaxMatchingBufLength>;
	}

EXPORT_C void CEikChoiceListBase::AllowPopout(TBool aAllow)
	{
	if (aAllow)
		iChoiceListFlags&=~ENoPopout;
	else
		iChoiceListFlags|=ENoPopout;
	}
	
EXPORT_C void CEikChoiceListBase::SetFontL(const CFont* aFont) // not available before Platform release 004
	{
	if (!iChLstExtension)
		iChLstExtension=new(ELeave) CChoiceListExtension;
	iChLstExtension->SetFont(aFont);
	}

EXPORT_C void CEikChoiceListBase::SizeChangedL()
    {
	const TRect rect=Rect();
	const TRect innerRect = iBorder.InnerRect(rect);
	const TSize innerSize(innerRect.Size());
    const CFont* font=Font();
	iAscent=(innerSize.iHeight-font->HeightInPixels())/2+font->AscentInPixels();
	if (iBorder.SizeDelta().iHeight)
		iAscent++;
    }

EXPORT_C void CEikChoiceListBase::SetAdjacent(TInt aAdjacent)
	{
	iBorder.SetAdjacent(aAdjacent);
	}

EXPORT_C void CEikChoiceListBase::Draw(const TRect& /*aRect*/) const
    {
	iBorder.Draw(SystemGc(),Rect());
	DrawContent();
	}

EXPORT_C void CEikChoiceListBase::ActivateL()
    {
    CCoeControl::ActivateL();
    if (iMatchBuf)
        DisplayCursor();
    }

EXPORT_C void CEikChoiceListBase::FocusChanged(TDrawNow aDrawNow)
	{
	if (aDrawNow && iMatchBuf)
		{
	    if (IsFocused())
	        DisplayCursor();
	    else
	        iEikonEnv->HideCursor(this);
		}
	}

EXPORT_C void CEikChoiceListBase::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{
	if (IsFocused() && aPointerEvent.iType==TPointerEvent::EButton1Down && !(iChoiceListFlags&ENoPopout))
        CreatePopoutL();
	}

EXPORT_C TKeyResponse CEikChoiceListBase::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode /*aEventCode*/)
    {
	const TInt code=aKeyEvent.iCode;
	if ((code == EKeyDownArrow) || (code == EKeyUpArrow))
		return(EKeyWasNotConsumed);
	if ((iArray) && (iArray->MdcaCount() > 0))
		{
		CheckCreateExtensions();
		TInt oldCurrentItem=iCurrentItem;
//		TInt oldCursorPos=iCursorPos;
		TInt oldCursorPos=CalcCursorPos();
	    TInt maxItem=iArray->MdcaCount()-1;
	    switch (code)
	        {
	    case EKeyEnd:
	        iCurrentItem=maxItem;
			ResetMatchBuf();
	        break;
	    case EKeyRightArrow:
	        iCurrentItem++;
			ResetMatchBuf();
	        break;
	    case EKeyHome:
	        iCurrentItem=0;
			ResetMatchBuf();
	        break;
	    case EKeyLeftArrow:
			if (iCurrentItem == -1)
				iCurrentItem = 0;
	        iCurrentItem--;
			ResetMatchBuf();
	        break;
	    case EKeyTab:
		case EKeyDownArrow:
			if (!(iChoiceListFlags&ENoPopout))
        		CreatePopoutL();
	        break;
	    case EKeyBackspace:
//		    if (iMatchBuf && iCursorPos)
		    if (iMatchBuf && oldCursorPos)
			    UndoLastChar();
		default:
		    if (TChar(aKeyEvent.iCode).IsPrint())
			    {
				MatchTypedChar(aKeyEvent.iCode);
			    if (oldCurrentItem!=iCurrentItem)
					{ // the next lines are to stop ContentHasChangedL from resetting the match buffer
					DrawContentNow();
					if (iMatchBuf)
						DisplayCursor();
					CurrentItemChangedL();
				    ReportEventL(MCoeControlObserver::EEventStateChanged);
					oldCurrentItem=iCurrentItem;
					}
				}
	        }
	    if ((iCurrentItem<0) && (code!=EKeyTab))
	        iCurrentItem=maxItem;
	    else if (iCurrentItem>maxItem)
	        iCurrentItem=0;
	    if (oldCurrentItem!=iCurrentItem)
			ContentHasChangedL();
//		else if (oldCursorPos!=iCursorPos && iMatchBuf)
		else if (oldCursorPos!=CalcCursorPos() && iMatchBuf)
			DisplayCursor();
		}
	else   // No array to change
        CEikonEnv::Beep();
    return(EKeyWasConsumed);
	}

EXPORT_C void CEikChoiceListBase::ContentHasChangedL()
	{
	DrawContentNow();
	if (iMatchBuf)
		{
		ResetMatchBuf();
		DisplayCursor();
		}
	CurrentItemChangedL(); // for subclassers
    ReportEventL(MCoeControlObserver::EEventStateChanged);
	}

EXPORT_C void CEikChoiceListBase::CurrentItemChangedL()
	{
	}

EXPORT_C void CEikChoiceListBase::MatchTypedChar(TUint aCode)
	{
	if (iMatchBuf)
		{
		if (iMatchBuf->MatchLength()==KEikMaxMatchingBufLength-2)
			return;
		iMatchBuf->AppendChar(aCode);
	    TInt newCurrentItem;
		TInt ret=iMatchBuf->FirstMatchingIndexF(newCurrentItem,*iArray);
		if (ret == KErrNone)
			{
			CheckCreateExtensions();
//			iCursorPos++;
			SetCursorPos(CalcCursorPos()+1);
			iCurrentItem=newCurrentItem;
			}
		else   // No match with buf with new letter: discard new char
			iMatchBuf->DeleteLastChar();
		}
	else
		{
		TChar aChar(aCode);
		aChar.Fold();
		TInt index=iCurrentItem+1;
	    TInt noItems=iArray->MdcaCount();
		for (TInt count=0; count<noItems; count++,index++)
			{
	        if (index==noItems)
	            index=0;
			TPtrC ptr=iArray->MdcaPoint(index);
			if (ptr.Length()==0)
				continue;
			TChar tempChar=ptr[0];
			tempChar.Fold();
			if ((aChar==tempChar)&&(index!=iCurrentItem))
				{
				iCurrentItem=index;
				return;
				}
			}
		}
	}

EXPORT_C void CEikChoiceListBase::UndoLastChar()
	{
	CheckCreateExtensions();
//	iCursorPos--;
	SetCursorPos(CalcCursorPos()-1);
	iMatchBuf->DeleteLastChar();
	TInt newCurrentItem;
	TInt ret=iMatchBuf->FirstMatchingIndexF(newCurrentItem,*iArray);
	if (!ret)
		iCurrentItem=newCurrentItem;
	}

EXPORT_C void CEikChoiceListBase::ResetMatchBuf()
	{
//	iCursorPos=0;
	CheckCreateExtensions();
	SetCursorPos(0);
	if (!iArray)
		{
		delete(iMatchBuf);
		iMatchBuf=NULL;
		}
	if (iMatchBuf)
		iMatchBuf->Clear();
	}

EXPORT_C void CEikChoiceListBase::HandleControlEventL(CCoeControl* /*aControl*/,TCoeEvent aEventType)
    {
    switch (aEventType)
        {
    case EEventRequestExit:
        HandleInteractionConfirmedL();
    case EEventRequestCancel:
        DestroyPopout();
    default:
        break;
        }
    }

EXPORT_C void CEikChoiceListBase::HandleInteractionConfirmedL()
    {
    TInt oldCurrentItem=iCurrentItem;
    iCurrentItem=PopoutCurrentItem();
    if (iCurrentItem!=oldCurrentItem)
		ContentHasChangedL();
    }

EXPORT_C TInt CEikChoiceListBase::PopoutCurrentItem() const
    {
	return(iCurrentItem);
    }

EXPORT_C void CEikChoiceListBase::DrawContentNow() const
    {
	ActivateGc();
    DrawContent();
	DeactivateGc();
	}

EXPORT_C void CEikChoiceListBase::DisplayCursor()
    {
	if ((!IsFocused()) || (iArray->MdcaCount() == 0) || (iCurrentItem == -1))
		return;
	const CFont* font=Font();
	TPoint cursorTl=Position();
    TMargins margins=iBorder.Margins();
	TPtrC des=iArray->MdcaPoint(iCurrentItem);
//	TPtrC stringBeforeCursor=des.Left(iCursorPos);
	CheckCreateExtensions();
	const TInt cursorPos=CalcCursorPos();
	TPtrC stringBeforeCursor=des.Left(cursorPos);
	TInt cursorWidth = 0;
//	if (iCursorPos < des.Length())
	if (cursorPos < des.Length())
		{
//		TPtrC charAtCursor = des.Mid(iCursorPos,1);
		TPtrC charAtCursor = des.Mid(cursorPos,1);
		cursorWidth = font->TextWidthInPixels(charAtCursor);
		}
	else
		cursorWidth = font->MaxNormalCharWidthInPixels();
	cursorTl.iX+=margins.iLeft+KEikChoiceListTextEdge+font->TextWidthInPixels(stringBeforeCursor);
	cursorTl.iY+=margins.iTop+iAscent;
    TRect rect = iBorder.InnerRect(Rect());
	if ((cursorTl.iX + cursorWidth) > rect.iBr.iX)
		{
		iMatchBuf->DeleteLastChar();
//		--iCursorPos;
		SetCursorPos(cursorPos-1);
		return;
		}
	iEikonEnv->DrawCursor(this,cursorTl,cursorWidth);
    }

EXPORT_C TInt CEikChoiceListBase::CursorPos() const
	{
//	return iCursorPos;
	return CalcCursorPos();
	}

EXPORT_C const CFont* CEikChoiceListBase::Font() const
	{
	return CalcFont();
	}

EXPORT_C void CEikChoiceListBase::Reserved_1()
	{}
EXPORT_C void CEikChoiceListBase::Reserved_2()
	{}
EXPORT_C void CEikChoiceListBase::Reserved_3()
	{}

inline void CEikChoiceListBase::CheckCreateExtensions()
	{
	if (!iChLstExtension)
		iChLstExtension=new CChoiceListExtension;
	}

void CEikChoiceListBase::SetCursorPos(TInt aPos)
	{
	if (iChLstExtension)
		iChLstExtension->SetCursorPos(aPos);
	// else ignore it
	}

TInt CEikChoiceListBase::CalcCursorPos() const
	{
	if (iChLstExtension)
		return iChLstExtension->CursorPos();
	return 0; // can't store pos otherwise
	}

const CFont* CEikChoiceListBase::CalcFont() const
	{
	return (iChLstExtension? iChLstExtension->Font()? 
						iChLstExtension->Font() : iEikonEnv->NormalFont() 
						: iEikonEnv->NormalFont());
	}

//
//Class CEikChoiceList
//

EXPORT_C void CEikChoiceList::ConstructL(CCoeControl* aParent, TInt aFlags, TInt aMaxDisplayChar)
	{
	iChoiceListFlags = aFlags;
	iMaxDisplayChar=aMaxDisplayChar;
	SetContainerWindowL(*aParent);
	}

EXPORT_C CEikChoiceList::CEikChoiceList()
    {
	__DECLARE_NAME(_S("CEikChoiceList"));
    }

EXPORT_C CEikChoiceList::~CEikChoiceList()
	{
	DestroyPopout();
	}

EXPORT_C CEikTextListBox* CEikChoiceList::PopoutTextListBox() const
	{
	return((CEikTextListBox*) iPopoutListBox);
	}

EXPORT_C TSize CEikChoiceList::MinimumSize()
    {
	TSize size = iBorder.SizeDelta();
	if (size.iHeight)
		size.iHeight+=2; // gap inside border
	size.iWidth += CalculateTextWidth() + 2*KEikChoiceListTextEdge;
	size.iHeight += Font()->HeightInPixels();
	return size;
	}

EXPORT_C void CEikChoiceList::ConstructFromResourceL(TResourceReader& aReader)
    {
	iChoiceListFlags = aReader.ReadInt16();
	iMaxDisplayChar=aReader.ReadInt16();
    TInt listId=aReader.ReadInt32();
	if (listId)
        iArray=iCoeEnv->ReadDesCArrayResourceL(listId);
	if ((iChoiceListFlags & EIncrementalMatching) && iArray)
	    iMatchBuf=new(ELeave)RIncrMatcherBuf<KEikMaxMatchingBufLength>;
    }

EXPORT_C void CEikChoiceList::DrawContent() const
	{
	TPtrC ptr;
	if (iArray && iArray->MdcaCount() && (iCurrentItem != -1))
		ptr.Set(iArray->MdcaPoint(iCurrentItem));
	DoDrawContent(ptr);
    }

EXPORT_C void CEikChoiceList::DoDrawContent(const TDesC& aDes) const
	{
	CWindowGc& gc = SystemGc();
   	gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	gc.SetBrushColor(iEikonEnv->ControlColor(EEikColorControlBackground, *this)); //KEikChoiceListBackGroundColor);
	gc.SetPenColor(iEikonEnv->ControlColor(EEikColorControlText, *this));
    gc.UseFont(Font());
	if (IsDimmed())
		gc.SetPenColor(iEikonEnv->ControlColor(EEikColorControlDimmedText, *this)); //KEikChoiceListDimmedBackGroundColor);
    TRect rect = iBorder.InnerRect(Rect());
	gc.DrawText(aDes,rect,iAscent,CGraphicsContext::ELeft,KEikChoiceListTextEdge);
	}

EXPORT_C void CEikChoiceList::DestroyPopout()
    {
    if (iPopoutListBox)
        {
        iEikonEnv->RemoveFromStack(iPopoutListBox);
        delete(iPopoutListBox);
        iPopoutListBox=NULL;
        }
    }

EXPORT_C void CEikChoiceList::CreatePopoutL()
    {
	if (!iArray || !iArray->MdcaCount())
		{
        CEikonEnv::Beep();
		return;
		}
	if (iPopoutListBox)
		return;
    TRAPD(err,DoCreatePopoutL());
    if (err)
        {
        DestroyPopout();
        User::Leave(err);
        }
    }

EXPORT_C void CEikChoiceList::DoCreatePopoutL()
    {
	CreateListBoxL();
	InitializeListBoxL();
	SetListBoxExtentL();
	ActivateListBoxL();
	}

EXPORT_C void CEikChoiceList::CreateListBoxL()
	{
	iPopoutListBox = new(ELeave) CEikChoiceListPopoutListBox;
	}

EXPORT_C void CEikChoiceList::InitializeListBoxL()
	{
	TInt listBoxFlags = CEikListBox::EPopout;
	if (iChoiceListFlags & EIncrementalMatching)
		listBoxFlags |= CEikListBox::EIncrementalMatching;
//	STATIC_CAST(CEikTextListBox*,iPopoutListBox)->ConstructL(NULL, listBoxFlags);
	STATIC_CAST(CEikChoiceListPopoutListBox*,iPopoutListBox)->ConstructL(NULL, listBoxFlags, Font());
	iPopoutListBox->CreateScrollBarFrameL();
	iPopoutListBox->ScrollBarFrame()->SetScrollBarVisibilityL(CEikScrollBarFrame::EOff, CEikScrollBarFrame::EAuto);
	iPopoutListBox->SetObserver(this);
	iEikonEnv->AddWindowShadow(iPopoutListBox);
	STATIC_CAST(CEikChoiceListPopoutListBox*,iPopoutListBox)->Model()->SetItemTextArray(iArray);
	STATIC_CAST(CEikChoiceListPopoutListBox*,iPopoutListBox)->Model()->SetOwnershipType(ELbmDoesNotOwnItemArray);
	}

EXPORT_C void CEikChoiceList::SetListBoxExtentL()
	{
	TInt listBoxWidth = iPopoutListBox->CalcWidthBasedOnRequiredItemWidth(CalculateTextWidth()); 
    TPoint choicePosScreenCoords=PositionRelativeToScreen();
	TRect listBoxRect(TPoint(choicePosScreenCoords.iX, 0), TSize(listBoxWidth, 0));
	iPopoutListBox->CalculatePopoutRect(iCurrentItem, choicePosScreenCoords.iY, listBoxRect);
	iPopoutListBox->SetExtentL(TPoint(choicePosScreenCoords.iX, listBoxRect.iTl.iY), listBoxRect.Size());
	}

EXPORT_C void CEikChoiceList::ActivateListBoxL()
	{
	if (iCurrentItem==-1)
		iPopoutListBox->SetCurrentItemIndex(0);
	else
		iPopoutListBox->SetCurrentItemIndex(iCurrentItem);
    iEikonEnv->AddDialogLikeControlToStackL(iPopoutListBox);
	iPopoutListBox->ActivateL();
	iPopoutListBox->HandleItemAdditionL();
    }

EXPORT_C TInt CEikChoiceList::PopoutCurrentItem() const
    {
    return(iPopoutListBox->CurrentItemIndex());
    }

EXPORT_C void CEikChoiceList::Reserved_1()
	{}
EXPORT_C void CEikChoiceList::Reserved_2()
	{}
EXPORT_C void CEikChoiceList::Reserved_3()
	{}

//
// class CEikChoiceListPopoutListBox
//

EXPORT_C CEikChoiceListPopoutListBox::CEikChoiceListPopoutListBox()
	{}

EXPORT_C void CEikChoiceListPopoutListBox::ConstructL(const CCoeControl* aParent,TInt aFlags,const CFont* aFont)
	{
	iModel=new(ELeave) CTextListBoxModel;
	STATIC_CAST(CTextListBoxModel*,iModel)->ConstructL();
	iItemDrawer=new(ELeave) CTextListItemDrawer(STATIC_CAST(CTextListBoxModel*,iModel),aFont);
	CEikListBox::ConstructL(aParent,aFlags);
	}
