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

#include "termGlobal.h"

	/* Handy signal masks. */

#define SIG_REVIEWPORT		(1L << ReviewPort -> mp_SigBit)
#define SIG_REVIEWWINDOW	(1L << ReviewWindow -> UserPort -> mp_SigBit)

	/* 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 = { -1 };
STATIC struct IOStdReq		*ReviewWriteRequest,
				*ReviewReadRequest;

STATIC struct MsgPort		*ReviewPort,
				*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,
				 IgnoreCase	= TRUE;

STATIC UBYTE			*ReviewLineWidths;
STATIC LONG			 ReviewMaxLines;

STATIC struct MsgQueue		*ReviewQueue;
STATIC struct Task		*ReviewTask;

STATIC WORD			 ReviewPen;

	/* 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 __far 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 __far 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",ReviewPen);
	}

	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",ReviewPen);

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

		DoIO(ReviewWriteRequest);

		ReviewWrites("\33[0;3%ldm",ReviewPen);

		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 __regargs
ReviewSearch(BYTE Repeat)
{
	if(Lines)
	{
		STATIC UBYTE __far SearchBuffer[256];

		LONG LineNumber;

		if(!Repeat || !SearchBuffer[0])
		{
			struct Hook HistoryHook;

			HistoryHook . h_Data = &ReviewBufferHistory;

			if(GetSearchString(ReviewWindow,NULL,SearchBuffer,&HistoryHook,&SearchForward,&IgnoreCase))
			{
				if(SearchBuffer[0])
				{
					if(ReviewSearchInfo)
						DeleteSearchInfo(ReviewSearchInfo);

					ReviewSearchInfo = CreateSearchInfo(SearchBuffer,SearchForward,IgnoreCase);
				}
				else
					return;
			}
			else
				return;
		}
		else
		{
			if(!ReviewSearchInfo)
				ReviewSearchInfo = CreateSearchInfo(SearchBuffer,TRUE,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));
}

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

			WaitIO(ReviewReadRequest);
		}

		FreeVecPooled(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);

		LT_DeleteWindowLock(ReviewWindow);

		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)
	{
		FreeVecPooled(ReviewLineWidths);

		ReviewLineWidths = NULL;
	}

	if(ReviewQueue)
	{
		DeleteMsgQueue(ReviewQueue);

		ReviewQueue = NULL;
	}
}

STATIC BOOLEAN
LocalCreateReview(VOID)
{
	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(ReviewQueue = CreateMsgQueue(NULL,0))
	{
		if(ReviewCreateScroller(Window -> WScreen))
		{
			LocalizeMenu(ReviewMenu,MSG_TERMREVIEW_PROJECT_MEN);

			if(ReviewMenuStrip = CreateMenus(ReviewMenu,TAG_DONE))
			{
				if(LayoutMenus(ReviewMenuStrip,VisualInfo,
					AmigaGlyph ? GTMN_AmigaKey :  TAG_IGNORE, AmigaGlyph,
					CheckGlyph ? GTMN_Checkmark : TAG_IGNORE, CheckGlyph,

					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,

						AmigaGlyph ? WA_AmigaKey  : TAG_IGNORE, AmigaGlyph,
						CheckGlyph ? WA_Checkmark : TAG_IGNORE, CheckGlyph,
					TAG_DONE))
					{
						ReviewMaxLines = (ReviewWindow -> WScreen -> Height - (ReviewWindow -> BorderTop + ReviewWindow -> BorderBottom)) / CurrentFont -> tf_YSize;

						if(ReviewLineWidths = (UBYTE *)AllocVecPooled(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 *)AllocVecPooled(sizeof(struct IOStdReq),MEMF_ANY | MEMF_CLEAR))
										{
											ReviewWriteRequest -> io_Data = ReviewWindow;

											if(!OpenDevice("console.device",CONU_SNIPMAP,ReviewWriteRequest,CONFLAG_NODRAW_ON_NEWSIZE))
											{
												CopyMem(ReviewWriteRequest,ReviewReadRequest,sizeof(struct IOStdReq));

												ReviewReadRequest -> io_Message . mn_ReplyPort = ReviewPort;

												switch(Config -> ScreenConfig -> ColourMode)
												{
													case COLOUR_AMIGA:
													case COLOUR_MONO:

														ReviewPen = 1;
														break;

													case COLOUR_EIGHT:
													case COLOUR_SIXTEEN:

														ReviewPen = 7;
														break;
												}

												ReviewPen = GetPenIndex(ReviewPen) & 7;

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

												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);
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	LocalDeleteReview();

	return(FALSE);
}

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

STATIC VOID __regargs
LocalUpdateReview(BYTE Force)
{
	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",ReviewPen);
	}
	else
		ReviewUpdatePot();
}

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

STATIC VOID __regargs
LocalMoveReview(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;
	}
}

STATIC VOID __stdargs
ReviewDestructor(struct MsgItem *Item)
{
	Signal(ReviewTask,SIG_HANDSHAKE);
}

STATIC VOID __regargs
ReviewSerWrite(APTR Data,LONG Size)
{
	struct DataMsg Msg;

	InitMsgItem(&Msg,ReviewDestructor);

	Msg . Type = DATAMSGTYPE_WRITE;
	Msg . Data = Data;
	Msg . Size = Size;

	Forbid();

	ClrSignal(SIG_HANDSHAKE);

	PutMsgItem(SpecialQueue,(struct MsgItem *)&Msg);

	Wait(SIG_HANDSHAKE);

	Permit();
}

STATIC VOID __saveds
ReviewTaskEntry(VOID)
{
	struct Process *Father = ThisProcess;

	if(LocalCreateReview())
	{
		ULONG	Signals;
		BOOLEAN	Done = FALSE;

		ReviewTask = SysBase -> ThisTask;

		Signal(ThisProcess,SIG_HANDSHAKE);

		do
		{
			Signals = Wait(SIG_KILL | SIG_REVIEWPORT | SIG_REVIEWWINDOW | ReviewQueue -> SigMask);

			if(Signals & SIG_KILL)
				break;

			if(Signals & SIG_REVIEWPORT)
			{
				UBYTE Char;

					/* Control sequence available? */

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

						/* Try to read the entire sequence. */

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

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

						/* Provide termination. */

					InputBuffer[Count] = 0;

						/* Raw event? */

					if(!strcmp(InputBuffer,"?~"))
						GuideDisplay(CONTEXT_TEXTBUFFER);

					if(!strcmp(InputBuffer,"A"))
						ReviewUp(1);

					if(!strcmp(InputBuffer,"B"))
						ReviewDown(1);

					if(!strcmp(InputBuffer,"T"))
						ReviewUp(ReviewLines);

					if(!strcmp(InputBuffer,"S"))
						ReviewDown(ReviewLines);

					if(!memcmp(InputBuffer,"11;",3))
					{
						Done = TRUE;

						if(!(SetSignal(0,SIG_KILL) & SIG_KILL))
							Father = NULL;
					}

					if(!memcmp(InputBuffer,"12;",3))
					{
						if(ReviewQuery())
							RefreshReview(ReviewTop);
					}

					if(!strcmp(InputBuffer,"0 v"))
					{
						struct DataMsg Msg;

						InitMsgItem(&Msg,ReviewDestructor);

						Msg . Type = DATAMSGTYPE_WRITECLIP;
						Msg . Size = Config -> ClipConfig -> ClipboardUnit;

						Forbid();

						ClrSignal(SIG_HANDSHAKE);

						PutMsgItem(SpecialQueue,(struct MsgItem *)&Msg);

						Wait(SIG_HANDSHAKE);

						Permit();
					}
				}
				else
				{
					if(Char)
					{
						if(Config -> SerialConfig -> StripBit8)
							Char &= 0x7F;

						if(Status == STATUS_HOLDING)
						{
							if(Char == XOF)
							{
								ReviewSerWrite(&Char,1);

								Status = STATUS_READY;
							}
						}
						else
						{
								/* Convert chars
								 * as approriate.
								 */

							if(Char == '\n')
							{
								switch(Config -> TerminalConfig -> SendLF)
								{
									case LF_ASLF:

										goto SendIt;

									case LF_ASLFCR:

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

							if(Char == '\r')
							{
								switch(Config -> TerminalConfig -> SendCR)
								{
									case CR_ASCR:

										goto SendIt;

									case CR_ASCRLF:

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

								/* Stop in/output. */

							if(Char == XON)
							{
								if(Config -> SerialConfig -> PassThrough)
									ReviewSerWrite(&Char,1);
								else
								{
									if(Config -> SerialConfig -> xONxOFF)
										Status = STATUS_HOLDING;
								}
							}

								/* Restart in/output. */

							if(Char == XOF)
							{
								if(Config -> SerialConfig -> PassThrough)
									ReviewSerWrite(&Char,1);
							}

								/* Convert special
								 * Amiga characters into
								 * alien IBM dialect.
								 */

SendIt:							if(Config -> TerminalConfig -> FontMode == FONT_IBM)
							{
								if(IBMConversion[Char])
									ReviewSerWrite(&IBMConversion[Char],1);
								else
									ReviewSerWrite(&Char,1);
							}
							else
								ReviewSerWrite(&Char,1);
						}
					}
				}
			}

			if(Signals & SIG_REVIEWWINDOW)
			{
				struct IntuiMessage	*Message;
				struct MenuItem		*MenuItem;
				struct TagItem		*TagList;
				ULONG			 MsgClass;
				UWORD			 MsgCode,
							 MsgQualifier;

				while(Message = (struct IntuiMessage *)GetMsg(ReviewWindow -> UserPort))
				{
					MsgClass	= Message -> Class;
					MsgCode		= Message -> Code;
					MsgQualifier	= Message -> Qualifier;
					TagList		= (struct TagItem *)Message -> IAddress;

					ReplyMsg((struct Message *)Message);

					switch(MsgClass)
					{
						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,(ULONG *)&NewTop))
									ScrollReview(NewTop);
							}

							break;

						case IDCMP_MENUPICK:

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

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

										LT_LockWindow(ReviewWindow);

										ReviewSearch(FALSE);

										LT_UnlockWindow(ReviewWindow);

										break;

									case MEN_REPEAT:

										LT_LockWindow(ReviewWindow);

										ReviewSearch(TRUE);

										LT_UnlockWindow(ReviewWindow);

										break;

									case MEN_QUITBUF:

										Done = TRUE;

										if(!(SetSignal(0,SIG_KILL) & SIG_KILL))
											Father = NULL;

										break;

									case MEN_CLEARBUF:

										if(Lines)
										{
											if(MsgQualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
												FreeBuffer();
											else
											{
												if(MyEasyRequest(ReviewWindow,LocaleString(MSG_TERMBUFFER_BUFFER_STILL_HOLDS_LINES_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT),Lines))
													FreeBuffer();
											}
										}

										break;
								}

								MsgCode = MenuItem -> NextSelect;
							}

							break;
					}
				}
			}

			if(Signals & ReviewQueue -> SigMask)
			{
				struct DataMsg *Msg;

				while(Msg = (struct DataMsg *)GetMsgItem(ReviewQueue))
				{
					switch(Msg -> Type)
					{
						case DATAMSGTYPE_UPDATEREVIEW:

							LocalUpdateReview(Msg -> Size);
							break;

						case DATAMSGTYPE_MOVEREVIEW:

							LocalMoveReview(Msg -> Size);
							break;
					}

					DeleteMsgItem(Msg);
				}
			}
		}
		while(!Done);

		LocalDeleteReview();
	}

	Forbid();

	ReviewTask = NULL;

	if(Father)
		Signal(Father,SIG_HANDSHAKE);
	else
		CheckItem(MEN_REVIEW_WINDOW,FALSE);
}

STATIC VOID __stdargs
ReviewClientDestructor(struct DataMsg *Item)
{
	Signal((struct Task *)Item -> Data,SIG_HANDSHAKE);
}

VOID __regargs
UpdateReview(BYTE Force)
{
	if(ReviewQueue)
	{
		if(SysBase -> ThisTask != ReviewTask)
		{
			struct DataMsg Msg;

			InitMsgItem(&Msg,ReviewClientDestructor);

			Msg . Type = DATAMSGTYPE_UPDATEREVIEW;
			Msg . Data = (UBYTE *)SysBase -> ThisTask;
			Msg . Size = Force;

			Forbid();

			ClrSignal(SIG_HANDSHAKE);

			PutMsgItem(ReviewQueue,(struct MsgItem *)&Msg);

			Wait(SIG_HANDSHAKE);

			Permit();
		}
		else
			LocalUpdateReview(Force);
	}
}

VOID __regargs
MoveReview(BYTE Mode)
{
	if(ReviewQueue)
	{
		if(SysBase -> ThisTask != ReviewTask)
		{
			struct DataMsg Msg;

			InitMsgItem(&Msg,ReviewClientDestructor);

			Msg . Type = DATAMSGTYPE_MOVEREVIEW;
			Msg . Data = (UBYTE *)SysBase -> ThisTask;
			Msg . Size = Mode;

			Forbid();

			ClrSignal(SIG_HANDSHAKE);

			PutMsgItem(ReviewQueue,(struct MsgItem *)&Msg);

			Wait(SIG_HANDSHAKE);

			Permit();
		}
		else
			LocalMoveReview(Mode);
	}
}

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

	if(ReviewTask)
	{
		Forbid();

		ClrSignal(SIG_HANDSHAKE);

		Signal(ReviewTask,SIG_KILL);

		Wait(SIG_HANDSHAKE);

		Permit();
	}
}

BYTE
CreateReview()
{
	if(ReviewTask)
		return(TRUE);
	else
	{
		BYTE Result = FALSE;

		Forbid();

		if(CreateTask("term Review Task",SysBase -> ThisTask -> tc_Node . ln_Pri,ReviewTaskEntry,4000))
		{
			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);

			if(ReviewTask)
			{
				CheckItem(MEN_REVIEW_WINDOW,TRUE);

				Result = TRUE;
			}
		}

		Permit();

		return(Result);
	}
}
