// EIKSFORM.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <coemain.h>
#include <eiksform.h>
#include <eiksform.pan>
#include <eikcapca.h>
#include <eikcapc.h>
#include <eikenv.h>

GLDEF_C void Panic(TEikScrollableFormPanic aPanic)
    {
    User::Panic(_L("EIKON-SCR-FORM"),aPanic);
    }  


EXPORT_C CEikScrollableForm::CEikScrollableForm()
	{
	__DECLARE_NAME(_S("CEikScrollableForm"));
	}

EXPORT_C CEikScrollableForm::~CEikScrollableForm()
	{
	if (iViewWin)
		{
		iViewWin->Close();
		delete iViewWin;
		iViewWin=NULL;
		}
	}

EXPORT_C void CEikScrollableForm::ConstructL(const CCoeControl& aParent)
	{
	CopyControlContextFrom(&aParent);
	iViewWin=new(ELeave) RWindow(iCoeEnv->WsSession());
	User::LeaveIfError(iViewWin->Construct(*(aParent.DrawableWindow()),(TUint32)this));
	CreateWindowL(*iViewWin);

	}

EXPORT_C TInt CEikScrollableForm::YPosToLine(TInt& aYPos) const
//
// Calculates the line that aYPos is on and adjust aYPos to the top of that line.
// YPos is relative to the ViewWin not the DataWin
	{
	__ASSERT_DEBUG(iLines!=NULL,Panic(EEikPanicScrollableFormNullCaptionControlArray));
	TInt count=iLines->Count();
	__ASSERT_DEBUG(count>0,Panic(EEikPanicScrollableFormNoControls));
	TInt currentLine=0;
	TInt currentYPos=(*iLines)[currentLine]->Position().iY+Window().Position().iY;
	if (aYPos>=currentYPos)
		{
		for (;currentLine+1<count;currentLine++)
			{
			TInt nextYPos=(*iLines)[currentLine+1]->Position().iY+Window().Position().iY;
			if (aYPos<nextYPos)
				break;
			currentYPos=nextYPos;
			}
		}
	aYPos=currentYPos;
	return currentLine;
	}

EXPORT_C TInt CEikScrollableForm::LineToYPos(TInt& aLine) const
//
// Calcs YPos of line relative to the ViewWin and adjusts aLine if its out of bounds
	{
	__ASSERT_DEBUG(iLines!=NULL,Panic(EEikPanicScrollableFormNullCaptionControlArray));
	TInt count=iLines->Count();
	__ASSERT_DEBUG(count>0,Panic(EEikPanicScrollableFormNoControls));
	if (aLine<0)
		aLine=0;
	else if (aLine>=count)
		aLine=count-1;
	return (*iLines)[aLine]->Position().iY+Window().Position().iY;
	}

EXPORT_C void CEikScrollableForm::ExposeLine(TInt aLine)
//
// Exposes the given line so that its fully visible in the ViewWin
	{
	TInt oldLine=aLine;
	TInt yPos=LineToYPos(aLine);
	if (oldLine<aLine)	// above top
		SetDataPosition(0);
	else if (oldLine>aLine)	// below bottom
		SetDataPosition(iSize.iHeight-Window().Size().iHeight);
	else
		{
		TInt controlHeight=ControlHeight(aLine);
		if (yPos<0 && controlHeight<=iSize.iHeight)
			SetDataPosition(Window().Position().iY-yPos);
		else
			{
			yPos+=controlHeight;
			if (yPos>iSize.iHeight || controlHeight>iSize.iHeight)
				SetDataPosition(Window().Position().iY+iSize.iHeight-yPos);
			}
		}
	}

EXPORT_C void CEikScrollableForm::SetDataPosition(TInt aPosition)
//
// Sets the y coordinate of the DataWin to the given aPosition
	{
	__ASSERT_DEBUG(aPosition>=(iSize.iHeight-Window().Size().iHeight) && aPosition<=0,
		Panic(EEikPanicScrollableFormInvalidDataPosition));
	TInt oldPos=Window().Position().iY;
	if (aPosition==oldPos)
		return;
	Window().SetPosition(TPoint(0,aPosition));
//
// The following code is not essential but redraws the screen without waiting for an explicit redraw
//
	TInt offset=Window().Position().iY-oldPos;
	TRect invalidRect=Rect();
	if (offset>0)
		invalidRect.iBr.iY=Min(invalidRect.iBr.iY,invalidRect.iTl.iY+offset);
	else
		invalidRect.iTl.iY=Max(invalidRect.iTl.iY,invalidRect.iBr.iY+offset);
	TInt topControlYPos=invalidRect.iTl.iY;
	TInt topControl=YPosToLine(topControlYPos);
	invalidRect.Move(0,-Window().Position().iY);	// Make it relative to DataWin
	Window().Invalidate(invalidRect);
	HandleRedrawEvent(invalidRect);
	TInt count=iLines->Count();	// Redraw window owning component controls
	for(;topControl<count;topControl++)
		{
		CCoeControl* ctrl=(*iLines)[topControl]->iControl;
		if (ctrl->Position().iY>invalidRect.iBr.iY)
			break;
		if (ctrl->OwnsWindow())
			ctrl->DrawNow();
		}
	}

EXPORT_C TInt CEikScrollableForm::ControlHeight(TInt aLineIndex) const
	{
	return (*iLines)[aLineIndex]->Size().iHeight;
	}

EXPORT_C void CEikScrollableForm::SizeChangedL()
	{
	iViewWin->SetExtent(iPosition,iSize);
	TSize dataSize=iLines->MinimumSize();
	dataSize.iWidth=Max(dataSize.iWidth,iSize.iWidth);
	dataSize.iHeight=Max(dataSize.iHeight,iSize.iHeight);
	TPoint dataPos(0,Window().Position().iY);
	TInt excess=dataPos.iY+dataSize.iHeight-iSize.iHeight;
	if (excess<0)
		dataPos.iY-=excess;
	if (dataPos.iY>0)
		dataPos.iY=0;
	Window().SetExtent(dataPos,dataSize);
	iLines->SetRectL(TRect(dataSize));
	}

EXPORT_C TSize CEikScrollableForm::MinimumSize()
	{
	return TSize(CEikForm::MinimumSize().iWidth,0);	// Can be zero height
	}

EXPORT_C void CEikScrollableForm::ActivateL()
	{
	iViewWin->Activate();
	CCoeControl::ActivateL();
	}

EXPORT_C void CEikScrollableForm::Reserved_1()
	{}
EXPORT_C void CEikScrollableForm::Reserved_2()
	{}
EXPORT_C void CEikScrollableForm::Reserved_3()
	{}

/////////////////////////////////////////////////

EXPORT_C CEikScrollableFormContainer::CEikScrollableFormContainer()
	{
	__DECLARE_NAME(_S("CEikScrollableFormContainer"));
	}

EXPORT_C CEikScrollableFormContainer::~CEikScrollableFormContainer()
	{
	delete iScrollableForm;
	delete iScrollBar;
	}

EXPORT_C void CEikScrollableFormContainer::CreateScrollBarL()
	{
	if (!iScrollBar)
		{
		iScrollBar = new(ELeave) CEikScrollBar;
		iScrollBar->ConstructL(this,this,CEikScrollBar::EVertical,0,0);
		}
	}

EXPORT_C void CEikScrollableFormContainer::HandleScrollEventL(CEikScrollBar* aScrollBar,TEikScrollEvent aEventType)
	{
	switch (aEventType)
		{
	case EEikScrollUp:
	case EEikScrollPageUp:
		{
		TInt yPos = 0;
		if (aEventType==EEikScrollPageUp)
			yPos-=iScrollableForm->Size().iHeight;
		TInt oldYPos=yPos;
		TInt topLine=iScrollableForm->YPosToLine(yPos);
		iScrollableForm->ExposeLine((yPos<oldYPos) ? topLine : topLine-1);
		}
		break;
	case EEikScrollDown:
	case EEikScrollPageDown:
		{
		TInt yPos = iScrollableForm->Size().iHeight;
		if (aEventType==EEikScrollPageDown)
			yPos*=2;
		TInt oldYPos=yPos;
		TInt bottomLine=iScrollableForm->YPosToLine(yPos);
		iScrollableForm->ExposeLine((yPos+iScrollableForm->ControlHeight(bottomLine)>oldYPos)
			? bottomLine : bottomLine+1);
		}
		break;
	case EEikScrollThumbDragVert:
	case EEikScrollThumbReleaseVert:
		iScrollableForm->SetDataPosition(-aScrollBar->ThumbPosition());
		return;
	default:
		return;
		};
	UpdateScrollBarL();
	}

EXPORT_C void CEikScrollableFormContainer::UpdateScrollBarL()
	{
	if (!iScrollBar || !iScrollBar->IsVisible())
		return;
	TEikScrollBarModel vertModel;
	RWindowBase& dataWin=*(iScrollableForm->DrawableWindow());
	vertModel.iScrollSpan = dataWin.Size().iHeight;
	vertModel.iThumbSpan = iScrollableForm->Size().iHeight;
	vertModel.iThumbPosition = -dataWin.Position().iY;
	iScrollBar->SetModelL(&vertModel);
	}

EXPORT_C void CEikScrollableFormContainer::SizeChangedL()
	{
	TRect formRect=Rect();
	if (iScrollBar)
		{
		iScrollableForm->SetSizeL(TSize());	// Forces DataWin size to be set correctly
		if (iScrollableForm->DrawableWindow()->Size().iHeight>formRect.Height())
			{
			iScrollBar->SetLengthL(formRect.Height());
			TPoint scrollBarPos=formRect.iBr-iScrollBar->Size();
			iScrollBar->SetPosition(scrollBarPos);
			formRect.iBr.iX=scrollBarPos.iX;
			iScrollBar->MakeVisible(ETrue);
			}
		else
			iScrollBar->MakeVisible(EFalse);
		
		}
	iScrollableForm->SetSizeL(formRect.Size());
	}

EXPORT_C TSize CEikScrollableFormContainer::MinimumSize()
	{
	TSize size=iScrollableForm->MinimumSize();
	if (iScrollBar && iScrollBar->IsVisible())
		{
		size.iHeight=Max(size.iHeight,CEikScrollBar::MinVisibleLength(0));
		size.iWidth=Max(size.iWidth,iScrollBar->Size().iWidth);
		}
	return size;
	}

EXPORT_C TInt CEikScrollableFormContainer::CountComponentControls() const
	{
	return 2;
	}

EXPORT_C CCoeControl* CEikScrollableFormContainer::ComponentControl(TInt aIndex) const
	{
	if (aIndex==0)
		return iScrollableForm;
	return iScrollBar;
	}

EXPORT_C void CEikScrollableFormContainer::Reserved_1()
	{}

EXPORT_C void CEikScrollableFormContainer::Reserved_2()
	{}
