/*
**	termReviewBuffer.c
**
**	Support routines for the review buffer
**
**	Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* Menu item codes. */

enum	{	MEN_SEARCH,MEN_REPEAT,MEN_CLEARBUF,MEN_QUITBUF };

	/* Gadget ID codes. */

enum	{	GAD_SCROLLER, GAD_UP, GAD_DOWN };

	/* The arrow image height. */

#define ARROW_HEIGHT	11

STATIC struct NewMenu ReviewMenu[] =
{
	{ NM_TITLE, NULL,		 0 , 0, 0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , 0, 0, (APTR)MEN_SEARCH},
	{  NM_ITEM, NULL,		 0 , 0, 0, (APTR)MEN_REPEAT},
	{  NM_ITEM, NM_BARLABEL,	 0 , 0, 0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , 0, 0, (APTR)MEN_CLEARBUF},
	{  NM_ITEM, NM_BARLABEL,	 0 , 0, 0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , 0, 0, (APTR)MEN_QUITBUF},

	{ NM_END, 0,			 0 , 0, 0, (APTR)0}
};

	/* Local routines. */

STATIC VOID		ReviewUpdatePot(VOID);
STATIC VOID __stdargs	ReviewWrites(STRPTR String,...);
STATIC VOID __regargs	PrintReviewLine(STRPTR Buffer,LONG Line);
STATIC VOID __regargs	RefreshReview(LONG Top);
STATIC VOID __regargs	ScrollReview(LONG Top);
STATIC BYTE		ReviewQuery(VOID);
STATIC UBYTE __regargs	GetReviewChar(BYTE WaitForIt);

	/* Local variables. */

STATIC struct Menu		*ReviewMenuStrip;

STATIC struct IBox		 ReviewBox;
STATIC struct IOStdReq		*ReviewWriteRequest,
				*ReviewReadRequest;

STATIC struct MsgPort		*ReviewWritePort;

STATIC struct Gadget		*Scroller,
				*UpArrow,
				*DownArrow;

STATIC UWORD			 RightBorderWidth;

STATIC struct Image		*UpImage,
				*DownImage;

STATIC UBYTE			 ReviewChar;
STATIC LONG			 ReviewColumns,ReviewLines,
				 ReviewTop = -1,ReviewGlobalLines;

STATIC struct SearchInfo	*ReviewSearchInfo;
STATIC BYTE			 SearchForward = TRUE;

STATIC UBYTE			*ReviewLineWidths;
STATIC LONG			 ReviewMaxLines;

	/* SplitEventReport():
	 *
	 *	Split a raw console input event report into components.
	 */

STATIC VOID __regargs
SplitEventReport(STRPTR Buffer,LONG Len,UBYTE *Class,UWORD *Code,UWORD *Qualifier,ULONG *Prev)
{
	STRPTR	Start = Buffer;
	LONG	Count = 0;

	if(Prev)
		*Prev = NULL;

	while(Len--)
	{
		if(*Buffer == ';')
		{
			*Buffer++ = 0;

			switch(Count++)
			{
				case 0:	*Class = Atol(Start);
					break;

				case 2:	if(Code)
						*Code = Atol(Start);

					break;

				case 3:	if(Qualifier)
						*Qualifier = Atol(Start);

					break;

				case 4:	if(Prev)
						*Prev = Atol(Start) << 16;

					break;

				case 5:	if(Prev)
						*Prev |= Atol(Start);

					break;
			}

			Start = Buffer;
		}
		else
			Buffer++;
	}
}

	/* ReviewDeleteScroller(VOID):
	 *
	 *	Delete scroller and arrow buttons.
	 */

STATIC VOID
ReviewDeleteScroller(VOID)
{
	if(Scroller)
	{
		DisposeObject(Scroller);

		Scroller = NULL;
	}

	if(UpArrow)
	{
		DisposeObject(UpArrow);

		UpArrow = NULL;
	}

	if(DownArrow)
	{
		DisposeObject(DownArrow);

		DownArrow = NULL;
	}

	if(UpImage)
	{
		DisposeObject(UpImage);

		UpImage = NULL;
	}

	if(DownImage)
	{
		DisposeObject(DownImage);

		DownImage = NULL;
	}
}

	/* ReviewCreateScroller(struct Screen *Screen):
	 *
	 *	Create scroller and arrow buttons.
	 */

STATIC BYTE __regargs
ReviewCreateScroller(struct Screen *Screen)
{
	struct DrawInfo	*DrawInfo;
	BYTE		 Result = FALSE;

	if(DrawInfo = GetScreenDrawInfo(Screen))
	{
		LONG	SizeWidth,
			SizeHeight;
		UWORD	SizeType;

		if(Screen -> Flags & SCREENHIRES)
		{
			SizeWidth	= 18;
			SizeHeight	= 10;

			SizeType	= SYSISIZE_MEDRES;
		}
		else
		{
			SizeWidth	= 13;
			SizeHeight	= 11;

			SizeType	= SYSISIZE_LOWRES;
		}

		RightBorderWidth = SizeWidth;

		if(UpImage = (struct Image *)NewObject(NULL,"sysiclass",
			SYSIA_Size,	SizeType,
			SYSIA_Which,	UPIMAGE,
			SYSIA_DrawInfo,	DrawInfo,
		TAG_DONE))
		{
			if(DownImage = (struct Image *)NewObject(NULL,"sysiclass",
				SYSIA_Size,	SizeType,
				SYSIA_Which,	DOWNIMAGE,
				SYSIA_DrawInfo,	DrawInfo,
			TAG_DONE))
			{
				if(Scroller = NewObject(NULL,"propgclass",
					GA_ID,		GAD_SCROLLER,

					GA_Top,		Screen -> WBorTop + Screen -> Font -> ta_YSize + 2,
					GA_RelHeight,	-(Screen -> WBorTop + Screen -> Font -> ta_YSize + 2 + SizeHeight + 1 + 2 * ARROW_HEIGHT),
					GA_Width,	SizeWidth - 8,
					GA_RelRight,	-(SizeWidth - 5),

					GA_Immediate,	TRUE,
					GA_FollowMouse,	TRUE,
					GA_RelVerify,	TRUE,
					GA_RightBorder,	TRUE,

					PGA_Freedom,	FREEVERT,
					PGA_NewLook,	TRUE,
					PGA_Borderless,	TRUE,

					PGA_Visible,	1,
					PGA_Total,	1,
				TAG_DONE))
				{
					STATIC struct TagItem ArrowMappings[] =
					{
						GA_ID,	GA_ID,
						TAG_END
					};

					if(UpArrow = NewObject(NULL,"buttongclass",
						GA_ID,		GAD_UP,

						GA_Image,	UpImage,
						GA_RelRight,	-(SizeWidth - 1),
						GA_RelBottom,	-(SizeHeight - 1 + 2 * ARROW_HEIGHT),
						GA_Height,	ARROW_HEIGHT,
						GA_Width,	SizeWidth,
						GA_Immediate,	TRUE,
						GA_RelVerify,	TRUE,
						GA_Previous,	Scroller,
						GA_RightBorder,	TRUE,

						ICA_TARGET,	ICTARGET_IDCMP,
						ICA_MAP,	ArrowMappings,
					TAG_DONE))
					{
						if(DownArrow = NewObject(NULL,"buttongclass",
							GA_ID,		GAD_DOWN,

							GA_Image,	DownImage,
							GA_RelRight,	-(SizeWidth - 1),
							GA_RelBottom,	-(SizeHeight - 1 + ARROW_HEIGHT),
							GA_Height,	ARROW_HEIGHT,
							GA_Width,	SizeWidth,
							GA_Immediate,	TRUE,
							GA_RelVerify,	TRUE,
							GA_Previous,	UpArrow,
							GA_RightBorder,	TRUE,

							ICA_TARGET,	ICTARGET_IDCMP,
							ICA_MAP,	ArrowMappings,
						TAG_DONE))
							Result = TRUE;
					}
				}
			}
		}

		FreeScreenDrawInfo(Screen,DrawInfo);
	}

	return(Result);
}

	/* ReviewUpdatePot():
	 *
	 *	Update size and position of the scroller gadget.
	 */

STATIC VOID
ReviewUpdatePot()
{
	if(ReviewGlobalLines)
	{
		SetGadgetAttrs(Scroller,ReviewWindow,NULL,
			PGA_Top,	ReviewTop,
			PGA_Visible,	ReviewLines,
			PGA_Total,	ReviewGlobalLines,
		TAG_DONE);
	}
}

	/* ReviewUp(LONG Count):
	 *
	 *	Move the contents of the review buffer up.
	 */

STATIC VOID __regargs
ReviewUp(LONG Count)
{
	if(BufferLines)
	{
		if(Count == 1)
		{
			if(ReviewTop)
			{
				WORD i;

				for(i = ReviewMaxLines - 2 ; i >= 0 ; i--)
					ReviewLineWidths[i + 1] = ReviewLineWidths[i];

				ReviewTop--;

				ReviewWrites("\33[T");

				ObtainSemaphore(BufferSemaphore);

				PrintReviewLine(BufferLines[ReviewTop],1);

				ReviewUpdatePot();

				ReleaseSemaphore(BufferSemaphore);
			}
		}
		else
		{
			LONG NewTop = ReviewTop;

			if(NewTop >= Count)
				NewTop -= Count;
			else
				NewTop = 0;

			if(NewTop != ReviewTop)
			{
				ScrollReview(NewTop);

				ReviewUpdatePot();
			}
		}
	}
}

	/* ReviewDown(LONG Count):
	 *
	 *	Move the contents of the review buffer down.
	 */

STATIC VOID __regargs
ReviewDown(LONG Count)
{
	if(BufferLines)
	{
		if(Count == 1)
		{
			if(ReviewTop + ReviewLines < Lines)
			{
				LONG Last;
				WORD i;

				for(i = 0 ; i < ReviewMaxLines - 1 ; i++)
					ReviewLineWidths[i] = ReviewLineWidths[i + 1];

				ReviewTop++;

				ReviewWrites("\33[S");

				ObtainSemaphore(BufferSemaphore);

				if((Last = ReviewTop + ReviewLines) < Lines)
					PrintReviewLine(BufferLines[Last],ReviewLines + 1);

				ReviewUpdatePot();

				ReleaseSemaphore(BufferSemaphore);
			}
		}
		else
		{
			LONG NewTop = ReviewTop;

			if((NewTop + Count + ReviewLines) > Lines)
			{
				if((NewTop = Lines - ReviewLines) < 0)
					NewTop = 0;
			}
			else
				NewTop += Count;

			if(NewTop != ReviewTop)
			{
				ScrollReview(NewTop);

				ReviewUpdatePot();
			}
		}
	}
}

	/* ReviewWrites(STRPTR String,...):
	 *
	 *	Write a string into the review buffer window.
	 */

STATIC VOID __stdargs
ReviewWrites(STRPTR String,...)
{
	va_list	VarArgs;

	va_start(VarArgs,String);
	VSPrintf(SharedBuffer,String,VarArgs);
	va_end(VarArgs);

	ReviewWriteRequest -> io_Command	= CMD_WRITE;
	ReviewWriteRequest -> io_Data		= SharedBuffer;
	ReviewWriteRequest -> io_Length		= -1;

	DoIO(ReviewWriteRequest);
}

	/* FilterReviewLine(register STRPTR Line,register LONG Length):
	 *
	 *	Replace non-printable characters in the text line
	 *	to be printed.
	 */
	 

STATIC STRPTR __regargs
FilterReviewLine(register STRPTR Line,register LONG Length)
{
	STATIC UBYTE ISO[256] =
	{
		 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
		 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
		 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
		 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
		 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
		 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
		 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
		112,113,114,115,116,117,118,119,120,121,122,123,124,125,126, 46,
		 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
		 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
		160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
		176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
		192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
		208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
		224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
		240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
	};

	STATIC UBYTE TempBuffer[256];

	register STRPTR Destination = TempBuffer;

	while(Length--)
		*Destination++ = ISO[*Line++];

	return(TempBuffer);
}

	/* PrintReviewLine(STRPTR Buffer,LONG Line):
	 *
	 *	Write the contents of a buffer line into the review buffer window.
	 */

STATIC VOID __regargs
PrintReviewLine(STRPTR Buffer,LONG Line)
{
	WORD Length = Buffer[-1];

	if(Length > ReviewColumns)
		Length = ReviewColumns;

	ReviewWrites("\33[%ldH",Line);

	ReviewWriteRequest -> io_Command	= CMD_WRITE;
	ReviewWriteRequest -> io_Data		= FilterReviewLine(Buffer,Length);
	ReviewWriteRequest -> io_Length		= Length;

	DoIO(ReviewWriteRequest);

	if(Length < ReviewLineWidths[Line - 1])
		ReviewWrites("\33[0K");

	ReviewLineWidths[Line - 1] = Length;
}

	/* RefreshReview(LONG Top):
	 *
	 *	Refresh the contents of the review buffer window.
	 */

STATIC VOID __regargs
RefreshReview(LONG Top)
{
	LONG i,Last,Line = 0;

	ObtainSemaphore(BufferSemaphore);

	ReviewGlobalLines = Lines;

	if((Last = Top + ReviewLines + 1) >= Lines)
	{
		Last = Lines;

		if((Top = Last - ReviewLines) < 0)
			Top = 0;

		ReviewTop = Top;
	}

	if(BufferLines)
	{
		for(i = Top ; i < Last ; i++)
			PrintReviewLine(BufferLines[i],++Line);
	}

	if(Line <= ReviewLines)
	{
		if(Line)
		{
			for(i = Line - 1 ; i < ReviewLines ; i++)
				ReviewLineWidths[i] = 0;
		}

		ReviewWrites("\33[0J\33[3%ldm",RenderPens[1] & 7);
	}

	ReleaseSemaphore(BufferSemaphore);
}

	/* MoveUp(VOID):
	 *
	 *	Move the review buffer contents up a line.
	 */

STATIC VOID
MoveUp(VOID)
{
	LONG i;

	for(i = 0 ; i < ReviewMaxLines - 1 ; i++)
		ReviewLineWidths[i] = ReviewLineWidths[i + 1];

	ReviewWrites("\33[S");

	ObtainSemaphore(BufferSemaphore);
	PrintReviewLine(BufferLines[ReviewTop + ReviewLines],ReviewLines + 1);
	ReleaseSemaphore(BufferSemaphore);
}

	/* ScrollReview(LONG Top):
	 *
	 *	Refresh the contents of the review buffer window, reprint
	 *	as few lines as possible.
	 */

STATIC VOID __regargs
ScrollReview(LONG Top)
{
	LONG i,Last,Line = 0,Delta,Total;

	ObtainSemaphore(BufferSemaphore);

	ReviewGlobalLines = Lines;

	Delta = Top - ReviewTop;

	ReviewTop = Top;

	if((Last = Top + ReviewLines + 1) >= Lines)
		Last = Lines;

	Total = Last - Top;

	if(ABS(Delta) < ReviewLines)
	{
		if(!Delta)
		{
			ReleaseSemaphore(BufferSemaphore);

			return;
		}
		else
		{
			if(Delta < 0)
			{
				Last = Top - Delta;

				for(i = ReviewMaxLines - 1 ; i > -Delta ; i--)
					ReviewLineWidths[i] = ReviewLineWidths[i + Delta];

				ReviewWrites("\33[%ldT",-Delta);
			}
			else
			{
				for(i = Delta ; i < ReviewMaxLines ; i++)
					ReviewLineWidths[i - Delta] = ReviewLineWidths[i];

				Top += ReviewLines - Delta;
				Line = ReviewLines - Delta;

				ReviewWrites("\33[%ldS",Delta);
			}
		}
	}

	if(BufferLines)
	{
		for(i = Top ; i < Last ; i++)
			PrintReviewLine(BufferLines[i],++Line);
	}

	if(Total <= ReviewLines)
		ReviewWrites("\33[0J");

	ReleaseSemaphore(BufferSemaphore);
}

	/* ReviewQuery():
	 *
	 *	Update the current review buffer window dimensions.
	 */

STATIC BYTE
ReviewQuery()
{
	struct ConUnit	*Unit = (struct ConUnit *)ReviewWriteRequest -> io_Unit;
	BYTE		 Refresh = FALSE;

	memset(ReviewLineWidths,0,ReviewMaxLines);

	if(ReviewColumns != Unit -> cu_XMax)
	{
		Refresh = TRUE;

		ReviewColumns = Unit -> cu_XMax;
	}

	if(ReviewLines != Unit -> cu_YMax)
	{
		if(ReviewTop + ReviewLines == Lines)
		{
			LONG Delta = ReviewLines - Unit -> cu_YMax;

			if(Delta < 0)
				ReviewTop += Delta;
			else
				ReviewTop -= Delta;

			if(ReviewTop < 0)
				ReviewTop = 0;
		}

		Refresh = TRUE;

		ReviewLines = Unit -> cu_YMax;
	}

	if(Refresh)
		ReviewUpdatePot();

	return(TRUE);
}

	/* GetReviewChar(BYTE WaitForIt):
	 *
	 *	Get the next character present at the console read port.
	 */

STATIC UBYTE __regargs
GetReviewChar(BYTE WaitForIt)
{
	UBYTE Char;

	if(!WaitForIt)
	{
		if(!CheckIO(ReviewReadRequest))
			return(0);
	}

	WaitIO(ReviewReadRequest);

	Char = ReviewChar;

	ReviewReadRequest -> io_Command	= CMD_READ;
	ReviewReadRequest -> io_Data	= &ReviewChar;
	ReviewReadRequest -> io_Length	= 1;

	SendIO(ReviewReadRequest);

	return(Char);
}

	/* MarkReviewLine(STRPTR Buffer,LONG Line,LONG Column,LONG Len):
	 *
	 *	Similar to PrintReviewLine(), but also allows to mark a
	 *	certain word on the line.
	 */

STATIC VOID __regargs
MarkReviewLine(STRPTR Buffer,LONG Line,LONG Column,LONG Len)
{
	WORD Length = Buffer[-1];

	if(Length > ReviewColumns)
		Length = ReviewColumns;

	ReviewWrites("\33[%ldH",Line);

	if(Length <= Column)
	{
		ReviewWriteRequest -> io_Command	= CMD_WRITE;
		ReviewWriteRequest -> io_Data		= Buffer;
		ReviewWriteRequest -> io_Length		= Length;

		DoIO(ReviewWriteRequest);

		ReviewWrites("\33[0K");
	}
	else
	{
		ReviewWriteRequest -> io_Command	= CMD_WRITE;
		ReviewWriteRequest -> io_Data		= Buffer;
		ReviewWriteRequest -> io_Length		= Column;

		DoIO(ReviewWriteRequest);

		ReviewWrites("\33[7;3%ldm",RenderPens[1] & 7);

		ReviewWriteRequest -> io_Command	= CMD_WRITE;
		ReviewWriteRequest -> io_Data		= &Buffer[Column];
		ReviewWriteRequest -> io_Length		= Len;

		DoIO(ReviewWriteRequest);

		ReviewWrites("\33[0;3%ldm",RenderPens[1] & 7);

		if(Column + Len < Length)
		{
			ReviewWriteRequest -> io_Command	= CMD_WRITE;
			ReviewWriteRequest -> io_Data		= &Buffer[Column + Len];
			ReviewWriteRequest -> io_Length		= Length - (Column + Len);

			DoIO(ReviewWriteRequest);
		}

		ReviewWrites("\33[0K");
	}
}

	/* ReviewMarkArea(LONG Top,LONG Column,LONG Line,LONG Len,BYTE FullRefresh):
	 *
	 *	Mark a word on the current display.
	 */

STATIC VOID __regargs
ReviewMarkArea(LONG Top,LONG Column,LONG Line,LONG Len,BYTE FullRefresh)
{
	STATIC LONG LastMarked = -1;

	LONG i,Last,LineIndex = 0;

	ReviewGlobalLines = Lines;

	ObtainSemaphore(BufferSemaphore);

	if((Last = Top + ReviewLines + 1) >= Lines)
		Last = Lines;

	if(FullRefresh)
	{
		if(BufferLines)
		{
			for(i = Top ; i < Last ; i++)
			{
				if(i != Line)
					PrintReviewLine(BufferLines[i],++LineIndex);
				else
					MarkReviewLine(BufferLines[i],++LineIndex,Column,Len);
			}
		}

		if(LineIndex <= ReviewLines)
			ReviewWrites("\33[0J");
	}
	else
	{
		if(BufferLines)
		{
			for(i = Top ; i < Last ; i++)
			{
				LineIndex++;

				if(i == LastMarked)
					PrintReviewLine(BufferLines[i],LineIndex);

				if(i == Line)
					MarkReviewLine(BufferLines[i],LineIndex,Column,Len);
			}
		}
	}

	LastMarked = Line;

	ReleaseSemaphore(BufferSemaphore);
}

	/* ReviewSearch(BYTE Repeat):
	 *
	 *	Search for a certain word in the text buffer.
	 */

STATIC VOID
ReviewSearch(BYTE Repeat)
{
	if(Lines)
	{
		STATIC UBYTE SearchBuffer[256];

		LONG LineNumber;

		if(!Repeat || !SearchBuffer[0])
		{
			if(GetSearchString(ReviewWindow,NULL,SearchBuffer,&SearchForward))
			{
				if(SearchBuffer[0])
				{
					if(ReviewSearchInfo)
						DeleteSearchInfo(ReviewSearchInfo);

					ReviewSearchInfo = CreateSearchInfo(SearchBuffer,SearchForward);
				}
				else
					return;
			}
			else
				return;
		}
		else
		{
			if(!ReviewSearchInfo)
				ReviewSearchInfo = CreateSearchInfo(SearchBuffer,TRUE);
		}

		if(ReviewSearchInfo)
		{
			ObtainSemaphore(BufferSemaphore);

			LineNumber = SearchTextBuffer(ReviewSearchInfo);

			ReleaseSemaphore(BufferSemaphore);

			if(LineNumber == -1)
			{
				RefreshReview(ReviewTop);

				MyEasyRequest(ReviewWindow,LocaleString(MSG_TERMBUFFER_DID_NOT_FIND_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),SearchBuffer);

				ReviewSearchInfo -> FoundY = -1;
			}
			else
			{
				if(LineNumber < ReviewTop || LineNumber > ReviewTop + ReviewLines)
				{
					if(LineNumber + ReviewLines > Lines)
					{
						ReviewTop = Lines - ReviewLines;

						if(ReviewTop < 0)
							ReviewTop = 0;
					}
					else
						ReviewTop = LineNumber;

					ReviewUpdatePot();

					ReviewMarkArea(ReviewTop,ReviewSearchInfo -> FoundX,LineNumber,ReviewSearchInfo -> PatternWidth,TRUE);
				}
				else
					ReviewMarkArea(ReviewTop,ReviewSearchInfo -> FoundX,LineNumber,ReviewSearchInfo -> PatternWidth,FALSE);
			}
		}
	}
	else
		MyEasyRequest(ReviewWindow,LocaleString(MSG_GLOBAL_NOTHING_IN_THE_BUFFER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));
}

	/* DeleteReview():
	 *
	 *	Delete the review buffer.
	 */

VOID
DeleteReview()
{
	CheckItem(MEN_REVIEW_WINDOW,FALSE);

	if(ReviewReadRequest)
	{
		if(ReviewReadRequest -> io_Device)
		{
			if(!CheckIO(ReviewReadRequest))
				AbortIO(ReviewReadRequest);

			WaitIO(ReviewReadRequest);
		}

		FreeVec(ReviewReadRequest);

		ReviewReadRequest = NULL;
	}

	if(ReviewWriteRequest)
	{
		if(ReviewWriteRequest -> io_Device)
			CloseDevice(ReviewWriteRequest);

		DeleteIORequest(ReviewWriteRequest);

		ReviewWriteRequest = NULL;
	}

	if(ReviewWindow)
	{
		PutWindowInfo(WINDOW_REVIEW,ReviewWindow -> LeftEdge,ReviewWindow -> TopEdge,ReviewWindow -> Width,ReviewWindow -> Height);

		ClearMenuStrip(ReviewWindow);

		CloseWindow(ReviewWindow);

		ReviewWindow = NULL;
	}

	if(ReviewMenuStrip)
	{
		FreeMenus(ReviewMenuStrip);

		ReviewMenuStrip = NULL;
	}

	ReviewDeleteScroller();

	if(ReviewWritePort)
	{
		DeleteMsgPort(ReviewWritePort);

		ReviewWritePort = NULL;
	}

	if(ReviewPort)
	{
		DeleteMsgPort(ReviewPort);

		ReviewPort = NULL;
	}

	if(ReviewSearchInfo)
	{
		DeleteSearchInfo(ReviewSearchInfo);

		ReviewSearchInfo = NULL;
	}

	if(ReviewLineWidths)
	{
		FreeVec(ReviewLineWidths);

		ReviewLineWidths = NULL;
	}
}

	/* CreateReview():
	 *
	 *	Create the review buffer.
	 */

BYTE
CreateReview()
{
	if(ReviewBox . Left == -1)
	{
		ReviewBox . Left	= 0;
		ReviewBox . Top		= Window -> WScreen -> BarHeight + 1;
		ReviewBox . Width	= Window -> WScreen -> Width;
		ReviewBox . Height	= Window -> WScreen -> Height - (Window -> WScreen -> BarHeight + 1);
	}

	if(ReviewCreateScroller(Window -> WScreen))
	{
		LocalizeMenu(ReviewMenu,MSG_TERMREVIEW_PROJECT_MEN);

		if(ReviewMenuStrip = CreateMenus(ReviewMenu,TAG_DONE))
		{
			if(LayoutMenus(ReviewMenuStrip,VisualInfo,
				GTMN_TextAttr,		&UserFont,
				GTMN_NewLookMenus,	TRUE,
			TAG_DONE))
			{
				LONG	Left	= 0,
					Top	= 0,
					Width	= 0,
					Height	= 0;

				GetWindowInfo(WINDOW_REVIEW,&Left,&Top,&Width,&Height,NULL,Window -> WScreen -> WBorTop + Window -> WScreen -> WBorBottom + 1 + Window -> WScreen -> Font -> ta_YSize + 10 * CurrentFont -> tf_YSize);

				if(ReviewWindow = OpenWindowTags(NULL,
					WA_Left,		Left,
					WA_Top,			Top,
					WA_Width,		Width,
					WA_Height,		Height,
					WA_MinWidth,		Window -> WScreen -> WBorLeft + RightBorderWidth + 15 * CurrentFont -> tf_XSize,
					WA_MinHeight,		Window -> WScreen -> WBorTop + Window -> WScreen -> WBorBottom + 1 + Window -> WScreen -> Font -> ta_YSize + 2 * CurrentFont -> tf_YSize,
					WA_MaxWidth,		Window -> WScreen -> Width,
					WA_MaxHeight,		Window -> WScreen -> Height,
					WA_DragBar,		TRUE,
					WA_CloseGadget,		TRUE,
					WA_DepthGadget,		TRUE,
					WA_SizeGadget,		TRUE,
					WA_SizeBRight,		TRUE,
					WA_IDCMP,		IDCMP_IDCMPUPDATE | IDCMP_GADGETDOWN | IDCMP_SIZEVERIFY | IDCMP_MOUSEMOVE | IDCMP_MENUPICK,
					WA_Title,		LocaleString(MSG_TERMREVIEW_REVIEWBUFFER_TXT),
					WA_CustomScreen,	Window -> WScreen,
					WA_SimpleRefresh,	TRUE,
					WA_RMBTrap,		TRUE,
					WA_Activate,		TRUE,
					WA_NewLookMenus,	TRUE,
					WA_Zoom,		&ReviewBox,
					WA_Gadgets,		Scroller,
				TAG_DONE))
				{
					ReviewMaxLines = (ReviewWindow -> WScreen -> Height - (ReviewWindow -> BorderTop + ReviewWindow -> BorderBottom)) / CurrentFont -> tf_YSize;

					if(ReviewLineWidths = (UBYTE *)AllocVec(ReviewMaxLines,MEMF_ANY | MEMF_CLEAR))
					{
						SetMenuStrip(ReviewWindow,ReviewMenuStrip);

						SetFont(ReviewWindow -> RPort,CurrentFont);

						if(ReviewWritePort = CreateMsgPort())
						{
							if(ReviewPort = CreateMsgPort())
							{
								if(ReviewWriteRequest = CreateIORequest(ReviewPort,sizeof(struct IOStdReq)))
								{
									if(ReviewReadRequest = (struct IOStdReq *)AllocVec(sizeof(struct IOStdReq),MEMF_ANY | MEMF_CLEAR))
									{
										ReviewWriteRequest -> io_Data = ReviewWindow;

										if(!OpenDevice("console.device",CONU_SNIPMAP,ReviewWriteRequest,CONFLAG_NODRAW_ON_NEWSIZE))
										{
											CheckItem(MEN_REVIEW_WINDOW,TRUE);

											CopyMem(ReviewWriteRequest,ReviewReadRequest,sizeof(struct IOStdReq));

											ReviewReadRequest -> io_Message . mn_ReplyPort = ReviewPort;

											ReviewWrites("\33[0 p\33[1;11;12{\33[3%ldm",RenderPens[1] & 7);

											ReviewQuery();

											ObtainSemaphore(BufferSemaphore);

											if(ReviewTop == -1 || !Config -> CaptureConfig -> RememberBufferWindow)
											{
												switch(Config -> CaptureConfig -> OpenBufferWindow)
												{
													case BUFFER_TOP:

														ReviewTop = 0;
														break;

													case BUFFER_END:

														if((ReviewTop = Lines - ReviewLines) < 0)
															ReviewTop = 0;

														break;

													default:

														ReviewTop = 0;
														break;
												}
											}

											if(ReviewTop > Lines - ReviewLines)
											{
												if((ReviewTop = Lines - ReviewLines) < 0)
													ReviewTop = 0;
											}

											ReleaseSemaphore(BufferSemaphore);

											RefreshReview(ReviewTop);

											ReviewUpdatePot();

											ReviewReadRequest -> io_Command	= CMD_READ;
											ReviewReadRequest -> io_Data	= &ReviewChar;
											ReviewReadRequest -> io_Length	= 1;

											SendIO(ReviewReadRequest);

											ReviewWindow -> Flags &= ~WFLG_RMBTRAP;

											return(TRUE);
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	DeleteReview();

	return(FALSE);
}

	/* UpdateReview():
	 *
	 *	Update the contents of the review buffer window.
	 */

VOID
UpdateReview(BYTE Force)
{
	if(ReviewPort)
	{
		if(Force)
		{
			if(Lines)
			{
				if(ReviewTop > 0)
				{
					SetGadgetAttrs(Scroller,ReviewWindow,NULL,
						PGA_Top,--ReviewTop,
					TAG_DONE);
				}
				else
					MoveUp();
			}
		}
		else
		{
			if(Lines >= ReviewGlobalLines && ReviewGlobalLines <= ReviewLines)
				RefreshReview(ReviewTop);
		}

		ReviewGlobalLines = Lines;

		if(!Lines)
		{
			SetGadgetAttrs(Scroller,ReviewWindow,NULL,
				PGA_Top,	ReviewTop = 0,
				PGA_Visible,	1,
				PGA_Total,	1,
			TAG_DONE);

			ReviewWrites("\f\33[3%ldm",RenderPens[1] & 7);
		}
		else
			ReviewUpdatePot();
	}
}

	/* MoveReview(BYTE Mode):
	 *
	 *	Move the currently visible review area.
	 */

VOID
MoveReview(BYTE Mode)
{
	LONG NewTop;

	switch(Mode)
	{
		case REVIEW_MOVE_TOP:

			if(ReviewTop)
			{
				ScrollReview(0);

				ReviewUpdatePot();
			}

			break;

		case REVIEW_MOVE_BOTTOM:

			NewTop = Lines - ReviewLines;

			if(NewTop < 0)
				NewTop = 0;

			if(ReviewTop != NewTop)
			{
				ScrollReview(NewTop);

				ReviewUpdatePot();
			}

			break;

		case REVIEW_MOVE_UP:

			ReviewUp(ReviewLines);
			break;

		case REVIEW_MOVE_DOWN:

			ReviewDown(ReviewLines);
			break;
	}
}

	/* HandleReview():
	 *
	 *	Process console and user input.
	 */

BYTE
HandleReview()
{
	UBYTE	IClass;
	UWORD	ICode,
		IQualifier;
	BYTE	Result = FALSE;

	if(ReviewWindow)
	{
		struct IntuiMessage	*Message;
		struct MenuItem		*MenuItem;
		struct TagItem		*TagList;
		ULONG			 IClass;

		if(Message = (struct IntuiMessage *)GetMsg(ReviewWindow -> UserPort))
		{
			IClass		= Message -> Class;
			ICode		= Message -> Code;
			IQualifier	= Message -> Qualifier;
			TagList		= (struct TagItem *)Message -> IAddress;

			ReplyMsg((struct Message *)Message);

			switch(IClass)
			{
				case IDCMP_IDCMPUPDATE:

					switch(GetTagData(GA_ID,0,TagList))
					{
						case GAD_UP:	ReviewUp(1);
								break;

						case GAD_DOWN:	ReviewDown(1);
								break;
					}

					break;

				case IDCMP_GADGETDOWN:
				case IDCMP_MOUSEMOVE:

					if(ReviewGlobalLines > ReviewLines)
					{
						LONG NewTop;

						if(GetAttr(PGA_Top,Scroller,&NewTop))
							ScrollReview(NewTop);
					}

					break;

				case IDCMP_MENUPICK:

					while(ICode != MENUNULL)
					{
						MenuItem = ItemAddress(ReviewMenuStrip,ICode);

						switch((ULONG)GTMENUITEM_USERDATA(MenuItem))
						{
							case MEN_SEARCH:

								BlockWindow(ReviewWindow);

								ReviewSearch(FALSE);

								ReleaseWindow(ReviewWindow);

								break;

							case MEN_REPEAT:

								BlockWindow(ReviewWindow);

								ReviewSearch(TRUE);

								ReleaseWindow(ReviewWindow);

								break;

							case MEN_QUITBUF:

								DeleteReview();
								return(TRUE);

							case MEN_CLEARBUF:

								if(Lines)
								{
									BlockWindows();

									BlockWindow(ReviewWindow);

									if(IQualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
									{
										FreeBuffer();

										ReleaseWindow(ReviewWindow);

										ReleaseWindows();

										return(TRUE);
									}
									else
									{
										if(MyEasyRequest(ReviewWindow,LocaleString(MSG_TERMBUFFER_BUFFER_STILL_HOLDS_LINES_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT),Lines))
										{
											FreeBuffer();

											ReleaseWindow(ReviewWindow);

											ReleaseWindows();

											return(TRUE);
										}
									}

									ReleaseWindow(ReviewWindow);

									ReleaseWindows();
								}

								break;
						}

						ICode = MenuItem -> NextSelect;
					}

					break;
			}

			Result = TRUE;
		}
	}

		/* Check for raw input events. */

	if(ReviewPort)
	{
		UBYTE Char;

			/* Control sequence available? */

		if((Char = GetReviewChar(FALSE)) == CSI)
		{
			LONG	Len;
			ULONG	Prev;
			UBYTE	InputBuffer[257];
			WORD	Count = 0;

				/* Try to read the entire sequence. */

			while(Char = GetReviewChar(FALSE))
			{
				SharedBuffer[Count++] = Char;

				if(Char != ' ' && Char != ';' && (Char < '0' || Char > '9'))
					break;
			}

				/* Provide termination. */

			SharedBuffer[Count] = 0;

				/* Raw event? */

			if(Char == '|')
				SplitEventReport(SharedBuffer,Count,&IClass,&ICode,&IQualifier,&Prev);
			else
				IClass = 0;

			if(!strcmp(SharedBuffer,"0 v"))
			{
				if(!ClipInput)
				{
					if(!OpenClip(Config -> ClipConfig -> ClipboardUnit))
						ClipInput = TRUE;
					else
						ClipInput = FALSE;

					ClipXerox = FALSE;
				}

				if(ClipInput)
				{
						/* Is a send-delay enabled? */

					if(Config -> ClipConfig -> CharDelay || Config -> ClipConfig -> LineDelay)
					{
						while((Len = GetClip(InputBuffer,256,FALSE)) > 0)
						{
							WORD i;

								/* Run down the buffer... */

							for(i = 0 ; i < Len ; i++)
							{
								SerWrite(&InputBuffer[i],1);

									/* What kind of delay
									 * are we to provide?
									 */

								if(InputBuffer[i] == '\r')
								{
									if(Config -> ClipConfig -> LineDelay)
										WaitTime(Config -> ClipConfig -> LineDelay / 100,(Config -> ClipConfig -> LineDelay % 100) * 10000);
								}
								else
								{
									if(Config -> ClipConfig -> CharDelay)
										WaitTime(Config -> ClipConfig -> CharDelay / 100,(Config -> ClipConfig -> CharDelay % 100) * 10000);
								}
							}
						}
					}
					else
					{
						while((Len = GetClip(InputBuffer,256,FALSE)) > 0)
							SerWrite(InputBuffer,Len);
					}

					CloseClip();

					ClipInput = ClipPrefix = FALSE;
				}
			}
			else
			{
					/* Check the event type. */

				switch(IClass)
				{
					case 1:

							/* Convert the event... */

						Char = ConvertTheKey(InputBuffer,&Len,ICode,IQualifier,Prev);

							/* Lather, rinse, process as usual. */

						if(Len)
						{
							switch(CharType[Char])
							{
								case CHAR_HELP:

									GuideDisplay(CONTEXT_TEXTBUFFER);

									Len = 0;

									break;

								case CHAR_CURSOR:

									if(ClipInput)
									{
										CloseClip();

										ClipInput = ClipXerox = ClipPrefix = FALSE;
									}

									if(Char == CUP || Char == CDN)
									{
											/* Find the approriate qualifier. */

										if(IQualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
										{
											if(Char == CUP)
												ReviewUp(ReviewLines);
											else
												ReviewDown(ReviewLines);
										}
										else
										{
											if(IQualifier & (IEQUALIFIER_CONTROL | IEQUALIFIER_LALT | IEQUALIFIER_RALT))
											{
												if(Char == CUP)
												{
													if(ReviewTop)
													{
														ScrollReview(0);

														ReviewUpdatePot();
													}
												}
												else
												{
													LONG NewTop = Lines - ReviewLines;

													if(NewTop < 0)
														NewTop = 0;

													if(NewTop != ReviewTop)
													{
														ScrollReview(NewTop);

														ReviewUpdatePot();
													}
												}
											}
											else
											{
												if(Char == CUP)
													ReviewUp(1);
												else
													ReviewDown(1);
											}
										}
									}

									Len = 0;

									break;

									/* Any function key pressed? */

								case CHAR_FUNCTION:

									if(ClipInput)
									{
										CloseClip();

										ClipInput = ClipXerox = ClipPrefix = FALSE;
									}

									if(IQualifier & IEQUALIFIER_CONTROL)
										SerialCommand(MacroKeys -> Keys[3][Char - FN1]);
									else
									{
										if(IQualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
											SerialCommand(MacroKeys -> Keys[2][Char - FN1]);
										else
										{
											if(IQualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
												SerialCommand(MacroKeys -> Keys[1][Char - FN1]);
											else
												SerialCommand(MacroKeys -> Keys[0][Char - FN1]);
										}
									}

									Len = 0;

									break;

									/* Anything else? */

								default:

									if(Len == 1 && (IQualifier & IEQUALIFIER_NUMERICPAD) && (Config -> EmulationConfig -> NumericMode == KEYMODE_APPLICATION))
									{
										STATIC STRPTR ApplicationTable[20][2] =
										{
											"0",	"\033Op",
											"1",	"\033Oq",
											"2",	"\033Or",
											"3",	"\033Os",
											"4",	"\033Ot",
											"5",	"\033Ou",
											"6",	"\033Ov",
											"7",	"\033Ow",
											"8",	"\033Ox",
											"9",	"\033Oy",
											"-",	"\033Om",
											"+",	"\033Ol",	/* This should really be a comma */
											".",	"\033On",

											"[",	"\033OP",
											"{",	"\033OP",
											"]",	"\033OQ",
											"}",	"\033OQ",
											"/",	"\033OR",
											"*",	"\033OS",

											"\r",	"\033OM"
										};

										WORD i;

										for(i = 0 ; i < 20 ; i++)
										{
											if(Char == ApplicationTable[i][0][0])
											{
												if(ClipInput)
												{
													CloseClip();

													ClipInput = ClipXerox = ClipPrefix = FALSE;
												}

												SerWrite(ApplicationTable[i][1],strlen(ApplicationTable[i][1]));

												Len = 0;

												break;
											}
										}
									}

									break;
							}

								/* Any characters to send? */

							if(Len)
							{
								STRPTR	Buffer = InputBuffer;
								UBYTE	Mask,
									c;

									/* Strip the high order bit? */

								if(Config -> SerialConfig -> StripBit8)
									Mask = 0x7F;
								else
									Mask = 0xFF;

								if(ClipInput)
								{
									CloseClip();

									ClipInput = ClipXerox = ClipPrefix = FALSE;
								}

								while(Len--)
								{
									switch(CharType[c = (*Buffer++) & Mask])
									{
										case CHAR_ENTER:

											if(Status == STATUS_HOLDING)
												DoBeep();
											else
											{
												switch(Config -> TerminalConfig -> SendLF)
												{
													case LF_ASLF:

														SerWrite("\n",1);
														break;

													case LF_ASLFCR:

														SerWrite("\n\r",2);
														break;
												}
											}

											break;

										case CHAR_RETURN:

											if(Status == STATUS_HOLDING)
												DoBeep();
											else
											{
												switch(Config -> TerminalConfig -> SendCR)
												{
													case CR_ASCR:

														SerWrite("\r",1);
														break;

													case CR_ASCRLF:

														SerWrite("\r\n",2);
														break;
												}
											}

											break;

											/* Stop in/output. */

										case CHAR_XON:

											if(Status == STATUS_HOLDING)
												DoBeep();
											else
											{
												if(Config -> SerialConfig -> xONxOFF)
													Status = STATUS_HOLDING;

												if(Config -> SerialConfig -> PassThrough)
													SerWrite(&c,1);
											}

											break;

											/* Restart in/output. */

										case CHAR_XOFF:

											if(Status == STATUS_HOLDING)
												Status = STATUS_READY;

											if(Config -> SerialConfig -> PassThrough)
												SerWrite(&c,1);

											break;

											/* Any other character. */

										case CHAR_VANILLA:

											if(Status == STATUS_HOLDING)
												DoBeep();
											else
											{
												if(Config -> TerminalConfig -> FontMode == FONT_IBM)
												{
														/* Convert special
														 * Amiga characters into
														 * alien IBM dialect.
														 */

													if(IBMConversion[c])
														SerWrite(&IBMConversion[c],1);
													else
														SerWrite(&c,1);
												}
												else
													SerWrite(&c,1);
											}

											break;
									}
								}
							}
						}

						break;

					case 11:

						DeleteReview();

						return(Result);

					case 12:

						if(ReviewQuery())
							RefreshReview(ReviewTop);

						break;
				}
			}

			return(TRUE);
		}
	}

	return(Result);
}
