// EIKCMBOX.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <e32keys.h>
#include <txtglobl.h>
#include <barsread.h>
#include <badesca.h>
#include <eikenv.h>
#include <eikcmbox.h>
#include <eikedwin.h>
#include <eikcmbut.h>
#include <eiktxlbx.h>
#include <eiktxlbm.h>
#include <eikpanic.h>
#include <eikon.rsg> 

EXPORT_C CEikComboBox::CEikComboBox()
	: CEikBorderedControl(TEikBorder(TEikBorder::ESingleGray))
	{
	__DECLARE_NAME(_S("CEikComboBox"));
	}

EXPORT_C CEikComboBox::~CEikComboBox()
	{
	if (!(iComboFlags&EArrayExternalOwner))
		delete iArray;
	delete iEdwin;
	delete iTabButton;
	DestroyPopout();
	}

EXPORT_C void CEikComboBox::ConstructL(const CCoeControl& aParent,TInt aWidthInChars,TInt aTextLimit,TInt aMaxArraySize)
	{
	iMaxArraySize=aMaxArraySize;
	SetContainerWindowL(aParent);
	iEdwin->ConstructL(0,aWidthInChars,aTextLimit,1);
	}

EXPORT_C void CEikComboBox::ConstructFromResourceL(TResourceReader& aReader)
	{
	const TInt widthInChars=aReader.ReadInt16();
	const TInt textLimit=aReader.ReadInt16();
	iEdwin->ConstructL(0,widthInChars,textLimit,1);
	iMaxArraySize=aReader.ReadInt16();
	}

EXPORT_C void CEikComboBox::SetContainerWindowL(const CCoeControl& aParent)
	{
	CCoeControl::SetContainerWindowL(aParent);
	iEdwin=new(ELeave) CEikEdwin(TEikBorder(TEikBorder::ENone));
	iEdwin->SetObserver(this);
	iEdwin->SetContainerWindowL(aParent);
	ConstructTabButtonL();
	}

void CEikComboBox::ConstructTabButtonL()
	{
	iTabButton=iEikonEnv->CreateStandardTabButtonL();
	iTabButton->SetObserver(this);
	iTabButton->SetContainerWindowL(*this);
	}

EXPORT_C MDesCArray* CEikComboBox::Array() const
	{
	return iArray;
	}

EXPORT_C void CEikComboBox::SetArrayL(TInt aResourceId,TArrayOwnershipType aOwner)
	{
	CDesCArray* array=iCoeEnv->ReadDesCArrayResourceL(aResourceId);
	SetArray(array,aOwner);
	}

EXPORT_C void CEikComboBox::SetArray(MDesCArray* aArray,TArrayOwnershipType aOwner)
	{
	if (!(iComboFlags&EArrayExternalOwner))
		delete iArray;
	if (aOwner)
		iComboFlags|=EArrayExternalOwner;
	iArray=aArray;
	}

EXPORT_C void CEikComboBox::FocusChanged(TDrawNow aDrawNow)
	{
	iEdwin->SetFocus(IsFocused(),aDrawNow);
	}

EXPORT_C TSize CEikComboBox::MinimumSize()
	{
	TSize size=iEdwin->MinimumSize();
	const TSize minTabSize(iTabButton->MinimumSize());
	size.iWidth+=minTabSize.iWidth+iBorder.Margins().iLeft;
	size.iHeight=Max(size.iHeight+iBorder.SizeDelta().iHeight,minTabSize.iHeight);
	return size;
	}

EXPORT_C void CEikComboBox::SizeChangedL()
	{
	// panic if there is insufficient width or height
	const TRect rect(Rect());
	const TRect innerRect(iBorder.InnerRect(rect));
	const TSize tabButtonSize(iTabButton->MinimumSize());
	const TInt availableWidthForEdwin=innerRect.Width()-tabButtonSize.iWidth+iBorder.Margins().iRight; // since tab button overlaps border
#if defined(__DEBUG__)
	const TInt minHeight=Max(iEdwin->MinimumSize().iHeight,tabButtonSize.iHeight-2);
	__ASSERT_DEBUG(innerRect.Height()>=minHeight, Panic(EEikPanicEditComboInsufficientSize));
	__ASSERT_DEBUG(availableWidthForEdwin>=iEikonEnv->NormalFont()->MaxNormalCharWidthInPixels(),
							Panic(EEikPanicEditComboInsufficientSize));
#endif
	iEdwin->SetExtentL(innerRect.iTl,TSize(availableWidthForEdwin,innerRect.Height()));
	TRect viewRect=iEdwin->TextView()->ViewRect();
	CParaFormat* format=CParaFormat::NewLC();
	TParaFormatMask mask;
	CGlobalText* text=(CGlobalText*)iEdwin->Text();
	text->GetParaFormatL(format,mask,0,text->DocumentLength());
	const TInt lineSpacing=Max(((100+CLayoutData::EFFontHeightIncreaseFactor)*iEikonEnv->NormalFont()->HeightInPixels())/100,
								iEikonEnv->ScreenDevice()->HorizontalTwipsToPixels(format->iLineSpacingInTwips));
	const TInt dY=Max(0, (viewRect.Height()-lineSpacing)/2);
	CleanupStack::PopAndDestroy(); // format
	viewRect.iTl.iY+=dY;
	viewRect.iBr.iY-=dY;
	iEdwin->TextView()->SetViewRect(viewRect);
	TViewYPosQualifier yPosQ;
	yPosQ.SetMakeLineFullyVisible();
	iEdwin->TextView()->HandleGlobalChangeNoRedrawL(yPosQ);
	iTabButton->SetExtentL(TPoint(rect.iBr.iX-tabButtonSize.iWidth,rect.iTl.iY),
								TSize(tabButtonSize.iWidth,rect.Height()));
	}

EXPORT_C TKeyResponse CEikComboBox::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
	{
	if (aKeyEvent.iCode==EKeyTab && (!(aKeyEvent.iModifiers&EModifierCtrl) || aKeyEvent.iModifiers&EModifierPureKeycode))
		{
		if (iArray)
			{
			iTabButton->Animate();
			CreatePopoutL();
			}
		else // No array to change
			iEikonEnv->InfoMsg(R_EIK_TBUF_COMBO_BOX_EMPTY_LIST);
		}
	else
		return iEdwin->OfferKeyEventL(aKeyEvent,aType);
	return EKeyWasConsumed;
	}

EXPORT_C void CEikComboBox::HandleControlEventL(CCoeControl* aControl,TCoeEvent aEvent)
	{
	switch (aEvent)
		{
	case EEventRequestFocus:
	case EEventPrepareFocusTransition:
		ReportEventL(aEvent);
		break;
    case EEventStateChanged:
		if (aControl==iTabButton)
			CreatePopoutL();
        break;
    case EEventRequestExit:
        HandleInteractionConfirmedL();
    case EEventRequestCancel:
        DestroyPopout();
	default:
		break;
		}
	}

EXPORT_C TInt CEikComboBox::CountComponentControls() const
	{
	return 2; // edwin and tab button
	}

EXPORT_C CCoeControl* CEikComboBox::ComponentControl(TInt aIndex) const
	{
	if (aIndex)
		return iTabButton;
	return iEdwin;
	}

void CEikComboBox::HandleInteractionConfirmedL()
	{
	if (!iArray)
		iEdwin->SetTextL(NULL);
	else
		{
		const TInt currentItem=iPopout->CurrentItemIndex();
		HBufC* string=iEdwin->GetTextInHBufL();
		CleanupStack::PushL(string);
		if (string==NULL || *string!=iArray->MdcaPoint(currentItem))
			{
			TPtrC text(iArray->MdcaPoint(currentItem));
			iEdwin->SetTextL(&text);
			iEdwin->DrawNow();
			ReportEventL(EEventStateChanged);
			}
		CleanupStack::PopAndDestroy(); // string
		}
	}

EXPORT_C void CEikComboBox::InsertTextIntoArrayL()
	{
	HBufC* string=iEdwin->GetTextInHBufL();
	CleanupStack::PushL(string);
	if (string && string->Length())
		{
		CDesCArray* list=((CDesCArray*)iArray);
		if (list)
			{
			TInt pos;
			if (!list->Find(*string,pos))
				list->Delete(pos);
			list->InsertL(0,*string);
			while (iMaxArraySize && list->Count()>iMaxArraySize)
				list->Delete(iMaxArraySize);
			}
		}
	CleanupStack::PopAndDestroy(); // string
	}

EXPORT_C void CEikComboBox::GetText(TDes& aDes) const
	{
	iEdwin->GetText(aDes);
	}

EXPORT_C void CEikComboBox::SetTextL(const TDesC* aDes)
	{
	iEdwin->SetTextL(aDes);
	}

EXPORT_C CEikEdwin* CEikComboBox::Edwin() const
	{
	return iEdwin;
	}

EXPORT_C TInt CEikComboBox::MaxArraySize() const
	{
	return iMaxArraySize;
	}

void CEikComboBox::DestroyPopout()
    {
    if (iPopout)
        {
        iEikonEnv->RemoveFromStack(iPopout);
        delete iPopout;
        iPopout=NULL;
        }
    }

void CEikComboBox::CreatePopoutL()
	{
	if (!iArray||!iArray->MdcaCount())
		{
		iEikonEnv->InfoMsg(R_EIK_TBUF_COMBO_BOX_EMPTY_LIST);
		return;
		}
	if (iPopout)
		return;
	TRAPD(err,DoCreatePopoutL());
	if (err)
		{
		DestroyPopout();
		User::Leave(err);
		}
	}

void CEikComboBox::DoCreatePopoutL()
	{
	iPopout=new(ELeave) CEikTextListBox;
	iPopout->ConstructL(NULL,CEikListBox::EPopout);
	iPopout->CreateScrollBarFrameL();
	iPopout->ScrollBarFrame()->SetScrollBarVisibilityL(CEikScrollBarFrame::EOff,CEikScrollBarFrame::EAuto);
	iPopout->SetObserver(this);
	TPoint comboPos=Window().Position()+Position();
	iPopout->Model()->SetItemTextArray(iArray);
	iPopout->Model()->SetOwnershipType(ELbmDoesNotOwnItemArray);
	const TInt listBoxWidth=Size().iWidth;
	TRect listBoxRect(TPoint(comboPos.iX,0),TSize(listBoxWidth,0));
	iPopout->CalculatePopoutRect(0,comboPos.iY,listBoxRect);
	iPopout->SetExtentL(TPoint(comboPos.iX,listBoxRect.iTl.iY),listBoxRect.Size());
	iPopout->SetCurrentItemIndex(0);
	iEikonEnv->AddDialogLikeControlToStackL(iPopout);
	iPopout->ActivateL(); // won't leave
	iPopout->DrawNow();
	}

EXPORT_C void CEikComboBox::Reserved_1()
	{}
EXPORT_C void CEikComboBox::Reserved_2()
	{}
