commands/fed/src/cmdwindow.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Sat, 31 Jul 2010 19:07:57 +0100
changeset 23 092bcc217d9d
parent 0 7f656887cf89
permissions -rw-r--r--
Tidied iocli exports, build macro tweaks. Removed 4 overloads of CCommandBase::RunCommand[L] that are no longer used at all, and changed one more to not be exported as it's only used internally to iocli.dll. fixed builds on platforms that don't support btrace or any form of tracing.

// cmdwindow.cpp
// 
// Copyright (c) 2009 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
// 
// Initial Contributors:
// Accenture - Initial contribution
//
#include "cmdwindow.h"
#include <e32cons.h>
#include <BADESCA.H>

const TInt KInfoPrintTime = 2500000; // 2.5 seconds

CCommandWindow* CCommandWindow::NewL(RFs& aFs, CColorConsoleBase& aConsole)
	{
	CCommandWindow* self = new(ELeave) CCommandWindow(aFs, aConsole);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CCommandWindow* CCommandWindow::NewL(RFs& aFs, CConsoleBase& aConsole)
	{
	CCommandWindow* self = new(ELeave) CCommandWindow(aFs, aConsole);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CCommandWindow::CCommandWindow(RFs& aFs, CConsoleBase& aConsole)
	: iFs(aFs), iConsole(aConsole), iColorConsole(NULL), iConsoleAdapter(aConsole)
	{}

CCommandWindow::CCommandWindow(RFs& aFs, CColorConsoleBase& aConsole)
	: iFs(aFs), iConsole(aConsole), iColorConsole(&aConsole), iConsoleAdapter(aConsole)
	{}

void CCommandWindow::ConstructL()
	{
	iInfoPrintDismisser = CPeriodic::NewL(CActive::EPriorityStandard);
	TBuf<64> buf;

	User::LeaveIfError(iFs.PrivatePath(buf));
	buf.Insert(0, _L("c:"));
	buf.Append(_L("fed_filehistory"));
	iFileNameLineEditor = CLineEditor::NewL(iFs, iConsoleAdapter, *this, *this, buf);

	User::LeaveIfError(iFs.PrivatePath(buf));
	buf.Insert(0, _L("c:"));
	buf.Append(_L("fed_generalhistory"));
	iGeneralLineEditor = CLineEditor::NewL(iFs, iConsoleAdapter, *this, *this, buf);
	}

CCommandWindow::~CCommandWindow()
	{
	if (iInfoPrintDismisser) iInfoPrintDismisser->Cancel();
	delete iInfoPrintDismisser;
	delete iGeneralLineEditor;
	delete iFileNameLineEditor;
	}

CConsoleBase& CCommandWindow::Console()
	{
	return iConsole;
	}

CColorConsoleBase* CCommandWindow::ColorConsole()
	{
	return iColorConsole;
	}

class TOverflow16 : public TDes16Overflow
	{
public:
	void Overflow(TDes16 &) {}
	};

void CCommandWindow::WriteStatus(const TDesC& aNameToTruncate, TRefByValue<const TDesC> aFmt, ...)
	{
	TDes& buf = iLastStatus;
	buf.Zero();
	VA_LIST args;
	VA_START(args, aFmt);
	TOverflow16 overflow;
	buf.AppendFormatList(aFmt, args, &overflow);
	VA_END(args);

	const TInt availWidth = iWindow.iWidth - 1;
	buf.SetLength(Min(buf.Length(), availWidth));
	TInt widthForName = availWidth - buf.Length();
	buf.Insert(0, aNameToTruncate.Right(widthForName));
	if (buf.Length() < iWindow.iWidth) buf.Append(' ');
	buf.AppendFill('-', iWindow.iWidth - buf.Length() - 1); // Don't fill to edge of screen, that causes a wrap

	DoWriteLine(buf);
	}

void CCommandWindow::DoWriteLine(const TDesC& aLine, TInt aHighlightStart, TInt aHighlightLength)
	{
	TPoint cursor = iConsole.CursorPos();
	iConsole.SetCursorHeight(0);
	iConsole.SetPos(iWindow.iX, iWindow.iY);
	if (aHighlightLength && ColorConsole())
		{
		TPtrC left = aLine.Left(aHighlightStart);
		TPtrC mid = aLine.Mid(aHighlightStart, aHighlightLength);
		TPtrC right = aLine.Mid(aHighlightStart + aHighlightLength);
		ColorConsole()->Write(left);
		ColorConsole()->SetTextAttribute(ETextAttributeInverse);
		ColorConsole()->Write(mid);
		ColorConsole()->SetTextAttribute(ETextAttributeNormal);
		ColorConsole()->Write(right);
		}
	else
		{
		iConsole.Write(aLine);
		}
	iConsole.SetCursorPosAbs(cursor);
	iConsole.SetCursorHeight(20);
	}

void CCommandWindow::InfoPrint(TRefByValue<const TDesC> aFmt, ...)
	{
	iInfoPrintDismisser->Cancel(); // If there's already a print onscreen, we bin it straight away (same as how User::InfoPrint works)
	TBuf<256> buf;
	VA_LIST args;
	VA_START(args, aFmt);
	TOverflow16 overflow;
	buf.AppendFormatList(aFmt, args, &overflow);
	VA_END(args);

	buf.SetLength(Min(buf.Length(), iWindow.iWidth-5)); // 4 for -[ and ]-, and one more to avoid hitting the final column
	TInt highlightLen = buf.Length() + 2; // Highlight the [] as well
	buf.Insert(0, _L("-["));
	buf.Append(_L("]-"));
	TInt fillWidth = iWindow.iWidth - buf.Length() - 1;
	TInt left = fillWidth / 2;
	TInt highlightStart = left+1;
	TInt right = fillWidth - left;
	while(left--)
		{
		buf.Insert(0, _L("-"));
		}
	buf.AppendFill('-', right);
	DoWriteLine(buf, highlightStart, highlightLen);
	iInfoPrintDismisser->Start(KInfoPrintTime, KInfoPrintTime, TCallBack(&DismissInfoPrint, this));
	}

TInt CCommandWindow::DismissInfoPrint(TAny* aSelf)
	{
	CCommandWindow* self = static_cast<CCommandWindow*>(aSelf);
	// Restore previous status
	self->DoWriteLine(self->iLastStatus);
	self->iInfoPrintDismisser->Cancel();
	return 0;
	}

TKeyCode CCommandWindow::Query(const TDesC& aPrompt, const TDesC& aValidKeys)
	{
	TPoint cursor = iConsole.CursorPos();
	iQuery = aPrompt;
	iQuery.Append(_L("[ ]"));
	iQuery.AppendFill('-', iQuery.MaxLength() - iQuery.Length());
	iQuery.SetLength(iWindow.iWidth - 1);
	DoWriteLine(iQuery);
	iConsole.SetPos(iWindow.iX + aPrompt.Length()+1, iWindow.iY);
	iConsole.SetCursorHeight(20);
	TKeyCode res = EKeyNull;
	while (aValidKeys.Locate(res) == KErrNotFound)
		{
		res = iConsole.Getch();
		}
	// Print the key that the user entered, between the square brackets
	if (TChar(res).IsPrint())
		{
		TBuf<1> chbuf;
		chbuf.Append(res);
		iConsole.Write(chbuf);
		}
	iConsole.SetCursorPosAbs(cursor);
	// Async clear the query (in case we are called repeatedly)
	iInfoPrintDismisser->Cancel();
	iInfoPrintDismisser->Start(0, KInfoPrintTime, TCallBack(&DismissInfoPrint, this));
	return res;
	}

TBool CCommandWindow::QueryText(const TDesC& aPrompt, TDes& aText)
	{
	iLineEditor = iGeneralLineEditor;
	return DoQueryText(aPrompt, aText);
	}

TBool CCommandWindow::QueryFilename(const TDesC& aPrompt, TFileName& aFileName)
	{
	iLineEditor = iFileNameLineEditor;
	TBool res = DoQueryText(aPrompt, aFileName);
	if (res)
		{
		IoUtils::TFileName2 fn2(aFileName);
		TRAPD(err, fn2.MakeAbsoluteL(iFs));
		if (err) res = EFalse;
		aFileName.Copy(fn2);
		}
	return res;
	}

TBool CCommandWindow::DoQueryText(const TDesC& aPrompt, TDes& aText)
	{
	// Get cursor in right position
	TPoint cursor = iConsole.CursorPos();
	// Clear the way
	iConsole.SetPos(iWindow.iX, iWindow.iY);
	iQuery.Fill(' ', iWindow.iWidth-1);
	iConsole.Write(iQuery);
	iConsole.SetPos(iWindow.iX, iWindow.iY);
	// Start the line editor going
	iLineEditor->Start(aPrompt, aText);
	iLineEditor->ReinstatePromptAndUserInput();
	iFinished = EFalse;
	TBool result = ETrue;
	iResultForQuery = &aText;
	while (!iFinished)
		{
		TKeyCode ch = iConsole.Getch();
		if (ch == EKeyEscape) 
			{
			result = EFalse;
			break;
			}
		if (ch == EKeyEnter)
			{
			// Fool line editor by moving cursor up a line, so when it does its newline it doesn't screw us up
			iConsole.SetCursorPosRel(TPoint(0, -1));
			}
		iLineEditor->HandleKey(ch, iConsole.KeyModifiers());
		}
	iConsole.SetCursorPosAbs(cursor);
	// Async clear the query (in case of... something. Velociraptors maybe?)
	iInfoPrintDismisser->Cancel();
	iInfoPrintDismisser->Start(0, KInfoPrintTime, TCallBack(&DismissInfoPrint, this));
	return result;
	}

RFs& CCommandWindow::Fs()
	{
	return iFs;
	}

void CCommandWindow::LeoHandleLine(const TDesC& aLine)
	{
	iFinished = ETrue;
	*iResultForQuery = aLine.Left(iResultForQuery->MaxLength());
	}

_LIT(KFileNameSlash, "\\");
_LIT(KDriveColonSlash, ":\\");
_LIT(KTab, "\t");
_LIT(KSpace, " ");

// This code ripped from ptf
void CCommandWindow::LcCompleteLineL(TConsoleLine& aLine, const TChar& /*aEscapeChar*/)
	{
	if (iLineEditor != iFileNameLineEditor) return; // Don't autocomplete for anything that isn't a filename

	IoUtils::TFileName2 fileNameMatch;
	fileNameMatch.Copy(aLine.ContentsToCursor());
	
	CDesCArray* suggestions = new(ELeave) CDesC16ArrayFlat(16);
	CleanupStack::PushL(suggestions);

	if (fileNameMatch.Length()==0)
		{
		SuggestDrivesL(suggestions);
		}
	else if (fileNameMatch.Length() == 2 && TChar(fileNameMatch[0]).IsAlpha() && fileNameMatch[1] == ':')
		{
		fileNameMatch.Append(KFileNameSlash);
		}

	if (!fileNameMatch.IsAbsolute())
		{
		fileNameMatch.MakeAbsoluteL(iFs);
		}
	
	TPtrC relativeDir = aLine.ContentsToCursor();
	relativeDir.Set(relativeDir.Left(relativeDir.LocateReverse('\\')+1)); // Remove any name to the right of the last backslash
		
	fileNameMatch.Append('*');
	
	CDir* files = NULL;
	User::LeaveIfError(iFs.GetDir(fileNameMatch, KEntryAttNormal | KEntryAttDir, ESortByName, files));
	CleanupStack::PushL(files);
		
	TInt numFiles = files->Count();
	if (numFiles == 1)
		{
		fileNameMatch.SetLength(fileNameMatch.DriveAndPath().Length());
		fileNameMatch.AppendComponentL((*files)[0].iName);
		TEntry entry;
		User::LeaveIfError(iFs.Entry(fileNameMatch, entry));
		suggestions->AppendL(fileNameMatch.NameAndExt());
		CompleteL(aLine, *suggestions, &relativeDir, entry.IsDir() ? &KFileNameSlash : NULL);
		}
	else if (numFiles > 0)
		{
		for (TInt i=0; i<numFiles; ++i)
			{
			suggestions->AppendL((*files)[i].iName);
			}
		CompleteL(aLine, *suggestions, &relativeDir, NULL);
		}
	
	CleanupStack::PopAndDestroy(2, suggestions); // files, suggestions
	}

void CCommandWindow::SuggestDrivesL(CDesCArray* aSuggestions)
	{
	TDriveList drives;
	User::LeaveIfError(iFs.DriveList(drives));
	
	for (TInt i=0; i<drives.Length(); ++i)
		{
		if (drives[i])
			{
			TBuf<3> buf;
			
			TChar driveChar;
			User::LeaveIfError(iFs.DriveToChar(i, driveChar));
			
			buf.Append(driveChar);
			buf.Append(KDriveColonSlash);
			aSuggestions->AppendL(buf);						
			}
		}
	}
	
void CCommandWindow::CompleteL(TConsoleLine& aLine, const CDesCArray& aPossibilities, const TDesC* aPrefix, const TDesC* aSuffix)
	{
	const TInt numPossibilities = aPossibilities.Count();

	if (numPossibilities > 1)
		{
		// Fill out possibilities buffer.
		IoUtils::CTextBuffer* possibilities = IoUtils::CTextBuffer::NewLC(0x100);
		for (TInt i = 0; i < numPossibilities; ++i)
			{
			possibilities->AppendL(aPossibilities[i]);
			if (i != (numPossibilities - 1))
				{
				possibilities->AppendL(KTab);
				}
			}

		aLine.PrintCompletionPossibilitiesL(possibilities->Descriptor());
		CleanupStack::PopAndDestroy(possibilities);
		}

	if (numPossibilities > 0)
		{
		IoUtils::CTextBuffer* completion = IoUtils::CTextBuffer::NewLC(0x100);
		TPtrC commonChars(NULL, 0);
		if (numPossibilities > 1)
			{
			// Find common leading characters of the possibilities.
			TInt commonCharPos = -1;
			TBool finished(EFalse);
			do
				{
				++commonCharPos;
				TChar character(0);
				for (TInt i = 0; i < numPossibilities; ++i)
					{
					if (commonCharPos >= aPossibilities[i].Length())
						{
						finished = ETrue;
						break;
						}
					else if (i == 0)
						{
						character = aPossibilities[0][commonCharPos];
						character.Fold();
						}
					else
						{
						TChar c(aPossibilities[i][commonCharPos]);
						c.Fold();
						if (c != character)
							{
							finished = ETrue;
							break;
							}
						}
					}
				}
				while (!finished);

			commonChars.Set(aPossibilities[0].Mid(0, commonCharPos));
			}
		else
			{
			commonChars.Set(aPossibilities[0]);
			}

		if (aPrefix)
			{
			completion->AppendL(*aPrefix);
			}
		completion->AppendL(commonChars);

		if ((numPossibilities == 1) && (aSuffix))
			{
			completion->AppendL(*aSuffix);
			}
			
		if ((numPossibilities == 1) && (!aSuffix))
			{
			completion->AppendL(KSpace);
			}
			
		if (completion->Descriptor().Length() > 0)
			{
			aLine.Replace(0, completion->Descriptor());
			}
		CleanupStack::PopAndDestroy(completion);
		}
	}