// EIKGRID.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <e32std.h>
#include <e32base.h>
#include <coemain.h>
#include <grdstd.h>
#include <eikgrid.h>
#include <eiksbfrm.h>
#include <eikenv.h>

#define KGridPointerRepeatDelayInMicroSeconds	10000

#define KGridKludgeRectForPointerRepeats		(TRect(-1000,-1000,1000,1000))
// !! Until Steve provides a DigitizerRect() function

EXPORT_C CEikGrid::~CEikGrid()
	{
	delete iSBFrame;
	}

EXPORT_C CEikGrid* CEikGrid::NewL(CCoeControl* aControl,CGridLay *aGridLay,CGridImg *aGridImg)
	{
	CEikGrid *self=new(ELeave) CEikGrid(aGridLay,aGridImg);
	CleanupStack::PushL(self);
	self->ConstructL(aControl);
	CleanupStack::Pop();
	return self;
	}

EXPORT_C CEikGrid::CEikGrid(CGridLay *aGridLay,CGridImg *aGridImg)
	: iGridLay(aGridLay),
	iGridImg(aGridImg)
	{
	iBorder.SetType(TEikBorder::ESingleGray);
	__DECLARE_NAME(_S("CEikGrid"));
	}

EXPORT_C void CEikGrid::ConstructL(CCoeControl* aControl)
	{
	CreateWindowL(aControl);
	Window().PointerFilter(EPointerFilterDrag,0);
	Window().SetPointerGrab(ETrue);
	iGridImg->SetWindow(&Window());
	if (iSBFrame==NULL)
		iSBFrame=new(ELeave) CEikScrollBarFrame(this, this); 
	if (iGridLay->IsIndefiniteRowBoundaries())
		{
		iGridLay->SetUniformRowHeight(ETrue);
		}
	iSBFrame->SetScrollBarVisibilityL(CEikScrollBarFrame::EAuto,CEikScrollBarFrame::EAuto);	
	iSBFrame->SetAdjustsHorizontalModel(EFalse);
	iSBFrame->SetAdjustsVerticalModel(EFalse);
	iSBFrame->SetFlagsForHScrollBar(CEikScrollBar::ENoAutoDimming);
	iSBFrame->SetFlagsForVScrollBar(CEikScrollBar::ENoAutoDimming);
	}

EXPORT_C CGridLay* CEikGrid::GridLay() const
	{
	return iGridLay;
	}

EXPORT_C CGridImg* CEikGrid::GridImg() const
	{
	return iGridImg;
	}

EXPORT_C CEikScrollBarFrame* CEikGrid::SBFrame() const
	{
	return iSBFrame;
	}

EXPORT_C void CEikGrid::Draw(const TRect& aRect) const
	{
	CWindowGc& gc=SystemGc();
	CEikBorderedControl::Draw(aRect);
	TRAPD(err,iGridImg->DrawL(&gc,aRect))
	if (err)
		{
		gc.Clear(aRect);
		iEikonEnv->NotifyIdleErrorWhileRedrawing(err);
		}
	}

EXPORT_C void CEikGrid::HandleScrollEventL(CEikScrollBar* aScrollBar,TEikScrollEvent aEventType)
	{
	TPoint offset;
	TRangeRef visRange=iGridLay->VisibleRange();
	TBool checkScrollBar=ETrue;
	switch (aEventType&KEikScrollEventBarMask)
		{
	case KEikScrollEventFromHBar:
		switch (aEventType)
			{
		case EEikScrollLeft:
			offset=iGridLay->ExposeCellToTopLeft(TCellRef(visRange.iFrom.iRow,visRange.iFrom.iCol-1));
			break;
		case EEikScrollRight:
			offset=iGridLay->ExposeCellToTopLeft(TCellRef(visRange.iFrom.iRow,visRange.iFrom.iCol+1));
			break;
		case EEikScrollPageLeft:
			offset=iGridLay->PageScroll(EMovePageRight);
			break;
		case EEikScrollPageRight:
			offset=iGridLay->PageScroll(EMovePageLeft);
			break;
		case EEikScrollThumbDragHoriz:
		case EEikScrollThumbReleaseHoriz:
			{
			TInt thumbPos=aScrollBar->ThumbPosition();
			offset=iGridLay->ExposeCellToTopLeft(TCellRef(visRange.iFrom.iRow,
				thumbPos+iGridLay->MinVisibleFromColumn()));
			if (aEventType==EEikScrollThumbDragHoriz)
				checkScrollBar=EFalse;
			break;
			}
		default:
			break;
			}
	case KEikScrollEventFromVBar:
		switch (aEventType)
			{
		case EEikScrollUp:
			offset=iGridLay->ExposeCellToTopLeft(TCellRef(visRange.iFrom.iRow-1,visRange.iFrom.iCol));
			break;
		case EEikScrollDown:
			offset=iGridLay->ExposeCellToTopLeft(TCellRef(visRange.iFrom.iRow+1,visRange.iFrom.iCol));
			break;
		case EEikScrollPageUp:
			offset=iGridLay->PageScroll(EMovePageDown);
			break;
		case EEikScrollPageDown:
			offset=iGridLay->PageScroll(EMovePageUp);
			break;
		case EEikScrollThumbDragVert:
		case EEikScrollThumbReleaseVert:
			{
			TInt thumbPos=aScrollBar->ThumbPosition();
			offset=iGridLay->ExposeCellToTopLeft(TCellRef(thumbPos+iGridLay->MinVisibleFromRow(),
				visRange.iFrom.iCol));
			if (aEventType==EEikScrollThumbDragVert)
				checkScrollBar=EFalse;
			break;
			}
		default:
			break;
			}
		break;
		}
	iGridImg->ScrollL(offset);
	if (checkScrollBar)
		CheckScrollBarPosL();
	}

EXPORT_C TKeyResponse CEikGrid::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
// Tells GridImg to do the appropriate action on a keypress.
	{
	if (aType!=EEventKey)
		return EKeyWasNotConsumed;
	TUint selectState=0;
	if (aKeyEvent.iModifiers&EModifierShift)
		selectState=CGridImg::EIsWithSelect;
	if (aKeyEvent.iModifiers&EModifierCtrl)
		{
 		switch (aKeyEvent.iCode)
			{
		case EKeyUpArrow:
			iGridImg->MoveCursorL(EMovePageUp,selectState);
			break;
		case EKeyDownArrow:
			iGridImg->MoveCursorL(EMovePageDown,selectState);
			break;
		case EKeyLeftArrow:
			iGridImg->MoveCursorL(EMovePageLeft,selectState);
			break;
		case EKeyRightArrow:
			iGridImg->MoveCursorL(EMovePageRight,selectState);
			break;
		case EKeyHome:
			iGridImg->MoveCursorL(EMoveHome,selectState);
			break;
		case EKeyEnd:
			iGridImg->MoveCursorL(EMoveEnd,selectState);
			break;
		case EKeyPageUp:
			iGridImg->MoveCursorL(EMoveColumnStart,selectState);
			break;
		case EKeyPageDown:
			iGridImg->MoveCursorL(EMoveColumnEnd,selectState);
			break;
		case EKeyNull:	// Must be a 'space' for reasons beyond mortal ken
			iGridImg->AddColToSelectedL(CursorPos().iCol);
			break;
		case EKeyTab:
			iGridImg->AddRowToSelectedL(CursorPos().iRow);
			break;
		default:
			return EKeyWasNotConsumed;
			}
		}
	else
		{
		switch (aKeyEvent.iCode)
			{
		case EKeyUpArrow:
			iGridImg->MoveCursorL(EMoveRowUp,selectState);
			break;
		case EKeyDownArrow:
			iGridImg->MoveCursorL(EMoveRowDown,selectState);
			break;
		case EKeyLeftArrow:
			iGridImg->MoveCursorL(EMoveColumnLeft,selectState);
			break;
		case EKeyRightArrow:
			iGridImg->MoveCursorL(EMoveColumnRight,selectState);
			break;
		case EKeyPageUp:
			iGridImg->MoveCursorL(EMovePageUp,selectState);
			break;
		case EKeyPageDown:
			iGridImg->MoveCursorL(EMovePageDown,selectState);
			break;
		case EKeyHome:
			iGridImg->MoveCursorL(EMoveRowStart,selectState);
			break;
		case EKeyEnd:
			iGridImg->MoveCursorL(EMoveRowEnd,selectState);
			break;
		case EKeyTab:
			if (selectState)
				iGridImg->MoveCursorL(EMoveColumnLeft,0);
			else
				iGridImg->MoveCursorL(EMoveColumnRight,0);
			break;
		default:
			return EKeyWasNotConsumed;
			}
		}
	ExposeCellAndScrollL(CellToExpose());
	return EKeyWasConsumed;
	}

EXPORT_C void CEikGrid::HandlePointerEventL(const TPointerEvent& aPointerEvent)
// Tells GridImg to do the appropriate action on a pointer event
	{
    if ((aPointerEvent.iType==TPointerEvent::EDrag || aPointerEvent.iType==TPointerEvent::EButtonRepeat)
		&& !iGridImg->MainRect().Contains(aPointerEvent.iPosition))
		{
        Window().RequestPointerRepeatEvent(KGridPointerRepeatDelayInMicroSeconds,KGridKludgeRectForPointerRepeats);
		}
	TPointerEvent event=aPointerEvent;
	if (event.iType==TPointerEvent::EButtonRepeat)
		event.iType=TPointerEvent::EDrag;
	else if (event.iModifiers&EModifierDoubleClick)
		event.iType=TPointerEvent::EButton1Down;

	TUint flagList=0;
	if (event.iModifiers&EModifierCtrl)
		flagList=CGridImg::EIsWithControl;
	if (event.iType == TPointerEvent::EButton1Up)
		iGridImg->FinishLabelDragL();
	else if (event.iType == TPointerEvent::EButton1Down)
		{
		if (!iGridImg->StartLabelDrag(event.iPosition))
			{
			if (event.iModifiers&EModifierShift)
				flagList|=CGridImg::EIsWithSelect;
			iGridImg->SetCursorWithPointerL(event.iPosition,flagList);
			}
		}
	else if (event.iType == TPointerEvent::EDrag)
		{
		if (!iGridImg->UpdateLabelDrag(event.iPosition))
			{
			flagList|=CGridImg::EIsWithSelect|CGridImg::EIsWithDrag;
			iGridImg->SetCursorWithPointerL(event.iPosition,flagList);
			}
		}
	CheckScrollBarPosL();
	}

EXPORT_C void CEikGrid::SizeChangedL()
	{
	iGridImg->SetGridRect(iBorder.InnerRect(Rect()));
	iGridLay->ResetVisibleToCell();
	CheckScrollBarPosL();
	}

EXPORT_C TInt CEikGrid::CountComponentControls() const
	{
	TInt count=CEikBorderedControl::CountComponentControls();
	if (iSBFrame)
		count+=iSBFrame->CountComponentControls();
	return count;
	}

EXPORT_C CCoeControl* CEikGrid::ComponentControl(TInt aIndex) const
	{
	TInt baseCount=CEikBorderedControl::CountComponentControls();
	if (aIndex<baseCount)
		return CEikBorderedControl::ComponentControl(aIndex);
	aIndex-=baseCount;
	return iSBFrame->ComponentControl(aIndex);
	}

EXPORT_C void CEikGrid::DrawCellL(const TCellRef& aCell) const
	{
	iGridImg->DrawCellL(aCell);
	}

EXPORT_C void CEikGrid::DrawRangeL(const TRangeRef& aRange) const
	{
	iGridImg->DrawRangeL(aRange);
	}

EXPORT_C void CEikGrid::DrawSelectedL() const
	{
	iGridImg->DrawSelectedL();
	}

EXPORT_C TCellRef CEikGrid::CursorPos() const
	{
	return iGridImg->CursorPos();
	}

EXPORT_C void CEikGrid::SetCursorPosL(const TCellRef& aCursorPos) const
	{
	iGridImg->SetCursorPosL(aCursorPos);
	ExposeCellAndScrollL(aCursorPos);
	}

EXPORT_C TPoint CEikGrid::ExposeCellL(const TCellRef& aCell) const
	{
	const TInt noOfReps=3;	// Should never need more than 3 
	TPoint offset;
	TRangeRef oldVisRange=iGridLay->VisibleRange();
	for (TInt ii=0;ii<noOfReps;ii++)
		{
		offset+=iGridLay->ExposeCell(aCell);
		iGridImg->CheckSideLabelWidthAndScrollL();
		CheckScrollBarPosL();
		TRangeRef newVisRange=iGridLay->VisibleRange();
		if (newVisRange==oldVisRange)	//i.e. no scrollbars have (dis)appeared
			break;
		oldVisRange=newVisRange;
		}
	return offset;
	}

EXPORT_C void CEikGrid::ExposeCellAndScrollL(const TCellRef& aCell) const
	{
	iGridImg->ScrollL(ExposeCellL(aCell));
	}

EXPORT_C TCellRef CEikGrid::AnchorPos() const
	{
	return iGridImg->AnchorPos();
	}

EXPORT_C void CEikGrid::SetAnchorPosL(const TCellRef& aAnchorPos) const
	{
	iGridImg->SetAnchorPosL(aAnchorPos);
	ExposeCellAndScrollL(aAnchorPos);
	}

EXPORT_C void CEikGrid::CheckScrollBarPosL() const
// checks whether the datasize and thumb positions of the scrollbars are
// correct and updates them if not.
	{
	if (!iSBFrame)
		return;
	TBool retile=ETrue;
	while (retile)
		{
		TRangeRef visRange=iGridLay->VisibleRange();
		TEikScrollBarModel hSBModel(0);
		hSBModel.iThumbSpan=visRange.iTo.iCol-visRange.iFrom.iCol+1;
		if (hSBModel.iThumbSpan>=0)
			{
			TInt minCol=iGridLay->MinVisibleFromColumn();
			hSBModel.iScrollSpan=Max(iGridLay->ColumnExtent(),visRange.iTo.iCol)-minCol+1;
			hSBModel.iThumbPosition=visRange.iFrom.iCol-minCol;
			}
		else	// title lines must be outside screen
			hSBModel.iThumbSpan=0;
		TEikScrollBarModel vSBModel(0);
		vSBModel.iThumbSpan=visRange.iTo.iRow-visRange.iFrom.iRow+1;
		if (vSBModel.iThumbSpan>=0)
			{
			TInt minRow=iGridLay->MinVisibleFromRow();
			vSBModel.iScrollSpan = Max(iGridLay->RowExtent(),visRange.iTo.iRow)-minRow+1;
			vSBModel.iThumbPosition=visRange.iFrom.iRow-minRow;
			}
		else
			vSBModel.iThumbSpan=0;

		TRect inclusiveRect=Rect();
		inclusiveRect.Shrink(1,1);
		TRect clientRect=iGridImg->GridRect();
		TEikScrollBarFrameLayout layout;
		layout.iTilingMode=TEikScrollBarFrameLayout::EInclusiveRectConstant;
		if (iSBFrame->TileL(&hSBModel,&vSBModel,clientRect,inclusiveRect,layout))
			{
			iGridImg->SetGridRect(clientRect);
			iGridLay->ResetVisibleToCell();
			}
		else
			retile=EFalse;
		}
	}

EXPORT_C void CEikGrid::UpdateGridAfterZoomL()
	{
	((CGridLabelImg*)iGridImg->GridLabelImg())->NotifyGraphicsDeviceMapChangeL();	//Const cast
	iGridLay->RecalcPixelSparseMaps();
	iGridImg->ResetReferencePoints();
	iGridLay->ResetVisibleToCell();
	ExposeCellL(CellToExpose());
	DrawNow();
	}

EXPORT_C TCellRef CEikGrid::CellToExpose() const
	{
	TCellRef cursorPos=CursorPos();
	TRangeRef visRange=iGridLay->VisibleRange();
	const CGridCellRegion* selected=iGridImg->Selected();
	if (selected->IsRowSelectedLastIndex(cursorPos.iRow))
		cursorPos.iCol=visRange.iFrom.iCol;
	if (selected->IsColSelectedLastIndex(cursorPos.iCol))
		cursorPos.iRow=visRange.iFrom.iRow;
	return cursorPos;
	}

EXPORT_C void CEikGrid::Reserved_1()
	{}

EXPORT_C void CEikGrid::Reserved_2()
	{}

