// EIKDTLBX.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//
 
#include <barsread.h>
#include <eikenv.h>
#include <eikhlbv.h>
#include <eikdtlbi.h>
#include <eikdtlbx.h>
#include <eikdtlbm.h>
#include <eiklbx.pan>
#include <eikon.rsg>
#include <eikon.mbg>

#define ITEM_EXISTS(x) ((x>-1) && (x<DtlModel()->NumberOfItems()))

//
// class CEikDirectoryTreeListBox
//

EXPORT_C CEikDirectoryTreeListBox::CEikDirectoryTreeListBox()
    {
    }

EXPORT_C CEikDirectoryTreeListBox::~CEikDirectoryTreeListBox()
    {
	if (iDtlbxDestroyed)
		*iDtlbxDestroyed=ETrue;	
    }

EXPORT_C void CEikDirectoryTreeListBox::ConstructL(const CCoeControl* aParent,TInt aFlags,TInt aModelFlags,TInt aNumVisibleLines,TInt aWidthInPixels)
	{
	aFlags&=~EIncrementalMatching; // don't want incremental matching
	iListBoxFlags=aFlags;
	BaseConstructL(aModelFlags);
	DtlModel()->ConstructL();
	iRequiredHeightInNumOfItems=aNumVisibleLines;
	iSize.iWidth=aWidthInPixels;
	CEikListBox::ConstructL(aParent,aFlags);
	}

EXPORT_C void CEikDirectoryTreeListBox::ConstructFromResourceL(TResourceReader& aReader)
	{
	iListBoxFlags=aReader.ReadInt16();
	iListBoxFlags&=~EIncrementalMatching; // don't want incremental matching
	const TInt modelFlags=aReader.ReadInt16();
	iRequiredHeightInNumOfItems=aReader.ReadInt16();
	iSize.iWidth=aReader.ReadInt16();
	BaseConstructL(modelFlags);
	DtlModel()->ConstructL();
	CreateViewL();
	}

void CEikDirectoryTreeListBox::BaseConstructL(TInt aModelFlags)
	{
	iModel=new(ELeave) CDirectoryTreeListBoxModel(iEikonEnv->FsSession(),aModelFlags);
	CArrayPtrFlat<CFbsBitmap>* bitmaps=new(ELeave) CArrayPtrFlat<CFbsBitmap>(2);
	CleanupStack::PushL(bitmaps);
	bitmaps->AppendL(iEikonEnv->CreateBitmapL(TPtrC(),EMbmEikonFileopen));
	bitmaps->AppendL(iEikonEnv->CreateBitmapL(TPtrC(),EMbmEikonFileclsd));
	iItemDrawer=new(ELeave) CDirectoryTreeListItemDrawer((CDirectoryTreeListBoxModel*)iModel,iEikonEnv->NormalFont(),bitmaps);
	CleanupStack::Pop(); // bitmaps
	}

EXPORT_C TSize CEikDirectoryTreeListBox::MinimumSize()
	{
	TSize size;
	size.iWidth=iSize.iWidth;
	size.iHeight=(2*VerticalMargin())+(iRequiredHeightInNumOfItems*(iEikonEnv->NormalFont()->HeightInPixels()+VerticalInterItemGap()));
	if (!(iListBoxFlags&EScrollBarSizeExcluded) && iSBFrame && iSBFrame->HScrollBarVisibility()!=CEikScrollBarFrame::EOff)
		size.iHeight+=CEikScrollBar::EScrollbarWidth;
	size+=iBorder.SizeDelta();
	return size;
	}

TInt CEikDirectoryTreeListBox::NewItemIndex(const TDesC& aItemPath) const
	{
	const TInt count=DtlModel()->NumberOfItems();
	for (TInt ii=0;ii<count;ii++)
		if (DtlModel()->FullPath(ii)==aItemPath)
			return ii;
	return -1;
	}

EXPORT_C void CEikDirectoryTreeListBox::UpdateL()
	{
	TFileName* path=new(ELeave) TFileName;
	CleanupStack::PushL(path);
	TParse* parse=new(ELeave) TParse;
	CleanupStack::PushL(parse);
	const TInt current=CurrentItemIndex();
	*path=DtlModel()->FullPath(current);
	// must get 'old' path before updating as it may not exist after
	DtlModel()->UpdateL();
	View()->CalcBottomItemIndex();
	if (current==-1 && !path->Length())
		{
		if (DtlModel()->NumberOfItems()>0)
			SetCurrentItemIndex(0);
		}
	else if (ITEM_EXISTS(current) && DtlModel()->FullPath(current)==*path)
		; // current item is correct
	else
		{
		TInt index=NewItemIndex(*path);
		if (index==-1 && path->Length())
			{
			User::LeaveIfError(parse->Set(*path,NULL,NULL));
			while (index==-1 && !parse->PopDir())
				index=NewItemIndex(parse->DriveAndPath());
			}
		if (index==-1 && (DtlModel()->NumberOfItems()>0))
			index=0;
		SetCurrentItemIndex(index);
		}
	CleanupStack::PopAndDestroy(2); // path, parse
	UpdateScrollBarsL();
	DrawNow();
	}

EXPORT_C void CEikDirectoryTreeListBox::SetCurrentItemPathL(const TDesC& aPath)
	{
	DtlModel()->UpdateL();
	iView->CalcBottomItemIndex();
	TParse parse;
	User::LeaveIfError(iEikonEnv->FsSession().Parse(aPath,parse));
	TInt index=0;
	SetCurrentItemName(parse.Drive().Left(1),0,index);
	TInt numDirs=0;
	while (!parse.PopDir())
		++numDirs;
	const TInt count=numDirs;
	TInt foundLength=3; // drive letter, ":" and "\"
	for (TInt ii=0;ii<count;ii++)
		{
		User::LeaveIfError(iEikonEnv->FsSession().Parse(aPath,parse));
		TInt num=numDirs-ii-1;
		while (num>0)
			{
			parse.PopDir();
			--num;
			}
		TPtrC remainder=parse.DriveAndPath();
		const TInt length=remainder.Length();
		TFileName name=remainder.Right(length-foundLength);
		foundLength+=name.Length();
		name.Delete(name.Length()-1,1); // delete trailing "\"
		DtlModel()->ExpandItemL(index);
		iView->CalcBottomItemIndex();
		SetCurrentItemName(name,ii+1,index);
		}
	if (count==0)
		iView->CalcBottomItemIndex();
	const TInt topItemIndex=TopItemIndex();
	const TInt bottomItemIndex=BottomItemIndex();
	if (index>bottomItemIndex)
		SetTopItemIndex(index+topItemIndex-bottomItemIndex);
	if (index<topItemIndex)
		SetTopItemIndex(index);
	}

void CEikDirectoryTreeListBox::SetCurrentItemName(const TDesC& aName,TInt aItemLevel,TInt& aSearchFromIndex)
	{
	TFileName name=aName;
	name.LowerCase();
	const TInt count=DtlModel()->NumberOfItems();
	for (TInt ii=aSearchFromIndex;ii<count;ii++)
		{
		TFileName itemName=DtlModel()->ItemText(ii);
		itemName.LowerCase();
		if (DtlModel()->Item(ii)->Level()==aItemLevel && itemName==name)
			{
			HlView()->SetCurrentItemIndexOnly(ii);
			aSearchFromIndex=ii;
			break;
			}
		}
	}

EXPORT_C CDirectoryTreeListBoxModel* CEikDirectoryTreeListBox::DtlModel() const
	{
	return (CDirectoryTreeListBoxModel*)iModel;
	}

EXPORT_C TKeyResponse CEikDirectoryTreeListBox::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
	{
	if (aKeyEvent.iCode==EKeyTab && !(aKeyEvent.iModifiers&(EModifierShift|EModifierCtrl|EModifierFunc)))
		{
		TRAPD(err,CEikHierarchicalListBox::OfferKeyEventL(aKeyEvent,aType));
		CheckForErrorFromFileServerL(err);
		}
	else
		CEikListBox::OfferKeyEventL(aKeyEvent,aType);
	return EKeyWasConsumed;
	}

EXPORT_C void CEikDirectoryTreeListBox::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{
	TBool destroyed = EFalse;
	iDtlbxDestroyed=&destroyed;
	TRAPD(err,CEikHierarchicalListBox::HandlePointerEventL(aPointerEvent));
	if (destroyed)
		return;
	iDtlbxDestroyed=NULL;
	CheckForErrorFromFileServerL(err);
	}               

void CEikDirectoryTreeListBox::CheckForErrorFromFileServerL(TInt aErrorCode)
	{
	if (aErrorCode==KErrNone)
		return;
	if (aErrorCode==KErrNotReady)
		iEikonEnv->InfoMsg(R_EIK_TBUF_DISK_NOT_PRESENT);
	else if (aErrorCode==KErrCorrupt)
		iEikonEnv->InfoMsg(R_EIK_TBUF_CANNOT_BE_READ);
	else
		User::LeaveIfError(aErrorCode);
	}

EXPORT_C void CEikDirectoryTreeListBox::CreateListL(TDriveNumber* aDrive)
	{
	Reset();
	DtlModel()->CreateListForSpecifiedDriveL(aDrive);
	iView->CalcBottomItemIndex();
	}
