commands/fed/src/fed.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 00:49:35 +0100
changeset 45 534b01198c2d
parent 0 7f656887cf89
child 95 b3ffff030d5c
permissions -rw-r--r--
Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase. Commands can now get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.

// fed.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 <e32cons.h>
#include <BADESCA.H>
#include <charconv.h>

#include "fed.h"
#include "viewbase.h"
#include "bufferbase.h"
#include "screenmngr.h"
#include "cmdwindow.h"

#include "filebuffer.h"
#include "lrtextview.h"

const TInt KDefScreenPosX = 0;
const TInt KDefScreenPosY = 0;

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

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

CFed::CFed(CColorConsoleBase& aConsole, RFs& aFs)
	: CActive(CActive::EPriorityStandard), iFs(aFs), iConsole(aConsole), iColorConsole(&aConsole), iViewIdx(-1)
	{
	CActiveScheduler::Add(this);
	}

CFed::CFed(CConsoleBase& aConsole, RFs& aFs)
	: CActive(CActive::EPriorityStandard), iFs(aFs), iConsole(aConsole), iViewIdx(-1)
	{
	CActiveScheduler::Add(this);
	}

CFed::~CFed()
	{
	Cancel();
	for (TInt i = iViewArray.Count()-1; i >= 0; i--)
		delete iViewArray[i];
	iViewArray.Reset();
	delete iCmdWindow;
	delete iCharconv;
	}

void CFed::ConstructL()
	{
	iFs.CreatePrivatePath(EDriveC);
	TSize size = iConsole.ScreenSize();
	iScreenMngr.ResizeScreenL(TWindow(KDefScreenPosX, KDefScreenPosY, size.iWidth, size.iHeight));
	if (iColorConsole)
		{
		iCmdWindow = CCommandWindow::NewL(iFs, *iColorConsole);
		}
	else
		{
		iCmdWindow = CCommandWindow::NewL(iFs, iConsole);
		}
	iCmdWindow->SetWindow(iScreenMngr.GetCommandWindow());
	
	TRAPD(err, iCharconv = CCnvCharacterSetConverter::NewL());
	if (!err)
		{
		// This can fail if charconv isn't properly configured in the ROM
		ASSERT(iCharconv->PrepareToConvertToOrFromL(KCharacterSetIdentifierUtf8, iFs) == CCnvCharacterSetConverter::EAvailable);
		}
	}

void CFed::StartL(CDesC16Array* aFiles)
	{
	CleanupStack::PushL(aFiles);
	CreateViewsL(aFiles);
	CleanupStack::PopAndDestroy(aFiles);

	iConsole.Read(iStatus);
	SetActive();
	}

//CActive
void CFed::DoCancel()
	{
	iConsole.ReadCancel();
	}

void CFed::RunL()
	{
	if(iStatus.Int() < 0)
		{
		CActiveScheduler::Stop();
		return;
		}

	TKeyCode code = iConsole.KeyCode();
	TCmdType cmd = ProcessKeyL(code);

	if(!ExecuteCommandL(cmd) && iViewIdx >= 0)
		iViewArray[iViewIdx]->ConsumeKeyL(code);
	if(iViewArray.Count() == 0)
		{
		// No more views, time to exit.
		// Set the cursor to the bottom of the screen before we do - easier for the user to see that we've exited
		TSize screenSize = iConsole.ScreenSize();
		iConsole.SetPos(screenSize.iWidth-1, screenSize.iHeight-1); // Bottom right corner
		iConsole.Write(_L(" ")); // This should cause the window to scroll and the cursor to be on the bottom line
		CActiveScheduler::Stop();
		return;
		}

	iConsole.Read(iStatus);
	SetActive();
	}

TInt CFed::RunError(TInt aError)
	{
	if (iCmdWindow)
		{
		iCmdWindow->InfoPrint(_L("Leave occurred: %d"), aError);

		// And rerequest further keys
		iConsole.Read(iStatus);
		SetActive();

		return KErrNone;
		}
	return aError;
	}

void CFed::CreateViewsL(CDesC16Array* aFiles)
	{
	TInt n = aFiles->Count();
	for (TInt i = 0; i < n; i++)
		{
		NewViewL((*aFiles)[i], ETrue);
		}

	if (n == 0)
		{
		// Create a new untitled view instead
		NewViewL(KNullDesC);
		}
	if (iCharconv)
		{
		iCmdWindow->InfoPrint(_L("Welcome to fed. Press Ctrl-L for help."));
		}
	else
		{
		iCmdWindow->InfoPrint(_L("Warning: couldn't load charconv. UTF-8 not available."));
		}
	}

CFed::TCmdType CFed::ProcessKeyL(const TKeyCode& aCode)
	{
	switch(aCode)
		{
	case CTRL('q'):
		return ECmdCloseAll;
	case EKeyF1:
	case CTRL('l'):
		return ECmdHelp;
	case EKeyF2:
	case CTRL('p'):
		return ECmdPrevView;
	case EKeyF3:
	case CTRL('}'):
		return ECmdNextView;
	case EKeyEscape:
	case CTRL('w'):
		return ECmdCloseCurrent;
	case CTRL('n'):
		return ECmdNewDocument;
	case CTRL('o'):
		return ECmdOpenDocument;
	case CTRL('r'):
	case EKeyF5:
		return ECmdRefresh;
	default:
		break;
		}
	return ECmdNone;
	}

TBool CFed::ExecuteCommandL(TCmdType aCmd)
{
	switch(aCmd)
		{
		case ECmdHelp:
			::ShowHelpL();
			break;
		case ECmdPrevView:
			iViewIdx--;
			if (iViewIdx < 0) iViewIdx = iViewArray.Count()-1;
			iScreenMngr.AttachViewL(*iViewArray[iViewIdx]);
			break;
		case ECmdNextView:
			iViewIdx++;
			if (iViewIdx >= iViewArray.Count()) iViewIdx = 0;
			iScreenMngr.AttachViewL(*iViewArray[iViewIdx]);
			break;
		case ECmdCloseCurrent:
			CloseCurrentViewL();
			break;
		case ECmdCloseAll:
			CloseAllViewsL();
			break;
		case ECmdNewDocument:
			NewViewL(KNullDesC);
			break;
		case ECmdOpenDocument:
			OpenDocumentL();
			break;
		case ECmdRefresh:
			iScreenMngr.RefreshScreenL();
			break;
		default:
			return EFalse;
		}
	return ETrue;
}

TBool CFed::CloseCurrentViewL()
{
	if(iViewIdx < 0)
		{
		//Show a message to the user that there is nothing to close
		return EFalse;
		}

	CFedViewBase* view = iViewArray[iViewIdx];
	TBool okToClose = ETrue; 
	if (view->Buffer().RefCount() == 1)
		{
		// We're the only view on this buffer - better ask about saving
		okToClose = view->TryCloseL();
		}

	if (okToClose)
		{
		DeleteView();
		}
	return okToClose;
	}

void CFed::CloseAllViewsL()
{
	TBool ret = ETrue;
	while(iViewIdx >=0 && ret)
		ret = CloseCurrentViewL();
}

void CFed::DeleteView()
	{
	delete iViewArray[iViewIdx];
	iViewArray.Remove(iViewIdx);
	iScreenMngr.UnsetCurrentView();
	if (iViewIdx >= iViewArray.Count())
		{
		iViewIdx = iViewArray.Count()-1;
		}
	if (iViewIdx >= 0)
		{
		// Attach the new frontmost view
		iScreenMngr.AttachViewL(*(iViewArray[iViewIdx]));
		}
	}

void CFed::NewViewL(const TDesC& aFileName, TBool aPromptOnNonexistantName)
	{
	CFileBuffer* buffer = NULL;
	TRAPD(err, buffer = CFileBuffer::NewL(iFs, iCharconv, aFileName, EFalse));
	if ((err == KErrNotFound || err == KErrPathNotFound) && aPromptOnNonexistantName)
		{
		if (iCmdWindow->Query(_L("File not found. Create? (yes/no) "), _L("yn")) == 'y')
			{
			buffer = CFileBuffer::NewL(iFs, iCharconv, aFileName, ETrue);
			err = KErrNone;
			}
		}
	User::LeaveIfError(err);

	buffer->PushL();
	CLRTextView* view = CLRTextView::NewL(*iCmdWindow, *buffer);
	CleanupStack::Pop(buffer);
	buffer->DecRef(); // view now has a ref to it
	CleanupStack::PushL(view);
	iViewArray.AppendL(view);
	CleanupStack::Pop(view);
	iViewIdx = iViewArray.Count()-1;
	iScreenMngr.AddViewL(*view);
	}

void CFed::OpenDocumentL()
	{
	TFileName name;
	TBool go = iCmdWindow->QueryFilename(_L("Open file: "), name);
	if (go)
		{
		TInt prevIdx = iViewIdx;
		TRAPD(err, NewViewL(name));
		if (err)
			{
			switch (err)
				{
				case KErrNotFound:
					iCmdWindow->InfoPrint(_L("File '%S' not found"), &name);
					break;
				case KErrPathNotFound:
					iCmdWindow->InfoPrint(_L("KErrPathNotFound. Directory doesn't exist?"));
					break;
				case KErrBadName:
					iCmdWindow->InfoPrint(_L("Bad name"));
					break;
				default:
					iCmdWindow->InfoPrint(_L("Unable to open file: %d."), err);
					break;
				}
			}
		else if (!err && prevIdx >= 0)
			{
			CFedViewBase* prevView = iViewArray[prevIdx];
			if (prevView->Buffer().Editable() && !prevView->Buffer().Modified() && prevView->Buffer().Title().Length() == 0)
				{
				// Then previous doc was an empty, untitled window, which this open should replace
				delete prevView;
				iViewArray.Remove(prevIdx);
				iViewIdx = iViewArray.Count()-1;
				}
			}
		}
	}

// Not usable: ^H = Bksp, ^M = CR, ^I = Tab, ^J = LF, ^Z = suspend, ^[ = Esc
// Currently free: ^E, ^Y
void CFed::ShowHelpL(HBufC* aHelpText)
	{
	_LIT(KHelpTitle, "Fed Help");
	CleanupStack::PushL(aHelpText);
	CConstDataBuffer* helpBuf = new(ELeave) CConstDataBuffer(KHelpTitle, aHelpText);
	CleanupStack::Pop(aHelpText);
	helpBuf->PushL();
	CLRTextView* view = CLRTextView::NewL(*iCmdWindow, *helpBuf);
	CleanupStack::Pop(helpBuf);
	helpBuf->DecRef(); // view now has a ref to it
	CleanupStack::PushL(view);
	iViewArray.AppendL(view);
	CleanupStack::Pop(view);
	iViewIdx = iViewArray.Count()-1;
	iScreenMngr.AddViewL(*view);
	}

void CFed::RedrawEverythingL()
	{
	ExecuteCommandL(ECmdRefresh);
	}