plugins/consoles/rcons/server/win32/TextBuffer.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/consoles/rcons/server/win32/TextBuffer.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,467 @@
+// TextBuffer.cpp
+// 
+// Copyright (c) 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 "Misc.h"
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+#include "TextBuffer.h"
+
+const int KTabSize = 4;
+
+
+CTextBuffer* CTextBuffer::New(int aWidth, int aHeight, int aMaxNumOverflowLines)
+	{
+	std::auto_ptr<CTextBuffer> self(new(EThrow) CTextBuffer(aWidth, aHeight, aMaxNumOverflowLines));
+	self->Construct();
+	return self.release();
+	}
+
+CTextBuffer::~CTextBuffer()
+	{
+	delete iBuffer;
+	}
+
+void CTextBuffer::SetObserver(MTextBufferObserver* aObserver)
+	{
+	iObserver = aObserver;
+	}
+
+int StringLength(LPCTSTR aStart, LPCTSTR aEnd)
+	{
+	return (aEnd - aStart) + 1;
+	}
+
+void CTextBuffer::Write(LPCTSTR aString, int aLength)
+	{
+	LPCTSTR startBlock = aString;
+	LPCTSTR ptr = aString;
+	LPCTSTR end = aString + aLength - 1;
+	int numCharsToLineEnd = NumCharsToLineEnd();
+	while (ptr <= end)
+		{
+		if (!IsOrdinaryChar(*ptr))
+			{
+			// Not an ordinary character, so write the block we've gathered so far.
+			if (StringLength(startBlock, ptr - 1) > 0)
+				{
+				WriteBlock(startBlock, StringLength(startBlock, ptr - 1));
+				}
+			// Write the special character.
+			WriteSpecialChar(*ptr);
+			// Setup variables for the next block.
+			startBlock = ptr + 1;
+			numCharsToLineEnd = NumCharsToLineEnd();
+			}
+		else if (StringLength(startBlock, ptr) == numCharsToLineEnd)
+			{
+			// Reached line end, so write the block we've gathered so far.
+			ASSERT(numCharsToLineEnd > 0);
+			WriteBlock(startBlock, numCharsToLineEnd);
+			// Setup variables for the next block.
+			startBlock = ptr + 1;
+			numCharsToLineEnd = NumCharsToLineEnd();
+			}
+		else if (ptr == end)
+			{
+			// Reached end of buffer.
+			if (StringLength(startBlock, ptr) > 0)
+				{
+				WriteBlock(startBlock, StringLength(startBlock, ptr));
+				}
+			}
+		++ptr;
+		}
+	}
+
+void CTextBuffer::GetCursorPos(int& aX, int& aY) const
+	{
+	aX = iCursorPosX;
+	aY = iCursorPosY;
+	}
+
+void CTextBuffer::SetAbsCursorPos(int aX, int aY)
+	{
+	MoveCursor(aX, aY);
+	}
+
+void CTextBuffer::SetRelCursorPos(int aX, int aY)
+	{
+	MoveCursor(iCursorPosX + aX, iCursorPosY + aY);
+	}
+
+void CTextBuffer::GetSize(int& aWidth, int& aHeight) const
+	{
+	aWidth = iWidth;
+	aHeight = iHeight;
+	}
+
+int CTextBuffer::NumOverflowLines() const
+	{
+	return iNumOverflowLines;
+	}
+
+void CTextBuffer::Clear()
+	{
+	ClearBuffer();
+	MoveCursor(0, 0);
+	}
+
+void CTextBuffer::ClearToEndOfLine()
+	{
+	const PTCHAR start = CursorPtr();
+	PTCHAR ptr = start;
+	const PTCHAR end = LineEndPtr();
+
+	while (ptr <= end)
+		{
+		*ptr++ = TCHAR(' ');
+		}
+
+	if (iObserver)
+		{
+		iObserver->HandleTextBufferChange(iCursorPosX, iCursorPosY, start, StringLength(start, end));
+		}
+	}
+
+CTextBuffer::CTextBuffer(int aWidth, int aHeight, int aMaxNumOverflowLines)
+	: iObserver(NULL), iBuffer(NULL), iWidth(aWidth), iHeight(aHeight), iMaxNumOverflowLines(aMaxNumOverflowLines), iNumOverflowLines(0), iCursorPosX(0), iCursorPosY(0), iCaptureFile(0)
+	{
+	}
+
+void CTextBuffer::Construct()
+	{
+	iBuffer = new(EThrow) TCHAR[iWidth * (iHeight + iMaxNumOverflowLines)];
+	ClearBuffer();
+	}
+
+void CTextBuffer::MoveCursor(int aNewX, int aNewY)
+	{
+	ASSERT((aNewX >= 0) && (aNewY >= 0));
+	if ((aNewX >= 0) && (aNewY >= 0))
+		{
+		if (aNewX < iWidth)
+			{
+			iCursorPosX = aNewX;
+			}
+		if (aNewY < iHeight)
+			{
+			iCursorPosY = aNewY;
+			}
+		if (iObserver)
+			{
+			iObserver->HandleTextBufferCursorChange();
+			}
+		}
+	}
+
+PTCHAR CTextBuffer::CursorPtr() const
+	{
+	return iBuffer + (((iMaxNumOverflowLines + iCursorPosY) * iWidth) + iCursorPosX);
+	}
+
+PTCHAR CTextBuffer::LineEndPtr() const
+	{
+	return iBuffer + (((iMaxNumOverflowLines + iCursorPosY) * iWidth) + (iWidth - 1));
+	}
+
+PTCHAR CTextBuffer::BufEndPtr() const
+	{
+	return iBuffer + (iWidth * (iHeight + iMaxNumOverflowLines));
+	}
+
+void CTextBuffer::WriteSpecialChar(TCHAR aChar)
+	{
+	switch(aChar)
+		{
+		case 0x00:	// Null.
+			break;
+		case 0x07:	// Bell.
+			break;
+		case 0x08:	// Backspace.
+		case 0x7f:	// Delete.
+			BackSpace();
+			break;
+		case 0x09:
+			HorizontalTab();
+			break;
+		case 0x0a:
+			LineFeed();
+			break;
+		case 0x0b:	// Vertical tab.
+			break;
+		case 0x0c:
+			FormFeed();
+			break;
+		case 0x0d:
+			CarriageReturn();
+			break;
+		default:
+			ASSERT(FALSE);
+		}
+	}
+
+void CTextBuffer::WriteBlock(LPCTSTR aString, int aLength)
+	{
+	PTCHAR cursorPtr = CursorPtr();
+	PTCHAR ptr = cursorPtr;
+	for (int i = 0; i < aLength; ++i)
+		{
+		*ptr++ = aString[i];
+		}
+	if (iObserver)
+		{
+		iObserver->HandleTextBufferChange(iCursorPosX, iCursorPosY, cursorPtr, aLength);
+		}
+	if ((iCursorPosX + aLength) == iWidth)
+		{
+		MoveCursor(iWidth - 1, iCursorPosY);
+		CursorRight();
+		}
+	else
+		{
+		MoveCursor(iCursorPosX + aLength, iCursorPosY);
+		}
+	}
+
+bool CTextBuffer::IsOrdinaryChar(TCHAR aChar) const
+	{
+	switch(aChar)
+		{
+		case 0x00:	// Null.
+		case 0x07:	// Bell.
+		case 0x08:	// Backspace.
+		case 0x7f:	// Delete.
+		case 0x09:	// Tab.
+		case 0x0a:	// Line feed.
+		case 0x0b:	// Vertical tab.
+		case 0x0c:	// Form feed.
+		case 0x0d:	// Carriage return.
+			return FALSE;
+		default:
+			return TRUE;
+		}
+	}
+
+int CTextBuffer::NumCharsToLineEnd() const
+	{
+	return iWidth - iCursorPosX;
+	}
+
+void CTextBuffer::ClearBuffer()
+	{
+	PTCHAR bufPtr = iBuffer;
+	PTCHAR end = BufEndPtr();
+	while (bufPtr < end )
+		{
+		*bufPtr++ = TCHAR(' ');
+		}
+	iNumOverflowLines = 0;
+	if (iObserver)
+		{
+		iObserver->HandleTextBufferCleared();
+		}
+	}
+
+LPCTSTR CTextBuffer::GetLine(int aPos) const
+	{
+	ASSERT(aPos >= -iNumOverflowLines);
+	ASSERT(aPos < iHeight);
+	return iBuffer + ((iMaxNumOverflowLines + aPos) * iWidth);
+	}
+
+void CTextBuffer::CaptureToFile(LPCTSTR aFileName)
+	{
+	ASSERT(iCaptureFile == 0);
+	iCaptureFile = _wopen(aFileName, _O_CREAT | _O_WRONLY | _O_BINARY, _S_IWRITE);
+	if (iCaptureFile == -1)
+		{
+		iCaptureFile = 0;
+		throw KExceptionFailedToCreateCaptureFile;
+		}
+	WriteOverflowLinesToCaptureFile();
+	}
+
+void CTextBuffer::StopCaptureToFile()
+	{
+	if (iCaptureFile)
+		{
+		WriteBufferToCaptureFile();
+		_close(iCaptureFile);
+		iCaptureFile = 0;
+		}
+	}
+
+bool CTextBuffer::IsCapturingToFile() const
+	{
+	return !(iCaptureFile == 0);
+	}
+
+void CTextBuffer::CursorLeft()
+	{
+	if (iCursorPosX > 0)
+		{
+		// Not yet reached beginning of line.
+		MoveCursor(iCursorPosX - 1, iCursorPosY);
+		}
+	else if (iCursorPosY > 0)
+		{
+		// Reached beginning of line, so jump to end of line above.
+		MoveCursor(iWidth - 1, iCursorPosY - 1);
+		}
+	else
+		{
+		// Reached the top left corner of the console - do nothing.
+		}
+	}
+
+void CTextBuffer::CursorRight()
+	{
+	if (iCursorPosX < (iWidth - 1))
+		{
+		// Not yet reached the end of the line.
+		MoveCursor(iCursorPosX + 1, iCursorPosY);
+		}
+	else if (iCursorPosY < (iHeight - 1))
+		{
+		// Reached the end of the line and there's space below - jump to the beginning of the line below.
+		MoveCursor(0, iCursorPosY + 1);
+		}
+	else
+		{
+		// Reached the end of the line and there's no space below - scroll up a line and jump to the beginning of the newly exposed line.
+		ScrollUp();
+		MoveCursor(0, iCursorPosY);
+		}
+	}
+
+void CTextBuffer::FormFeed()
+	{
+	Clear();
+	}
+
+void CTextBuffer::LineFeed()
+	{
+	if (iCursorPosY < (iHeight - 1))
+		{
+		MoveCursor(0, iCursorPosY + 1);
+		}
+	else
+		{
+		ScrollUp();
+		MoveCursor(0, iCursorPosY);
+		}
+	}
+
+void CTextBuffer::CarriageReturn()
+	{
+	MoveCursor(0, iCursorPosY);
+	}
+
+void CTextBuffer::BackSpace()
+	{
+	if (!((iCursorPosX == 0) && (iCursorPosY == 0)))
+		{
+		CursorLeft();
+		WriteBlock(TEXT(" "), 1);
+		CursorLeft();
+		}
+	}
+
+void CTextBuffer::HorizontalTab()
+	{
+	MoveCursor(iCursorPosX - (iCursorPosX % KTabSize) + KTabSize, iCursorPosY);
+	if (iCursorPosX > (iWidth - 1))
+		{
+		CarriageReturn();
+		LineFeed();
+		}
+	}
+
+void CTextBuffer::ScrollUp()
+	{
+	WriteLineToCaptureFile(GetLine(0));
+	PTCHAR dst = iBuffer;
+	PTCHAR end = BufEndPtr();
+	PTCHAR src = dst + iWidth;
+	while (src < end)
+		{
+		*dst++ = *src++;
+		}
+	while (dst < end)
+		{
+		*dst++ = TCHAR(' ');
+		}
+	if (iNumOverflowLines < iMaxNumOverflowLines)
+		{
+		++iNumOverflowLines;
+		}
+	if (iObserver)
+		{
+		iObserver->HandleTextBufferScroll();
+		}
+	}
+
+void CTextBuffer::WriteLineToCaptureFile(LPCTSTR aLinePtr)
+	{
+	if (iCaptureFile)
+		{
+		LPCTSTR lineEnd = aLinePtr + iWidth - 1;
+		while ((*lineEnd == TCHAR(' ')) && (lineEnd > aLinePtr))
+			{
+			--lineEnd;
+			}
+		if (lineEnd == aLinePtr)
+			{
+			if (_write(iCaptureFile, "\r\n", 2) != 2)
+				{
+				_close(iCaptureFile);
+				iCaptureFile = 0;
+				throw KExceptionFailedToWriteToCaptureFile;
+				}
+			}
+		else
+			{
+			const int lineLength = (lineEnd - aLinePtr) + 3;
+			std::auto_ptr<char> narrowBuf(new(EThrow) char[lineLength]);
+			char* narrowBufPtr = narrowBuf.get();
+			while (aLinePtr <= lineEnd)
+				{
+				*narrowBufPtr++ = (char)*aLinePtr++;
+				}
+			*narrowBufPtr++ = '\r';
+			*narrowBufPtr = '\n';
+			if (_write(iCaptureFile, narrowBuf.get(), lineLength) != lineLength)
+				{
+				_close(iCaptureFile);
+				iCaptureFile = 0;
+				throw KExceptionFailedToWriteToCaptureFile;
+				}
+			
+			}
+		}
+	}
+
+void CTextBuffer::WriteOverflowLinesToCaptureFile()
+	{
+	for (int i = -iNumOverflowLines; i < 0; ++i)
+		{
+		WriteLineToCaptureFile(GetLine(i));
+		}
+	}
+
+void CTextBuffer::WriteBufferToCaptureFile()
+	{
+	for (int i = 0; i < iHeight; ++i)
+		{
+		WriteLineToCaptureFile(GetLine(i));
+		}
+	}