// EIKUFSEL.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <eikufsel.h>
#include <eikfsel.h> 
#include <f32file.h>
#include <eikfutil.h>
#include <eikenv.h>
#include <bamatch.h>
#include <badesca.h>
#include <barsread.h>
#include <eiktxlbm.h>
#include <eikchlst.h>
#include <eikchlst.hrh>
#include <eikon.rsg>

#define KROMDrive 'Z'
const TInt KFileNameArrayGranularity = 5;
const TInt KSpecialChoiceListMessageSize = 32;
const TInt KMaxFileNameLen = KMaxFileName;
const TInt KDriveDelimiterLen = 2;
const TInt KDriveLetterLen = 1;

#define KTemplateExtension _L(".t")
#define KTemplateExtensionMatch _L(".t*")

GLDEF_C void Panic(TInt aPanic)
    {
    User::Panic(_L("EIKON-UFSEL"),aPanic);
    }

LOCAL_C void CheckLanguage(TFindFile& aFf, TFileName& aFilename, const TDesC& aDriveAndPath)
	{
	TInt err = aFf.FindByDir(aFilename,aDriveAndPath);
	if (err)
		{
		aFilename.Append(KTemplateExtension);
		TBuf<2> lang;
		TBuf<1> buf;
		TInt langNum=STATIC_CAST(TInt,User::Language());
		if (langNum<10)
			buf.Num(0);
		lang.Num(langNum);
		aFilename.Append(buf);
		aFilename.Append(lang);
		err = aFf.FindByDir(aFilename,aDriveAndPath);
		if (err==KErrNone)
			{
			TParsePtrC parse(aFf.File());
			aFilename=parse.Drive();
			aFilename.Append(parse.NameAndExt());
			}
		}
	}

LOCAL_C void StripLanguage(TFileName& aFileName)
	{
	TParsePtrC parse(aFileName);
	TFileName filename=parse.Ext();
	if (filename.Length()==4)
		// could have a .tNN ext
		{
		if (filename.Match(KTemplateExtensionMatch)==0)
			{
			TLex lex(filename);
			lex.Inc(2);
			lex.Mark();
			TInt8 lang;
			lex.Val(lang);
			if (lang==STATIC_CAST(TInt8,User::Language()))
				{
				filename=aFileName.Left(aFileName.Length()-4);
				aFileName=filename;
				}
			}
		}
	}

EXPORT_C CEikUnifiedFileNameSelector::CEikUnifiedFileNameSelector()
	: iIncludeROM(EFalse)
	{
	__DECLARE_NAME(_S("CEikUnifiedFileNameSelector"));
	}

EXPORT_C void CEikUnifiedFileNameSelector::SetROMIncluded(TBool aIncludeROM)
	{
	iIncludeROM = aIncludeROM;
	}

EXPORT_C TBool CEikUnifiedFileNameSelector::ROMIncluded()
	{
	return iIncludeROM;
	}

EXPORT_C TFileName CEikUnifiedFileNameSelector::FullName() const
    {
	if (!iArray || (iFileSelectorFlags & ESpecifiedFolderIsEmpty))
        return TFileName();
	TParsePtrC parse1(iRef);
	TParse parse2;
	TFileName currentItem = (MultipleFilesSelected()) ? (*iSelectionArray)[0] : iArray->MdcaPoint(iCurrentItem);
	ParseToFront(currentItem);
	TPtrC path(parse1.Path());
	parse2.Set(currentItem, &path, NULL);
    return parse2.FullName();
	}

EXPORT_C void CEikUnifiedFileNameSelector::GetFullNamesOfSelectedFilesL(CDesCArray* aFullNames) const
	{
	aFullNames->Reset();
	if (! MultipleFilesSelected())
		aFullNames->AppendL(FullName());
	else
		{
		const TInt count = iSelectionArray->Count();
		TParsePtrC parse1(iRef);
		TParse parse2;
		TFileName selectedItem;
		for (TInt i=0; i < count; i++)
			{
			selectedItem = (*iSelectionArray)[i];
			ParseToFront(selectedItem);
			TPtrC driveAndPath(parse1.DriveAndPath());
			parse2.Set(selectedItem, &driveAndPath, NULL);
			aFullNames->AppendL(parse2.FullName());
			}
		}
	}

EXPORT_C void CEikUnifiedFileNameSelector::SetSelectedFileNamesL(CDesCArray* aFileNames)
	{
	// Takes an array of filenames and converts them into unified file names
	// and inserts them onto iSelectionArray
	const TInt selectionCount = aFileNames->Count();
	iSelectionArray->Reset();
	TFileName filename;
	for (TInt i = 0; i < selectionCount; i++)
		{
		filename = (*aFileNames)[i];
		StripLanguage(filename);
		ParseToRear(filename);
		iSelectionArray->AppendL(filename);
		}
	}

EXPORT_C void CEikUnifiedFileNameSelector::BuildFileNameArrayL(const TParse& aParse, MDesCArray* aFileNames)
// build list of filenames
// Add file if it has no .tNN type extension.
// if there is a .tNN extension check that it's the correct language code, if it
// is then append it, if not then don't.
    {
	enum TState
		{
		EFullName,
		ENoExtension,
		EIgnore
		};
	TInt endOfArray=aFileNames->MdcaCount();
	CEikFileNameSelector::BuildFileNameArrayL(aParse, aFileNames);
	TFileName filename;
	TBuf<2> drive=aParse.Drive();
	TInt count=aFileNames->MdcaCount();
	for (TInt i=count; i > endOfArray;)
		{
		i--;
		TParsePtrC parse(aFileNames->MdcaPoint(i));
		TState state=EFullName;
		filename=parse.Ext();
		if (filename.Length()==4)
			// could have a .tNN ext
			{
			if (filename.Match(KTemplateExtensionMatch)==0)
				{
				TLex lex(filename);
				lex.Inc(2);
				lex.Mark();
				TInt8 lang;
				lex.Val(lang);
				if (lang!=STATIC_CAST(TInt8,User::Language()))
					state=EIgnore;
				else
					{
					state=ENoExtension;
					filename=drive;
					filename.Append(parse.Name());
					ParseToRear(filename);
					// check this drive to see if there is a no ext file of the same name
					TInt ii=0;
					for (ii=count; ii > i;)
						{
						ii--;
						TInt comp=filename.CompareF(aFileNames->MdcaPoint(ii));
						if (comp==0)
							{
							state=EIgnore;
							break;
							}
						}
					filename=parse.Name();
					for (ii=i; ii > endOfArray;)
						{
						ii--;
						TInt comp=filename.CompareF(aFileNames->MdcaPoint(ii));
						if (comp==0)
							{
							state=EIgnore;
							break;
							}
						}
					}
				}
			}
		filename=drive;
		switch (state)
			{
		case EFullName:
			filename.Append(aFileNames->MdcaPoint(i));
			break;
		case ENoExtension:
			filename.Append(parse.Name());
			break;
		case EIgnore:
			STATIC_CAST(CDesCArray*,aFileNames)->Delete(i);
			count--;
			continue;
			}
		ParseToRear(filename);
		STATIC_CAST(CDesCArray*,aFileNames)->Delete(i);
		STATIC_CAST(CDesCArray*,aFileNames)->InsertL(i,filename);
		}
	}

EXPORT_C void CEikUnifiedFileNameSelector::DoCreatePopoutL()
	{
	delete iPopoutListBox;
	iPopoutListBox = NULL;
	iPopoutListBox = new(ELeave) CEikFileSelectorListBox();
	TInt listBoxFlags = CEikListBox::EPopout;
	if ((iChoiceListFlags & EEikChlistIncrementalMatching) && (! MultipleSelectionAllowed()))
		listBoxFlags |= CEikListBox::EIncrementalMatching;
	if (MultipleSelectionAllowed())
		listBoxFlags |= CEikListBox::EMultipleSelection;
	PopoutTextListBox()->ConstructL(NULL, listBoxFlags);
	iPopoutListBox->CreateScrollBarFrameL();
	iPopoutListBox->ScrollBarFrame()->SetScrollBarVisibilityL(CEikScrollBarFrame::EOff, CEikScrollBarFrame::EAuto);
	iPopoutListBox->SetObserver(this);
    TPoint choicePosScreenCoords=PositionRelativeToScreen();
	iEikonEnv->AddWindowShadow(iPopoutListBox);
	TParse* parse = new(ELeave) TParse;
	CleanupStack::PushL(parse);
	if (!MultipleFilesSelected())
		PopoutTextListBox()->Model()->SetItemTextArray(iArray);
	else
		{
		delete iTempFileNameArray;
		iTempFileNameArray=NULL;
		iTempFileNameArray  = new(ELeave) CDesCArrayFlat(KFileNameArrayGranularity); 
		TryBuildFileNameArrayL(FullName(), iTempFileNameArray);
		PopoutTextListBox()->Model()->SetItemTextArray(iTempFileNameArray);
		}
	PopoutTextListBox()->Model()->SetOwnershipType(ELbmDoesNotOwnItemArray);
	const TInt listBoxWidth=Size().iWidth;
	TRect listBoxRect(TPoint(choicePosScreenCoords.iX, 0), TSize(listBoxWidth, 0));
	iPopoutListBox->CalculatePopoutRect(iCurrentItem, choicePosScreenCoords.iY, listBoxRect);
	iPopoutListBox->SetExtentL(TPoint(choicePosScreenCoords.iX, listBoxRect.iTl.iY), listBoxRect.Size());
	TInt currentItemIndex = iCurrentItem;
	CArrayFix<TInt>* selectionIndexes = NULL;
	if (MultipleSelectionAllowed())
		{
		selectionIndexes = new(ELeave) CArrayFixFlat<TInt>(5);
		CleanupStack::PushL(selectionIndexes);
		if (MultipleFilesSelected())
			{
			TInt fileListSize = iTempFileNameArray->Count();
			TInt selectionCount = iSelectionArray->Count();

			for (TInt i = 0; i < selectionCount; i++)
				for (TInt j = 0; j < fileListSize; j++)
					{
					if ((*iSelectionArray)[i] == (*iTempFileNameArray)[j])
						{
						selectionIndexes->AppendL(j);
						break;
						}
					}
			}
		else if (! (iFileSelectorFlags & ESpecifiedFolderIsEmpty))
			selectionIndexes->AppendL(currentItemIndex);
		iPopoutListBox->SetSelectionIndexesL(selectionIndexes);
		CleanupStack::PopAndDestroy(); // selectionIndexes
		}
	CleanupStack::PopAndDestroy();	// get rid of "parse"
	iPopoutListBox->SetCurrentItemIndex(currentItemIndex);
	if (MultipleSelectionAllowed())
		iPopoutListBox->View()->SetAnchor(currentItemIndex);
	// now update (if ncessary) top item index so that current item index is visible 
	if (!(iPopoutListBox->View()->ItemIsVisible(iCurrentItem)))
		iPopoutListBox->SetTopItemIndex(iPopoutListBox->View()->CalcNewTopItemIndexSoItemIsVisible(iCurrentItem));
    iEikonEnv->AddDialogLikeControlToStackL(iPopoutListBox);
	iPopoutListBox->ActivateL();
	iPopoutListBox->HandleItemAdditionL();
	}

EXPORT_C void CEikUnifiedFileNameSelector::SetFullNameL(const TDesC& aFullName)
    {
  	iCurrentItem = 0;
	ClearFlags(ESpecifiedFolderIsEmpty);
    iRef = aFullName;
    if (iArray)
        DesCArray()->Reset();
    else
        iArray = new(ELeave) CDesCArrayFlat(KFileNameArrayGranularity); 
	ResetMatchBuf();
	if (MultipleFilesSelected())
		{
		DesCArray()->Reset();
		TBuf<KSpecialChoiceListMessageSize> multipleFilesSelectedMsg;
		iCoeEnv->ReadResource(multipleFilesSelectedMsg, R_EIK_TBUF_MULTIPLE_FILES_DISPLAY);
		SpecialMessage(multipleFilesSelectedMsg);
		return;
		}
    TryBuildFileNameArrayL(aFullName, NULL);
    if (!iArray->MdcaCount())
        {
		SetFlags(ESpecifiedFolderIsEmpty);
		TBuf<KSpecialChoiceListMessageSize> noFile;
		iCoeEnv->ReadResource(noFile, R_EIK_TBUF_NO_FILES_DISPLAY);
		SpecialMessage(noFile);
		return;
        }
	iChoiceListFlags |= EEikChlistIncrementalMatching;
	if (!iMatchBuf)
		iMatchBuf=new(ELeave)RIncrMatcherBuf<KEikMaxMatchingBufLength>;
	TInt err = TryFindCurrentItemL(aFullName);
	if(err != KErrNotFound)
		{
		iCurrentItem = err;
		ContentHasChangedL();
		}
	if (IsFocused())
		DisplayCursor();
	}

TInt CEikUnifiedFileNameSelector::TryFindCurrentItemL(const TDesC& aFullName)
	{
	const TInt fileNameListSize=iArray->MdcaCount();
	TParsePtrC parse(aFullName);
	TFileName filename = parse.Drive();
	filename.Append(parse.NameAndExt());
	StripLanguage(filename);
	ParseToRear(filename);
    for (TInt i=0; i<fileNameListSize; i++)
		{
		if (!iArray->MdcaPoint(i).CompareF(filename))
			return i;
		}
	return KErrNotFound;
	}

TInt CEikUnifiedFileNameSelector::TryBuildFileNameArrayL(const TDesC& aFullName, MDesCArray* aFileNames = NULL)
	{
	TParse* parse = new(ELeave) TParse;
	CleanupStack::PushL(parse);
    User::LeaveIfError(iCoeEnv->FsSession().Parse(aFullName, *parse));
	// Try all drives
	// get a list of drives
	CDesCArray* drives = new(ELeave) CDesCArrayFlat(1);
	CleanupStack::PushL(drives);
	TDriveNumber driveNum=EDriveC;
	TBuf<2> driveLetter;
	EikFileUtils::UpdateDiskListL(iEikonEnv->FsSession(),*drives,iIncludeROM,driveNum);
	for (TInt j = 0; j < drives->MdcaCount(); j++)
		{
		driveLetter = (*drives)[j];
		driveLetter.Append(_L(":"));
		parse->Set(driveLetter,&aFullName,NULL);
		if (aFileNames)
			BuildFileNameArrayL(*parse, aFileNames);
		else
			BuildFileNameArrayL(*parse, iArray);
		}
	CleanupStack::PopAndDestroy(2);	// drives & parse
	return(NULL);
	}

void CEikUnifiedFileNameSelector::ParseToRear(TFileName& aFileName) const
// 
// Change a filename from the format "C:My file name" to "My file name (C)"
// Special treatment of ROM, no (Z) appended.
// 
	{
	__ASSERT_DEBUG(aFileName[1] == ':',Panic(KErrArgument));
	TBuf<9> pre;
	iEikonEnv->ReadResource(pre, R_EIK_TBUF_UNIFIED_PRE_DRIVE);
	TBuf<9> post;
	iEikonEnv->ReadResource(post, R_EIK_TBUF_UNIFIED_POST_DRIVE);
	TInt driveTrailerLen = pre.Length() + post.Length() + KDriveLetterLen;
	TText drive = aFileName[0];
	if ((aFileName.Length() - KDriveDelimiterLen + driveTrailerLen) <= KMaxFileNameLen)
		aFileName = aFileName.Right(aFileName.Length() - KDriveDelimiterLen);
	else
		User::Leave(KErrOverflow);
	if (drive == KROMDrive)
		return;
	aFileName.Append(pre);
	aFileName.Append(drive);
	aFileName.Append(post);
	}

void CEikUnifiedFileNameSelector::ParseToFront(TFileName& aFileName) const
//
// Change a filename from the format "My file name (C)" to "C:My file name"
// Special treatment of ROM, no Z
	{
	TFileName filename;
	TBuf<9> pre;
	iEikonEnv->ReadResource(pre, R_EIK_TBUF_UNIFIED_PRE_DRIVE);
	TBuf<9> post;
	iEikonEnv->ReadResource(post, R_EIK_TBUF_UNIFIED_POST_DRIVE);
	TInt driveTrailerLen = pre.Length() + post.Length() + KDriveLetterLen;
	if (aFileName.Right(post.Length()) == post)
		filename.Append(aFileName[aFileName.Length() - post.Length() - 1]);
	else
		{
		filename.Append(KROMDrive);
		driveTrailerLen=0;
		}
	filename.Append(_L(":"));
	filename.Append(aFileName.Left(aFileName.Length() - driveTrailerLen));

	TParsePtrC parse1(iRef);
	TFindFile ff(iEikonEnv->FsSession());
	CheckLanguage(ff,filename,parse1.DriveAndPath());
	aFileName = filename;
	}

void CEikUnifiedFileNameSelector::SpecialMessage(TDesC& aMessage)
	{
	DesCArray()->AppendL(aMessage);
	delete(iMatchBuf);
	iMatchBuf=NULL;
 	iChoiceListFlags &= (~EEikChlistIncrementalMatching);
	if (IsFocused() && (iMatchBuf != NULL))
		iEikonEnv->HideCursor(this);
	ContentHasChangedL();
	}
