/*
**	termReviewBuffer.c
**
**	Support routines for the review buffer
**
**	Copyright © 1990-1995 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 };

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 BYTE			 SearchForward	= TRUE,
				 IgnoreCase	= TRUE,
				 WholeWords	= FALSE;

STATIC UBYTE			*ReviewLineWidths;
STATIC LONG			 ReviewMaxLines;

STATIC struct MsgQueue		*ReviewQueue;
STATIC struct Process		*ReviewProcess;

STATIC WORD			 ReviewPen;
STATIC BYTE			 ReviewSignal;

STATIC struct TextFont		*LocalFont;
STATIC struct TTextAttr		 LocalTextFont;
STATIC UBYTE __far		 LocalTextFontName[MAX_FILENAME_LENGTH];

	/* 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))
	{
		struct Image	*SizeImage;
		ULONG		 SizeWidth,
				 SizeHeight,
				 ArrowHeight;
		UWORD		 SizeType;

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

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

			SizeType	= SYSISIZE_LOWRES;
		}

		if(SizeImage = (struct Image *)NewObject(NULL,"sysiclass",
			SYSIA_Size,	SizeType,
			SYSIA_Which,	SIZEIMAGE,
			SYSIA_DrawInfo,	DrawInfo,
		TAG_DONE))
		{
			GetAttr(IA_Width,	SizeImage,&SizeWidth);
			GetAttr(IA_Height,	SizeImage,&SizeHeight);

			DisposeObject(SizeImage);

			RightBorderWidth = SizeWidth;

			if(UpImage = (struct Image *)NewObject(NULL,"sysiclass",
				SYSIA_Size,	SizeType,
				SYSIA_Which,	UPIMAGE,
				SYSIA_DrawInfo,	DrawInfo,
			TAG_DONE))
			{
				GetAttr(IA_Height,UpImage,&ArrowHeight);

				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 * ArrowHeight),
						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 * ArrowHeight),
							GA_Height,	ArrowHeight,
							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 + ArrowHeight),
								GA_Height,	ArrowHeight,
								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,...)
{
	UBYTE	LocalBuffer[256];
	va_list	VarArgs;

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

	ReviewWriteRequest -> io_Command	= CMD_WRITE;
	ReviewWriteRequest -> io_Data		= LocalBuffer;
	ReviewWriteRequest -> io_Length		= strlen(LocalBuffer);

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

		if(Screen)
			ReviewWrites("\33[0J\33[3%ldm",ReviewPen);
		else
			ReviewWrites("\33[0J");
	}

	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)
	{
		Refresh = TRUE;

		if(Unit -> cu_YMax < ReviewGlobalLines)
		{
			LONG Delta = Unit -> cu_YMax - ReviewLines;

			ReviewLines = Unit -> cu_YMax;

			if(Delta > 0)
			{
				if((ReviewTop = ReviewTop - Delta) < 0)
					ReviewTop = 0;
			}
			else
			{
				ReviewTop -= Delta;

				if(ReviewTop + ReviewLines > ReviewGlobalLines)
					ReviewTop = ReviewGlobalLines - ReviewLines;
			}
		}
		else
		{
			ReviewTop	= 0;
			ReviewLines	= ReviewGlobalLines;
		}
	}

	if(Refresh)
		ReviewUpdatePot();

	return(Refresh);
}

	/* 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;

	SetSignal(0,SIG_REVIEWPORT);

	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(struct SearchInfo *SearchInfo):
	 *
	 *	Search for a certain word in the text buffer.
	 */

STATIC VOID __regargs
ReviewSearch(struct SearchInfo *SearchInfo,STRPTR SearchBuffer)
{
	LT_LockWindow(ReviewWindow);

	if(Lines)
	{
		LONG LineNumber;

		ObtainSemaphore(BufferSemaphore);

		LineNumber = SearchTextBuffer(SearchInfo);

		ReleaseSemaphore(BufferSemaphore);

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

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

			SearchInfo -> 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,SearchInfo -> FoundX,LineNumber,SearchInfo -> PatternWidth,TRUE);
			}
			else
				ReviewMarkArea(ReviewTop,SearchInfo -> FoundX,LineNumber,SearchInfo -> PatternWidth,FALSE);
		}
	}
	else
		MyEasyRequest(ReviewWindow,LocaleString(MSG_GLOBAL_NOTHING_IN_THE_BUFFER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

	LT_UnlockWindow(ReviewWindow);
}

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(LocalFont)
	{
		CloseFont(LocalFont);

		LocalFont = NULL;
	}

	if(ReviewMenuStrip)
	{
		FreeMenus(ReviewMenuStrip);

		ReviewMenuStrip = NULL;
	}

	ReviewDeleteScroller();

	if(ReviewWritePort)
	{
		DeleteMsgPort(ReviewWritePort);

		ReviewWritePort = NULL;
	}

	if(ReviewPort)
	{
		DeleteMsgPort(ReviewPort);

		ReviewPort = NULL;
	}

	if(ReviewSignal != -1)
	{
		FreeSignal(ReviewSignal);

		ReviewSignal = -1;
	}

	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((ReviewSignal = AllocSignal(-1)) != -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_MenuHelp,		TRUE,
							WA_IDCMP,		IDCMP_IDCMPUPDATE | IDCMP_GADGETDOWN | IDCMP_SIZEVERIFY | IDCMP_MOUSEMOVE | IDCMP_MENUPICK | IDCMP_MENUHELP,
							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);

								memcpy(&LocalTextFont,&TextAttr,sizeof(struct TTextAttr));

								LocalTextFont . tta_Name	= LocalTextFontName;
								LocalTextFont . tta_YSize	= Config -> TerminalConfig -> TextFontHeight;

								strcpy(LocalTextFontName,Config -> TerminalConfig -> TextFontName);

								if(!Config -> CaptureConfig -> ConvertChars && Config -> TerminalConfig -> FontMode != FONT_STANDARD)
								{
									strcpy(LocalTextFontName,Config -> TerminalConfig -> IBMFontName);

									LocalTextFont . tta_YSize = Config -> TerminalConfig -> IBMFontHeight;
								}

								if(LocalFont = OpenDiskFont(&LocalTextFont))
									SetFont(ReviewWindow -> RPort,LocalFont);

								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;

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

													SendIO(ReviewReadRequest);

													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;

													if(Screen)
														ReviewWrites("\33[0 p\33[11;12\173\33[3%ldm",ReviewPen);
													else
														ReviewWrites("\33[0 p\33[11;12\173");

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

													ReviewGlobalLines = Lines;

													ReleaseSemaphore(BufferSemaphore);

													ReviewQuery();

													RefreshReview(ReviewTop);

													ReviewUpdatePot();

													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)
		{
			if(ReviewLines < ((struct ConUnit *)ReviewWriteRequest -> io_Unit) -> cu_YMax)
			{
				if(((struct ConUnit *)ReviewWriteRequest -> io_Unit) -> cu_YMax < Lines)
					ReviewLines = ((struct ConUnit *)ReviewWriteRequest -> io_Unit) -> cu_YMax;
				else
					ReviewLines = Lines;
			}

			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
ReviewClientDestructor(struct DataMsg *Item)
{
	Signal((struct Task *)Item -> Client,Item -> Mask);
}

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

	InitMsgItem(&Msg,ReviewClientDestructor);

	Msg . Type	= DATAMSGTYPE_WRITE;
	Msg . Data	= Data;
	Msg . Size	= Size;
	Msg . Client	= ReviewProcess;
	Msg . Mask	= ReviewSignal;

	Forbid();

	ClrSignal(Msg . Mask);

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

	Wait(Msg . Mask);

	Permit();
}

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

	if(LocalCreateReview())
	{
		UBYTE			 SearchBuffer[256];
		struct Hook		 HistoryHook;
		ULONG			 Signals;
		BOOLEAN			 Done		= FALSE;
		struct SearchInfo	*SearchInfo	= NULL;
		struct SearchContext	*Context	= NULL;

		HistoryHook . h_Data = &ReviewBufferHistory;

		Forbid();

		ReviewProcess = (struct Process *)SysBase -> ThisTask;

		Signal(ThisProcess,SIG_HANDSHAKE);

		Permit();

		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,ReviewClientDestructor);

						Msg . Type	= DATAMSGTYPE_WRITECLIP;
						Msg . Size	= Config -> ClipConfig -> ClipboardUnit;
						Msg . Client	= ReviewProcess;
						Msg . Mask	= ReviewSignal;

						Forbid();

						ClrSignal(Msg . Mask);

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

						Wait(Msg . Mask);

						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 EOL_LF:

										goto SendIt;

									case EOL_LFCR:

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

									case EOL_CRLF:

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

									case EOL_CR:

										ReviewSerWrite("\r",1);
										break;
								}
							}

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

										goto SendIt;

									case EOL_LFCR:

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

									case EOL_CRLF:

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

									case EOL_LF:

										ReviewSerWrite("\n",1);
										break;
								}
							}

								/* Stop in/output. */

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

								if(Config -> SerialConfig -> xONxOFF)
									Status = STATUS_HOLDING;
							}

								/* Restart in/output. */

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

								if(Status == STATUS_HOLDING)
									Status = STATUS_READY;
							}

								/* 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))
				{
					if(Context && Context -> SearchWindow == Message -> IDCMPWindow)
					{
						MsgClass = NULL;

						if(HandleSearchMessage(Context,&Message))
						{
							BOOLEAN Ok = Context -> Ok;

							DeleteSearchContext(Context);

							Context = NULL;

							if(Ok)
							{
								if(SearchInfo)
									DeleteSearchInfo(SearchInfo);

								if(SearchInfo = CreateSearchInfo(SearchBuffer,SearchForward,IgnoreCase,WholeWords))
									ReviewSearch(SearchInfo,SearchBuffer);
							}
							else
							{
								if(SearchInfo)
								{
									DeleteSearchInfo(SearchInfo);

									SearchInfo = NULL;
								}
							}
						}
					}
					else
					{
						MsgClass	= Message -> Class;
						MsgCode		= Message -> Code;
						MsgQualifier	= Message -> Qualifier;
						TagList		= (struct TagItem *)Message -> IAddress;

						ReplyMsg((struct Message *)Message);
					}

					switch(MsgClass)
					{
						case IDCMP_MENUHELP:

							GuideDisplay(CONTEXT_BUFFER_MENU);
							break;

						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:

										if(Context)
											LT_ShowWindow(Context -> SearchHandle,TRUE);
										else
											Context = CreateSearchContext(ReviewWindow,SearchBuffer,&HistoryHook,&SearchForward,&IgnoreCase,&WholeWords);

										break;

									case MEN_REPEAT:

										if(Context)
											LT_ShowWindow(Context -> SearchHandle,TRUE);
										else
										{
											if(SearchInfo)
												ReviewSearch(SearchInfo,SearchBuffer);
											else
												Context = CreateSearchContext(ReviewWindow,SearchBuffer,&HistoryHook,&SearchForward,&IgnoreCase,&WholeWords);
										}

										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)) || !Config -> MiscConfig -> ProtectiveMode)
												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);

		if(Context)
			DeleteSearchContext(Context);

		if(SearchInfo)
			DeleteSearchInfo(SearchInfo);

		LocalDeleteReview();
	}

	Forbid();

	ReviewProcess = NULL;

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

VOID __regargs
UpdateReview(BYTE Force)
{
	if(ReviewQueue)
	{
		if(SysBase -> ThisTask != (struct Task *)ReviewProcess)
		{
			BYTE SigBit;

			if((SigBit = AllocSignal(-1)) != -1)
			{
				struct DataMsg Msg;

				InitMsgItem(&Msg,ReviewClientDestructor);

				Msg . Type	= DATAMSGTYPE_UPDATEREVIEW;
				Msg . Size	= Force;
				Msg . Client	= SysBase -> ThisTask;
				Msg . Mask	= 1L << SigBit;

				Forbid();

				if(ReviewProcess)
				{
					ClrSignal(Msg . Mask);

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

					Wait(Msg . Mask);
				}

				Permit();

				FreeSignal(SigBit);
			}
		}
		else
			LocalUpdateReview(Force);
	}
}

VOID __regargs
MoveReview(BYTE Mode)
{
	if(ReviewQueue)
	{
		if(SysBase -> ThisTask != (struct Task *)ReviewProcess)
		{
			BYTE SigBit;

			if((SigBit = AllocSignal(-1)) != -1)
			{
				struct DataMsg Msg;

				InitMsgItem(&Msg,ReviewClientDestructor);

				Msg . Type	= DATAMSGTYPE_MOVEREVIEW;
				Msg . Size	= Mode;
				Msg . Client	= SysBase -> ThisTask;
				Msg . Mask	= 1L << SigBit;

				Forbid();

				if(ReviewProcess)
				{
					ClrSignal(Msg . Mask);

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

					Wait(Msg . Mask);
				}

				Permit();

				FreeSignal(SigBit);
			}
		}
		else
			LocalMoveReview(Mode);
	}
}

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

	Forbid();

	if(ReviewProcess)
	{
		ClrSignal(SIG_HANDSHAKE);

		Signal(ReviewProcess,SIG_KILL);

		Wait(SIG_HANDSHAKE);
	}

	Permit();
}

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

		Forbid();

		if(CreateNewProcTags(
			NP_Entry,	ReviewProcessEntry,
			NP_Name,	"term Review Process",
			NP_Priority,	SysBase -> ThisTask -> tc_Node . ln_Pri,
			NP_StackSize,	4000,
			NP_WindowPtr,	-1,
		TAG_END))
		{
			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);

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

				Result = TRUE;
			}
		}

		Permit();

		return(Result);
	}
}
