// EIKLBV.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <badesca.h>
#include <gdi.h>
#include <w32std.h>
#include <eiklbx.pan>
#include <eiklbv.h>
#include <eiklbm.h>
#include <eiklbi.h>
#include <eikdutil.h>
#include <eikenv.h>

#define ITEM_EXISTS(x) (((x) > -1) && ((x) < iModel->NumberOfItems()))

//
// class CListViewExt
//

class CListViewExt : public CBase
	{
public:
	typedef CArrayFix<TInt> CSelectionIndexArray;
public:
	~CListViewExt();
	CListViewExt();

	// constructors
	virtual void ConstructL();

	// new functions
	const TBool IsVisible() const;
public:
	CSelectionIndexArray* iSelectionIndexes;
	MListVisibilityObserver* iVisibilityObserver;
	};

CListViewExt::CListViewExt()
	{
	}

CListViewExt::~CListViewExt()
	{
	delete iSelectionIndexes;
	}

void CListViewExt::ConstructL()
	{
	iSelectionIndexes = new(ELeave) CArrayFixFlat<TInt>(5);
	}

const TBool CListViewExt::IsVisible() const
	{
	if(iVisibilityObserver)
		return(iVisibilityObserver->IsVisible());
	else
		return ETrue;
	}

//
// class CListBoxView
//
 
void CListBoxView::SetVisibilityObserver(MListVisibilityObserver* aObserver)
	{
	iExt->iVisibilityObserver=aObserver;
	}

TBool CListBoxView::IsVisible() const
	{
	return(iExt->IsVisible());
	}

EXPORT_C CListBoxView::CListBoxView()
	{
	iItemHeight = 20;
//	iBackColor = KDefaultLbxBackColor;
	iBackColor = CEikonEnv::Static()->Color(EEikColorControlBackground); //KDefaultLbxBackColor;
	iMatcherCursorColor = TRgb(177, 177, 177);		// light gray?
	iCurrentItemIndex = 0;
	iTopItemIndex = 0;
	}

EXPORT_C void CListBoxView::ConstructL(MListBoxModel* aList, CListItemDrawer* aItemDrawer, CWsScreenDevice* aScreen, RWindowGroup* aGroupWindow, RWindow* aWsWindow, const TRect& aDisplayArea, TInt aItemHeight)
	{
	iModel = aList;
	iItemDrawer = aItemDrawer;
	iWin = aWsWindow;
	iGroupWin = aGroupWindow;
	// create a graphics context
    User::LeaveIfError(aScreen->CreateContext((CGraphicsContext*&)iGc));
	iGc->Activate(*iWin);
	iItemDrawer->SetGc(iGc);
	iViewRect = aDisplayArea;
	iItemHeight = aItemHeight;
	iExt=new(ELeave) CListViewExt();
	iExt->ConstructL();
	}
	
EXPORT_C CListBoxView::~CListBoxView()
	{
	// Although my "item Drawer" is created by some other object, it is convenient
	// for me to destroy it here...
	delete(iItemDrawer);
	delete(iExt);
	delete(iGc);
	}

EXPORT_C TInt CListBoxView::TopItemIndex() const
	{
	return iTopItemIndex;
	}

EXPORT_C TInt CListBoxView::BottomItemIndex() const
	{
	return iBottomItemIndex;
	}

EXPORT_C TBool CListBoxView::ItemIsSelected(TInt aItemIndex) const
	{
	TKeyArrayFix key(0, ECmpTInt);	
	TInt pos;
	if (iExt->iSelectionIndexes->Find(aItemIndex, key, pos))
		return(EFalse);
	return(ETrue);
	}

EXPORT_C void CListBoxView::ToggleItemL(TInt aItemIndex) 
	{
	__ASSERT_DEBUG(iExt->iSelectionIndexes, Panic(EEikPanicListBoxNoSelIndexArray));
	TKeyArrayFix key(0, ECmpTInt);	
	TInt pos;
	if (iExt->iSelectionIndexes->Find(aItemIndex, key, pos))
		iExt->iSelectionIndexes->AppendL(aItemIndex);   
	else
		iExt->iSelectionIndexes->Delete(pos);
	DrawItem(aItemIndex);
	}

EXPORT_C void CListBoxView::SelectItemL(TInt aItemIndex)
	{
	// select the specified item; if already selected, do nothing
	__ASSERT_DEBUG(iExt->iSelectionIndexes, Panic(EEikPanicListBoxNoSelIndexArray));
	TKeyArrayFix key(0, ECmpTInt);	
	TInt pos;
	if (iExt->iSelectionIndexes->Find(aItemIndex, key, pos))
		{
		iExt->iSelectionIndexes->AppendL(aItemIndex);
		DrawItem(aItemIndex);
		}
	}  

EXPORT_C void CListBoxView::DeselectItem(TInt aItemIndex)
	{
	// deselect the specified item; if not currently selected, do nothing
	__ASSERT_DEBUG(iExt->iSelectionIndexes, Panic(EEikPanicListBoxNoSelIndexArray));
	TKeyArrayFix key(0, ECmpTInt);	
	TInt pos;
	if (!(iExt->iSelectionIndexes->Find(aItemIndex, key, pos)))
		{
		iExt->iSelectionIndexes->Delete(pos);
		DrawItem(aItemIndex);
		}
	}  

EXPORT_C void CListBoxView::GetSelectionIndexesL(CSelectionIndexArray *aSelectionArray) const
	{
	__ASSERT_DEBUG(aSelectionArray, Panic(EEikPanicListBoxInvalidSelIndexArraySpecified));
	__ASSERT_DEBUG(iExt->iSelectionIndexes, Panic(EEikPanicListBoxNoSelIndexArray));
	aSelectionArray->Reset();
	TInt selectionIndex;
	TInt numOfSelections = iExt->iSelectionIndexes->Count();
	for (TInt i = 0; i < numOfSelections; i++)
		{
		selectionIndex = (*(iExt->iSelectionIndexes))[i];
		aSelectionArray->AppendL(selectionIndex);
		}
	}

EXPORT_C void CListBoxView::SetSelectionIndexesL(const CSelectionIndexArray *aSelectionArray)
	{
	__ASSERT_DEBUG(aSelectionArray, Panic(EEikPanicListBoxInvalidSelIndexArraySpecified));
	ClearSelection();
	TInt numOfSelections = aSelectionArray->Count();
	TInt selectionIndex;
	for (TInt i = 0; i < numOfSelections; i++)
		{
		selectionIndex = (*aSelectionArray)[i];
		SelectItemL(selectionIndex);
		}
	}

EXPORT_C const CListBoxView::CSelectionIndexArray* CListBoxView::SelectionIndexes() const
	{
	return iExt->iSelectionIndexes;
	}

EXPORT_C void CListBoxView::ClearSelection()
	{
	__ASSERT_DEBUG(iExt->iSelectionIndexes, Panic(EEikPanicListBoxNoSelIndexArray));
	TInt numSelectedItems;
	TInt selectedItemIndex; 
	while ((numSelectedItems = iExt->iSelectionIndexes->Count()) > 0)
		{
		selectedItemIndex = (*(iExt->iSelectionIndexes))[0];
		DeselectItem(selectedItemIndex);
		}
	ClearSelectionAnchorAndActiveIndex();
	}

EXPORT_C void CListBoxView::ClearSelectionAnchorAndActiveIndex()
	{
	iAnchorIndex = 0;
	iActiveEndIndex = 0;
	ClearFlags(EAnchorExists);
	}

EXPORT_C void CListBoxView::SelectRangeL(TInt aItemIndex1, TInt aItemIndex2)
	{
	TInt startItem, endItem;
	startItem = Min(aItemIndex1, aItemIndex2);
	endItem = Max(aItemIndex1, aItemIndex2);
	for (TInt i = startItem; i <= endItem; i++)
		SelectItemL(i);
	}

EXPORT_C void CListBoxView::SetAnchor(TInt aItemIndex)
	{
    SetFlags(EAnchorExists);
	iAnchorIndex = aItemIndex;
	iActiveEndIndex = aItemIndex;
	}

EXPORT_C void CListBoxView::UpdateSelectionL(TSelectionMode aSelectionMode)
	{
	// assumes that iCurrentItemIndex has already been updated...
	if (iCurrentItemIndex == -1)   // i.e. no selection
		{
		ClearSelection();
		return;
		}
	TInt newActiveEndIndex = iCurrentItemIndex;
	switch (aSelectionMode)
		{
	case ENoSelection:
		DrawItem(iCurrentItemIndex);
		break;
	case ESingleSelection:
		{
		TInt i = 0;
		iAnchorIndex = newActiveEndIndex;
		iActiveEndIndex = newActiveEndIndex;
		SetFlags(EAnchorExists);
		TInt selectedItemIndex; 
		// deselect everything except newActiveEndIndex
		while (i < iExt->iSelectionIndexes->Count())
			{
			selectedItemIndex = (*(iExt->iSelectionIndexes))[i];
			DeselectItem(selectedItemIndex);
			}
		SelectItemL(newActiveEndIndex);
		}
		break;
	case EContiguousSelection:
		{
		TInt itemIndex;
		if (newActiveEndIndex == iActiveEndIndex)
			break;
		if (!(iFlags & EAnchorExists))
			{
			iAnchorIndex = newActiveEndIndex;
			iActiveEndIndex = newActiveEndIndex;
    		SetFlags(EAnchorExists);
			SelectRangeL(iAnchorIndex, newActiveEndIndex);
			break;
			}
		if ((newActiveEndIndex < iActiveEndIndex) && (iActiveEndIndex <= iAnchorIndex))
			SelectRangeL(iActiveEndIndex, newActiveEndIndex);
		else if ((newActiveEndIndex < iActiveEndIndex) && (iActiveEndIndex > iAnchorIndex))
			{
			itemIndex = iActiveEndIndex;
			while ((itemIndex > newActiveEndIndex) && (itemIndex > iAnchorIndex))
				{
				DeselectItem(itemIndex);
				--itemIndex;
				}
			SelectRangeL(iAnchorIndex, newActiveEndIndex);
			}
		else if ((newActiveEndIndex > iActiveEndIndex) && (iActiveEndIndex < iAnchorIndex))
			{
			itemIndex = iActiveEndIndex;
			while ((itemIndex < newActiveEndIndex) && (itemIndex < iAnchorIndex))
				{
				DeselectItem(itemIndex);
				++itemIndex;
				}
			SelectRangeL(iAnchorIndex, newActiveEndIndex);
			}
		else if ((newActiveEndIndex > iActiveEndIndex) && (iActiveEndIndex >= iAnchorIndex))
			SelectRangeL(iActiveEndIndex, newActiveEndIndex);
		iActiveEndIndex = newActiveEndIndex;
		DrawItem(iActiveEndIndex);
		}
		break;
	case EDisjointSelection:
		if (newActiveEndIndex == iActiveEndIndex)
			{
    		ClearFlags(EAnchorExists);
			ToggleItemL(newActiveEndIndex);
			break;
			}
		iAnchorIndex = newActiveEndIndex;
		iActiveEndIndex = newActiveEndIndex;
    	SetFlags(EAnchorExists);
		ToggleItemL(newActiveEndIndex);
		break;
		}
	}

EXPORT_C void CListBoxView::CalcBottomItemIndex()
/* called by listbox control when either the size of the listbox or the number of items in 
   its model changes
*/
	{
	TInt i = iTopItemIndex;
	TInt totalHeight = 0;
	TInt numberOfVisibleItems = 0;
	TInt viewHeight = iViewRect.Height();
	while ((totalHeight < viewHeight) && (ITEM_EXISTS(i)))
		{
		totalHeight += iItemHeight;
		++ i;
		++ numberOfVisibleItems;
		}
	iBottomItemIndex = Max(0, iTopItemIndex + numberOfVisibleItems - 1);
	}

EXPORT_C TInt CListBoxView::NumberOfItemsThatFitInRect(const TRect& aRect) const
	{
	if (iItemHeight == 0)
		return 0;
	return (aRect.Height() / iItemHeight);
	}

EXPORT_C TBool CListBoxView::ItemIsVisible(TInt aItemIndex) const
	{
	if ((aItemIndex == 0) && !(ITEM_EXISTS(aItemIndex)))
		return EFalse;
	return ((aItemIndex >= iTopItemIndex) && (aItemIndex <= iBottomItemIndex));
	}

EXPORT_C TPoint CListBoxView::ItemPos(TInt aItemIndex) const
	{
	return TPoint(-iHScrollOffset + iViewRect.iTl.iX, iViewRect.iTl.iY + (aItemIndex - iTopItemIndex) * iItemHeight);
	}

EXPORT_C TSize CListBoxView::ItemSize(TInt /*aItemIndex*/) const
	{
	return TSize(Max(iViewRect.Width(), DataWidth()), iItemHeight);
	}

EXPORT_C TBool CListBoxView::XYPosToItemIndex(TPoint aPosition, TInt& aItemIndex) const
	{
	// returns ETrue and sets aItemIndex to the index of the item whose bounding box contains aPosition
	// returns EFalse if no such item exists 
	TBool itemFound = EFalse;
	if (iItemHeight == 0) 
		return EFalse;
	if (iViewRect.Contains(aPosition))
		{
		// aPosition is inside the display area
		TInt yOffsetFromViewRectOrigin = aPosition.iY - iViewRect.iTl.iY;
		TInt numberOfItemsFromTheTop = yOffsetFromViewRectOrigin / iItemHeight;
		TInt itemAtSpecifiedPos = iTopItemIndex + numberOfItemsFromTheTop;
		if (ITEM_EXISTS(itemAtSpecifiedPos))
			{
			aItemIndex = itemAtSpecifiedPos;
			itemFound = ETrue;
			}
		}
	return itemFound;
	}

EXPORT_C void CListBoxView::MoveCursorL(TCursorMovement aCursorMovement, TSelectionMode aSelectionMode)
	{
	TInt pageSize = NumberOfItemsThatFitInRect(iViewRect);
	TInt numOfItems = iModel->NumberOfItems(); 
	switch (aCursorMovement)
		{
	case ECursorNextItem:
		VerticalMoveToItemL(iCurrentItemIndex + 1, aSelectionMode);
		break;
	case ECursorPreviousItem:
		VerticalMoveToItemL(iCurrentItemIndex - 1, aSelectionMode);
		break;
	case ECursorNextPage:
		VerticalMoveToItemL(iCurrentItemIndex + Min((pageSize - 1), numOfItems - 1 - iCurrentItemIndex),
							   aSelectionMode);
		break;
	case ECursorPreviousPage:
		VerticalMoveToItemL(iCurrentItemIndex - Min((pageSize - 1), iCurrentItemIndex),
						   aSelectionMode);
		break;
	case ECursorFirstItem:
		VerticalMoveToItemL(0, aSelectionMode);
		break;
	case ECursorLastItem:
		if (numOfItems > 0)
			VerticalMoveToItemL(numOfItems-1, aSelectionMode);
		break;
	default:
		break;
		};
	}

EXPORT_C void CListBoxView::VerticalMoveToItemL(TInt aTargetItemIndex, TSelectionMode aSelectionMode)
	{
	if (ITEM_EXISTS(aTargetItemIndex))
		{
		TInt oldCurrentItemIndex = iCurrentItemIndex;
		iCurrentItemIndex = aTargetItemIndex;
		if (!(ItemIsSelected(oldCurrentItemIndex)) || (aSelectionMode != ESingleSelection)) 
			DrawItem(oldCurrentItemIndex);
		if (ItemIsVisible(iCurrentItemIndex))
			{	
			UpdateSelectionL(aSelectionMode);
			return;
			}
		UpdateSelectionL(aSelectionMode);
		ScrollToMakeItemVisible(iCurrentItemIndex);
		}
	}

EXPORT_C void CListBoxView::VScrollTo(TInt aNewTopItemIndex)
	{
	if (RedrawDisabled())
		return;
	TInt oldTopItemIndex = iTopItemIndex;
	if (iTopItemIndex == aNewTopItemIndex)
		return;
	TRect minRedrawRect;
	HideMatcherCursor();
	VScrollTo(aNewTopItemIndex, minRedrawRect);
	iWin->Invalidate(minRedrawRect);
	iWin->BeginRedraw(minRedrawRect);
	if ((aNewTopItemIndex - oldTopItemIndex) == -1)
		DrawItem(aNewTopItemIndex);
	else if ((aNewTopItemIndex - oldTopItemIndex) == 1)
		DrawItem(iBottomItemIndex);
	else
		Draw(&minRedrawRect);
	iWin->EndRedraw();
	if (ItemIsVisible(iCurrentItemIndex))
		DrawMatcherCursor();
	}

EXPORT_C void CListBoxView::VScrollTo(TInt aNewTopItemIndex, TRect& aMinRedrawRect)
	{
	if (RedrawDisabled())
		return;
	if (iTopItemIndex == aNewTopItemIndex)
		return;
	TInt vScrollOffset = (aNewTopItemIndex * iItemHeight) - (iTopItemIndex * iItemHeight); 
	iWin->Scroll(iViewRect, TPoint(0, -vScrollOffset));
	if (vScrollOffset > 0)
		aMinRedrawRect.SetRect(TPoint(iViewRect.iTl.iX, iViewRect.iBr.iY-vScrollOffset), TSize(iViewRect.Width(), vScrollOffset));
	else
		aMinRedrawRect.SetRect(iViewRect.iTl, TSize(iViewRect.Width(), -vScrollOffset));
	aMinRedrawRect.Intersection(iViewRect);
	SetTopItemIndex(aNewTopItemIndex);
	}

EXPORT_C void CListBoxView::HScroll(TInt aHScrollAmount)
	{
	if (RedrawDisabled())
		return;
	if (aHScrollAmount == 0)
		return;
	TInt maxNumOfPixelsScrolledRight = DataWidth() - VisibleWidth(iViewRect);
	if (maxNumOfPixelsScrolledRight <= 0)
		return;
	TInt oldHScrollOffset = iHScrollOffset;
	iHScrollOffset += aHScrollAmount;
	iHScrollOffset = Max(0, Min(iHScrollOffset, maxNumOfPixelsScrolledRight));
	TInt actualScrollAmount = iHScrollOffset - oldHScrollOffset;
	iWin->Scroll(iViewRect, TPoint(-actualScrollAmount, 0));
	TRect redrawRect(iViewRect.iTl+TPoint(iViewRect.Width()-actualScrollAmount,0),TSize(actualScrollAmount,iViewRect.Height()));
	if (actualScrollAmount<0)
		redrawRect.SetRect(iViewRect.iTl,TSize(-actualScrollAmount,iViewRect.Height()));
	redrawRect.Intersection(iViewRect);
	iGc->Clear(redrawRect);
	iWin->Invalidate(redrawRect);
	iWin->BeginRedraw(redrawRect);
	Draw(&redrawRect);
	iWin->EndRedraw();
	}

EXPORT_C TInt CListBoxView::HScrollOffset() const
	{
	return iHScrollOffset;
	}

EXPORT_C void CListBoxView::SetHScrollOffset(TInt aHScrollOffset)
	{
	iHScrollOffset = aHScrollOffset;
	}

EXPORT_C void CListBoxView::SetItemHeight(TInt aItemHeight)
	{
	iItemHeight = aItemHeight;
	iItemDrawer->SetItemCellSize(ItemSize());
	}
	
EXPORT_C void CListBoxView::SetViewRect(const TRect& aRect)
	{
	iViewRect = aRect;
	iItemDrawer->SetViewRect(iViewRect);
	iItemDrawer->SetItemCellSize(ItemSize());
	}

EXPORT_C TRect CListBoxView::ViewRect() const
	{
	return iViewRect;
	}

EXPORT_C void CListBoxView::DrawItem(TInt aItemIndex) const
	{
	if (RedrawDisabled() || !IsVisible())
		return;
	if ((ITEM_EXISTS(aItemIndex)) && ItemIsVisible(aItemIndex))
		{
		iGc->SetClippingRect(iViewRect);
		iItemDrawer->DrawItem(aItemIndex, ItemPos(aItemIndex),
	                    	  ItemIsSelected(aItemIndex), 
	                   		  (aItemIndex == iCurrentItemIndex), iFlags & EEmphasized, iFlags & EDimmed);
		iGc->CancelClippingRect();
		}
	}
	                           
EXPORT_C void CListBoxView::Draw(const TRect* /*aClipRect*/) const
	{
	if (RedrawDisabled() || !IsVisible())
		return;
	__ASSERT_DEBUG(iModel, Panic(EEikPanicListBoxNoModel));
	if (iModel->NumberOfItems() == 0)
		iItemDrawer->ClearRect(iViewRect);
	else
		{
		TInt firstPotentialItemIndex = iTopItemIndex;
		TInt lastPotentialItemIndex = iTopItemIndex + NumberOfItemsThatFitInRect(iViewRect) - 1;
		TInt i;
		for (i = firstPotentialItemIndex; i <= lastPotentialItemIndex; i++)
			if (ITEM_EXISTS(i))
				DrawItem(i);
			else
				break;
		// clear the unused portion of the viewing area
		TRect usedPortionOfViewRect(iViewRect.iTl, TSize(iViewRect.Width(), (i - firstPotentialItemIndex) * iItemHeight));
		iGc->SetBrushColor(iBackColor);
		EikDrawUtils::ClearBetweenRects(*(iGc), iViewRect, usedPortionOfViewRect);
		// ((CListBoxView*)this)->iBottomItemIndex = i - 1;
		}
	} 
	
EXPORT_C void CListBoxView::SetTopItemIndex(TInt aNewTopItemIndex)
	{
	TBool validIndex = (aNewTopItemIndex == 0) || (ITEM_EXISTS(aNewTopItemIndex)); 
	__ASSERT_ALWAYS(validIndex, Panic(EEikPanicListBoxInvalidTopItemIndexSpecified));
	iTopItemIndex = aNewTopItemIndex;
	CalcBottomItemIndex();
	}
		
EXPORT_C void CListBoxView::SetCurrentItemIndex(TInt aItemIndex)
	{
	TBool validIndex = (aItemIndex == 0) || (ITEM_EXISTS(aItemIndex)); 
	__ASSERT_ALWAYS(validIndex, Panic(EEikPanicListBoxInvalidCurrentItemIndexSpecified));
	iCurrentItemIndex = aItemIndex;
	}

EXPORT_C TInt CListBoxView::CurrentItemIndex() const
	{
	if (ITEM_EXISTS(iCurrentItemIndex))
		return iCurrentItemIndex;
	else
		return -1;		// means there is no current item
	}

/*
EXPORT_C TInt CListBoxView::ItemHeight() const
	{
	return iItemHeight;
	}
*/

EXPORT_C TBool CListBoxView::ScrollToMakeItemVisible(TInt aItemIndex)
	{
	// return ETrue if any scrolling was done
	if (ItemIsVisible(aItemIndex) || (aItemIndex < 0))
		return EFalse;
	VScrollTo(CalcNewTopItemIndexSoItemIsVisible(aItemIndex));
	return ETrue;
	}

EXPORT_C TInt CListBoxView::CalcNewTopItemIndexSoItemIsVisible(TInt aItemIndex) const
	{
	TInt newTopItemIndex=iTopItemIndex;
	if (aItemIndex > iBottomItemIndex)
		newTopItemIndex = aItemIndex - NumberOfItemsThatFitInRect(iViewRect) + 1;
	if (aItemIndex < iTopItemIndex)
		newTopItemIndex = aItemIndex;
	return newTopItemIndex;
	}

EXPORT_C void CListBoxView::DrawMatcherCursor()
	{
	if (RedrawDisabled() || !IsVisible())
		return;
	if (!(iFlags & EHasMatcherCursor))
		return;	
	if (iModel->MatchableTextArray() == NULL)
		return;
	if (!(iFlags & EEmphasized))
		return;
	if (! ItemIsVisible(iCurrentItemIndex))
		return;
	// calculate pos and size of cursor
	TPoint cursorTl = iViewRect.iTl;
	cursorTl.iX += (-iHScrollOffset);
	const MDesCArray* matchableTextArray = iModel->MatchableTextArray();
	TPtrC des = matchableTextArray->MdcaPoint(iCurrentItemIndex);
	if (des.Length() == 0)
		return;
	TRect cursorRect(iItemDrawer->MatcherCursorRect(des, iMatcherCursorPos, (iCurrentItemIndex - iTopItemIndex) * iItemHeight));
	cursorTl += cursorRect.iTl;
	// draw the cursor
    TTextCursor cursor;
    cursor.iType = TTextCursor::ETypeRectangle;
	cursor.iWidth = cursorRect.Size().iWidth;
    cursor.iHeight = cursorRect.Size().iHeight;
    cursor.iAscent = iItemDrawer->MatcherCursorAscent();
    cursor.iFlags = 0;
    cursor.iColor = iMatcherCursorColor;
    iGroupWin->SetTextCursor(*iWin, cursorTl, cursor);
    }

EXPORT_C void CListBoxView::HideMatcherCursor()
	{
	if (!(iFlags & EHasMatcherCursor))
		return;	
    iGroupWin->CancelTextCursor(); 
	}

EXPORT_C void CListBoxView::SetMatcherCursorPos(TInt aPosWithinCurrentItem)
	{
	iMatcherCursorPos = aPosWithinCurrentItem;
	}

EXPORT_C TInt CListBoxView::MatcherCursorPos() const
	{
	return iMatcherCursorPos;
	}

EXPORT_C void CListBoxView::SetMatcherCursorColor(TRgb aColor)
	{
	iMatcherCursorColor = aColor;
	}

EXPORT_C void CListBoxView::SetMatcherCursor(TBool aMatcherCursor)
	{
	if (aMatcherCursor)
		SetFlags(EHasMatcherCursor);
	else
		ClearFlags(EHasMatcherCursor);
	}

EXPORT_C void CListBoxView::SetEmphasized(TBool aEmphasized)
	{
	if (aEmphasized)
		SetFlags(EEmphasized);
	else
		ClearFlags(EEmphasized);
	}

EXPORT_C void CListBoxView::SetDimmed(TBool aDimmed)
	{
	if (aDimmed)
		SetFlags(EDimmed);
	else
		ClearFlags(EDimmed);
	}

EXPORT_C void CListBoxView::SetDisableRedraw(TBool aDisableRedraw)
	{
	if (aDisableRedraw)
		SetFlags(EDisableRedraw);
	else
		ClearFlags(EDisableRedraw);
	}

EXPORT_C TBool CListBoxView::RedrawDisabled() const
	{
	return (iFlags & EDisableRedraw);
	}

EXPORT_C TInt CListBoxView::DataWidth() const
	{
	return iDataWidth;
	}

EXPORT_C TInt CListBoxView::VisibleWidth(const TRect& aRect) const
	{
	return aRect.Width();
	}

EXPORT_C void CListBoxView::CalcDataWidth()
	{
	// assumes that iModel and iItemDrawer are both valid 
	// calculate data width (in pixels)
	TInt i = 0;
	TInt largestItemWidth = 0;
	while (ITEM_EXISTS(i))
		{
		largestItemWidth = Max(largestItemWidth, iItemDrawer->ItemWidthInPixels(i)); 
		++ i;
		}
	iDataWidth = largestItemWidth;
	iItemDrawer->SetItemCellSize(ItemSize());
	}

EXPORT_C TRgb CListBoxView::BackColor() const
	{
	return iBackColor;
	}


// class CSnakingListBoxView

EXPORT_C CSnakingListBoxView::CSnakingListBoxView()
    {
	iColumnWidth = 100;
    }

EXPORT_C CSnakingListBoxView::~CSnakingListBoxView()
    {
    }

EXPORT_C void CSnakingListBoxView::CalcBottomItemIndex() 
	{
	TInt i = iTopItemIndex;
	iBottomItemIndex = i;
	if ((iColumnWidth == 0) || (iItemHeight == 0))
		return;
	TInt xpos = iViewRect.iTl.iX;
	TInt ypos = iViewRect.iTl.iY;	 
	TInt maxXPos = xpos + (iViewRect.Width() / iColumnWidth) * iColumnWidth;
	TInt maxYPos = ypos + ((iViewRect.Height() / iItemHeight) * iItemHeight)- iItemHeight;
	while ((xpos < maxXPos) && (ITEM_EXISTS(i+1)))
		{
		++ i;
		if (ypos == maxYPos)
			{
			ypos = iViewRect.iTl.iY;
			xpos += iColumnWidth;
			} 
		else
			ypos += iItemHeight;
		}
	if ((i > 0) && (xpos >= maxXPos))
		--i;
	iBottomItemIndex = i;
	}

EXPORT_C void CSnakingListBoxView::CalcRowAndColIndexesFromItemIndex(TInt aItemIndex, TInt& aRowIndex, TInt& aColIndex) const
	{
	// should panic if iItemHeight or iViewRect.Height() is 0
	// assumes specified item is currently visible
	TInt numOfItemsFromFirstVisibleItem = (aItemIndex - iTopItemIndex);
	TInt numOfItemsPerColumn = NumberOfItemsPerColumn();
	aColIndex = numOfItemsFromFirstVisibleItem / numOfItemsPerColumn;
	aRowIndex = numOfItemsFromFirstVisibleItem % numOfItemsPerColumn;
	}

EXPORT_C TInt CSnakingListBoxView::NumberOfItemsPerColumn() const
	{
	TInt numOfItemsPerColumn = iViewRect.Height() / iItemHeight;
	return (numOfItemsPerColumn<1? 1: numOfItemsPerColumn);
	}

EXPORT_C void CSnakingListBoxView::CalcItemIndexFromRowAndColIndexes(TInt& aItemIndex, TInt aRowIndex, TInt aColIndex) const
	{
	__ASSERT_DEBUG((aRowIndex >= 0), Panic(EEikPanicListBoxInvalidRowIndexSpecified));
	__ASSERT_DEBUG((aColIndex >= 0), Panic(EEikPanicListBoxInvalidColIndexSpecified));
	// should panic if iItemHeight is 0
	TInt numOfItemsPerColumn = NumberOfItemsPerColumn();
	aItemIndex = iTopItemIndex + ((aColIndex * numOfItemsPerColumn) + aRowIndex); 
	}

EXPORT_C TPoint CSnakingListBoxView::ItemPos(TInt aItemIndex) const
	{
	TInt rowIndex, colIndex;
	CalcRowAndColIndexesFromItemIndex(aItemIndex, rowIndex, colIndex);
	return TPoint(iViewRect.iTl.iX + colIndex * iColumnWidth, iViewRect.iTl.iY + rowIndex * iItemHeight);
	}

EXPORT_C TBool CSnakingListBoxView::XYPosToItemIndex(TPoint aPosition, TInt& aItemIndex) const
	{
	// returns ETrue and sets aItemIndex to the index of the item whose bounding box contains aPosition
	// returns EFalse if no such item exists 
	TBool itemFound = EFalse;
	if ((iColumnWidth == 0) || (iItemHeight == 0))
		return EFalse;
	if (iViewRect.Contains(aPosition))
		{
		// aPosition is inside the display area
		TInt yOffsetFromViewRectOrigin = aPosition.iY - iViewRect.iTl.iY;
		TInt xOffsetFromViewRectOrigin = aPosition.iX - iViewRect.iTl.iX;
		TInt colIndex = xOffsetFromViewRectOrigin / iColumnWidth;
		TInt rowIndex = yOffsetFromViewRectOrigin / iItemHeight;
		// now work out the item index given that we know which row and column it is in 
		TInt itemIndex;
		CalcItemIndexFromRowAndColIndexes(itemIndex, rowIndex, colIndex);
		if ((ITEM_EXISTS(itemIndex)) && (ItemIsVisible(itemIndex)))
			{
			aItemIndex = itemIndex;
			itemFound = ETrue;
			}
		}
	return itemFound;
	}

EXPORT_C void CSnakingListBoxView::MoveCursorL(TCursorMovement aCursorMovement, TSelectionMode aSelectionMode)
	{
	if (aCursorMovement == ECursorNextColumn)
		MoveToNextColumnL(aSelectionMode);
	else if (aCursorMovement == ECursorPreviousColumn)
		MoveToPreviousColumnL(aSelectionMode);
	else 
		CListBoxView::MoveCursorL(aCursorMovement, aSelectionMode);
	}

EXPORT_C TSize CSnakingListBoxView::ItemSize(TInt /*aItemIndex*/) const
	{
	return TSize(iColumnWidth, iItemHeight);
	}

EXPORT_C void CSnakingListBoxView::SetColumnWidth(TInt aColumnWidth)
	{
	TBool validColumnWidth = (aColumnWidth > 0);
	__ASSERT_ALWAYS(validColumnWidth, Panic(EEikPanicListBoxInvalidColumnWidthSpecified));
	iColumnWidth = aColumnWidth;
	CalcBottomItemIndex();
	iItemDrawer->SetItemCellSize(ItemSize());
	}

EXPORT_C TInt CSnakingListBoxView::NumberOfItemsThatFitInRect(const TRect& aRect) const
	{
	if ((iItemHeight == 0) || (iColumnWidth == 0))
		return 0;
	TInt numOfRows = aRect.Height() / iItemHeight;
	TInt numOfCols = aRect.Width() / iColumnWidth;
	return (numOfRows * numOfCols);
	}

EXPORT_C void CSnakingListBoxView::ClearUnusedItemSpace(TInt aStartItemIndex, TInt aEndItemIndex) const
	{
	TRect blankRect;
	for (TInt i = aStartItemIndex; i <= aEndItemIndex; i++)
		{
		blankRect.SetRect(ItemPos(i), ItemSize());
		iItemDrawer->ClearRect(blankRect);
		}
	}

EXPORT_C void CSnakingListBoxView::MoveToPreviousColumnL(TSelectionMode aSelectionMode)
	{
	TInt oldCurrentItemIndex = iCurrentItemIndex;
	TInt indexOfNewCurrentItem = iCurrentItemIndex;
	// TInt indexOfNewTopItem = iTopItemIndex;
	TInt numOfItemsPerColumn = NumberOfItemsPerColumn();
	indexOfNewCurrentItem = iCurrentItemIndex - numOfItemsPerColumn;
	if (indexOfNewCurrentItem < 0) 
		return;
	if (ITEM_EXISTS(indexOfNewCurrentItem))
		{
		iCurrentItemIndex = indexOfNewCurrentItem;
		if (!(ItemIsSelected(oldCurrentItemIndex)) || (aSelectionMode != ESingleSelection)) 
			DrawItem(oldCurrentItemIndex);
		if (iCurrentItemIndex < iTopItemIndex)
			{
			UpdateSelectionL(aSelectionMode);
			ScrollToMakeItemVisible(iCurrentItemIndex);
			}
		else
			{
			ScrollToMakeItemVisible(iCurrentItemIndex);
			UpdateSelectionL(aSelectionMode);
			}
		}
	}

EXPORT_C void CSnakingListBoxView::MoveToNextColumnL(TSelectionMode aSelectionMode)
	{
	TInt oldCurrentItemIndex = iCurrentItemIndex;
	TInt indexOfNewCurrentItem = iCurrentItemIndex;
	TInt numOfItemsPerColumn = NumberOfItemsPerColumn();
	indexOfNewCurrentItem = iCurrentItemIndex + numOfItemsPerColumn;
	if (ITEM_EXISTS(indexOfNewCurrentItem) == EFalse)
		{
		TInt indexOfItemAtTopOfTargetCol = (indexOfNewCurrentItem / numOfItemsPerColumn) * numOfItemsPerColumn;
		if (! (ITEM_EXISTS(indexOfItemAtTopOfTargetCol)))
			return;		// no more columns of data to the right, so just return
		// there are more columns of data available to the right of the current column
		// since there is no item at the expected position, look for an item further up in the same column
		TInt i = indexOfNewCurrentItem;
		while (ITEM_EXISTS(i) == EFalse)
			--i;
		indexOfNewCurrentItem = i;
		}
	iCurrentItemIndex = indexOfNewCurrentItem;
	if (!(ItemIsSelected(oldCurrentItemIndex)) || (aSelectionMode != ESingleSelection)) 
		DrawItem(oldCurrentItemIndex);
	if (iCurrentItemIndex > iBottomItemIndex)
		{
		UpdateSelectionL(aSelectionMode);
		ScrollToMakeItemVisible(iCurrentItemIndex);
		}
	else 
		{
		ScrollToMakeItemVisible(iCurrentItemIndex);
		UpdateSelectionL(aSelectionMode);
		}
	}

EXPORT_C TInt CSnakingListBoxView::CalcNewTopItemIndexSoItemIsVisible(TInt aItemIndex) const
	{
	if (aItemIndex == iTopItemIndex)
		return iTopItemIndex;
	TInt newTopItemIndex = iTopItemIndex;
	TInt numOfItemsPerColumn = NumberOfItemsPerColumn();
	TInt colIndexOfTargetItem = aItemIndex / numOfItemsPerColumn;
	TInt numOfColsThatFitInViewRect = VisibleWidth(iViewRect);
	if (aItemIndex < iTopItemIndex)
		newTopItemIndex = colIndexOfTargetItem * numOfItemsPerColumn;
	else if (aItemIndex > iBottomItemIndex)
		{
		TInt colIndexOfNewBottomItem = colIndexOfTargetItem;
		TInt colIndexOfNewTopItem = colIndexOfNewBottomItem - (numOfColsThatFitInViewRect - 1);
		newTopItemIndex = colIndexOfNewTopItem * numOfItemsPerColumn;
		}
	return newTopItemIndex;
	}

EXPORT_C void CSnakingListBoxView::HScroll(TInt aHScrollAmount)
	{
	// note: aHScrollAmount is assumed to be specified in terms of number of columns; 
	//       a negative value ==> we need to scroll view contents to the right
	if ((aHScrollAmount == 0) || (iItemHeight == 0))
		return;
	if (RedrawDisabled())
		return;
	iHScrollOffset += aHScrollAmount;
	TInt numOfColsThatFitInViewRect = VisibleWidth(iViewRect);
	TInt numOfItemsPerColumn = NumberOfItemsPerColumn();
	if (Abs(aHScrollAmount) >= numOfColsThatFitInViewRect)
		{
		iTopItemIndex = iTopItemIndex + (numOfItemsPerColumn * aHScrollAmount);
		CalcBottomItemIndex();
		Draw();
		}
	else
		{
		TInt oldTopItemIndex = iTopItemIndex;
		TInt usedViewRectPortionWidth = VisibleWidth(iViewRect) * iColumnWidth;
		TInt usedViewRectPortionHeight = (iViewRect.Height() / iItemHeight) * iItemHeight;
		TRect usedPortionOfViewRect(iViewRect.iTl, TSize(usedViewRectPortionWidth, usedViewRectPortionHeight));
		iWin->Scroll(usedPortionOfViewRect, TPoint(-aHScrollAmount * iColumnWidth, 0));
		// iWin->Scroll(iViewRect, TPoint(-aHScrollAmount * iColumnWidth, 0));
		iTopItemIndex = iTopItemIndex + (numOfItemsPerColumn * aHScrollAmount);
		CalcBottomItemIndex();
		TInt colIndexOfOldTopItem = oldTopItemIndex / numOfItemsPerColumn;
		TRect redrawRect;
		TInt startColIndex;
		TInt endColIndex;
		if (aHScrollAmount > 0)
			{
			redrawRect.SetRect(TPoint((numOfColsThatFitInViewRect - aHScrollAmount) * iColumnWidth + iViewRect.iTl.iX, iViewRect.iTl.iY), iViewRect.iBr);
			startColIndex = colIndexOfOldTopItem + numOfColsThatFitInViewRect;
			endColIndex = startColIndex + aHScrollAmount - 1;
			}
		else
			{
			redrawRect.SetRect(iViewRect.iTl, TPoint(Abs(aHScrollAmount) * iColumnWidth + iViewRect.iTl.iX, iViewRect.iBr.iY));
			startColIndex = colIndexOfOldTopItem + aHScrollAmount;
			endColIndex = colIndexOfOldTopItem - 1;
			}
		redrawRect.Intersection(iViewRect);
		iWin->Invalidate(iViewRect);
		iWin->BeginRedraw(iViewRect);
		DrawColumnRange(startColIndex, endColIndex);
		iWin->EndRedraw();
		}
	}

EXPORT_C void CSnakingListBoxView::CalcDataWidth() 
	{
	// assumes that iModel and iItemDrawer are both valid
	// assumes that the model knows how many items there are...
	// calculate data width (in columns)
	iDataWidth = 0;
	if ((iViewRect.Height() == 0) || (iItemHeight == 0))
		return;
	TInt numOfItemsPerColumn = NumberOfItemsPerColumn();
	iDataWidth = iModel->NumberOfItems() / numOfItemsPerColumn;
	if (iModel->NumberOfItems() % numOfItemsPerColumn)
		iDataWidth++;	// add on one column if remainder != 0
	}

EXPORT_C TInt CSnakingListBoxView::VisibleWidth(const TRect& aRect) const
	{
	return aRect.Width() / iColumnWidth;
	}

EXPORT_C void CSnakingListBoxView::Draw(const TRect* aClipRect) const
	{
	// temporary: some code sharing with CListBoxView::Draw() is needed!
	if (RedrawDisabled())
		return;
	__ASSERT_DEBUG(iModel, Panic(EEikPanicListBoxNoModel));
	if (iItemHeight == 0)
		return;
	if (! aClipRect)
		aClipRect = &iViewRect;
	iGc->SetClippingRect(*aClipRect);
	if (iModel->NumberOfItems() == 0)
		iItemDrawer->ClearRect(iViewRect);
	else
		{
		TInt firstPotentialItemIndex = iTopItemIndex;
		TInt lastPotentialItemIndex = iTopItemIndex + NumberOfItemsThatFitInRect(iViewRect) - 1;
		TInt i;
		for (i = firstPotentialItemIndex; i <= lastPotentialItemIndex; i++)
			if (ITEM_EXISTS(i))
				DrawItem(i);
			else
				{
				iGc->SetClippingRect(*aClipRect);
				ClearUnusedItemSpace(i, lastPotentialItemIndex);
				break;
				}
		// clear the unused portion of the viewing area
		TInt usedViewRectPortionWidth = VisibleWidth(iViewRect) * iColumnWidth;
		TInt usedViewRectPortionHeight = (iViewRect.Height() / iItemHeight) * iItemHeight;
		TRect usedPortionOfViewRect(iViewRect.iTl, TSize(usedViewRectPortionWidth, usedViewRectPortionHeight));
		iGc->SetBrushColor(BackColor());
		EikDrawUtils::ClearBetweenRects(*(iGc), iViewRect, usedPortionOfViewRect);
		// ((CSnakingListBoxView*)this)->iBottomItemIndex = i - 1;
		}
	iGc->CancelClippingRect();
	} 

EXPORT_C void CSnakingListBoxView::DrawItemRange(TInt aStartItemIndex, TInt aEndItemIndex) const
	{
	// temporary: some code sharing with CSnakingListBoxView::Draw() is needed!
	if (RedrawDisabled())
		return;
	if (iItemHeight == 0)
		return;
	TInt i;
	for (i = aStartItemIndex; i <= aEndItemIndex; i++)
		if (ITEM_EXISTS(i))
			DrawItem(i);
		else
			{
			ClearUnusedItemSpace(i, aEndItemIndex);
			break;
			}
	TInt usedViewRectPortionWidth = VisibleWidth(iViewRect) * iColumnWidth;
	TInt usedViewRectPortionHeight = (iViewRect.Height() / iItemHeight) * iItemHeight;
	TRect usedPortionOfViewRect(iViewRect.iTl, TSize(usedViewRectPortionWidth, usedViewRectPortionHeight));
	iGc->SetBrushColor(BackColor());
	EikDrawUtils::ClearBetweenRects(*(iGc), iViewRect, usedPortionOfViewRect);
	}

EXPORT_C void CSnakingListBoxView::DrawColumnRange(TInt aStartColIndex, TInt aEndColIndex) const
	{
	if (RedrawDisabled())
		return;
	if (iItemHeight == 0)
		return;
	TInt numOfItemsPerColumn = NumberOfItemsPerColumn();
	TInt startItemIndex = aStartColIndex * numOfItemsPerColumn;
	TInt endItemIndex = aEndColIndex * numOfItemsPerColumn + numOfItemsPerColumn - 1;
	DrawItemRange(startItemIndex, endItemIndex);
	}

EXPORT_C void CSnakingListBoxView::SetTopItemIndex(TInt aNewTopItemIndex)
	{
	if (iViewRect.Height() == 0)
		return;
	CListBoxView::SetTopItemIndex(aNewTopItemIndex);
	UpdateHScrollOffsetBasedOnTopItemIndex();
	}

EXPORT_C void CSnakingListBoxView::SetItemHeight(TInt aItemHeight)
	{
	CListBoxView::SetItemHeight(aItemHeight);
	CalcBottomItemIndex();
	UpdateHScrollOffsetBasedOnTopItemIndex();
	}

EXPORT_C void CSnakingListBoxView::UpdateHScrollOffsetBasedOnTopItemIndex()
	{
	TInt numOfItemsPerColumn = NumberOfItemsPerColumn();
	iHScrollOffset = iTopItemIndex / numOfItemsPerColumn;
	}

EXPORT_C TBool CSnakingListBoxView::ScrollToMakeItemVisible(TInt aItemIndex)
	{
	if ((RedrawDisabled()) || (ItemIsVisible(aItemIndex)) || (iViewRect.Height() == 0))
		return EFalse;
	HideMatcherCursor();
	TInt amountToScroll = CalculateHScrollOffsetSoItemIsVisible(aItemIndex);
	HScroll(amountToScroll);
	DrawMatcherCursor();
	return ETrue;
	}

EXPORT_C TInt CSnakingListBoxView::CalculateHScrollOffsetSoItemIsVisible(TInt aItemIndex) 
	{
	// returns the number of cols that we need to scroll, 0 if no scrolling is needed
	TInt newTopItemIndex = CalcNewTopItemIndexSoItemIsVisible(aItemIndex);
	TInt numOfItemsPerColumn = NumberOfItemsPerColumn();
	TInt oldHScrollOffset = iHScrollOffset;
	TInt newHScrollOffset = newTopItemIndex / numOfItemsPerColumn;
	TInt numOfColsToScroll = newHScrollOffset - oldHScrollOffset;
	return numOfColsToScroll;
	}
