/*
**	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_CLOSEBUF,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, NULL,		 0 , 0, 0, (APTR)MEN_CLOSEBUF},
	{  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 LONG			 ReviewWidth = -1,ReviewHeight,
				 ReviewX,ReviewY;
STATIC UBYTE			 ReviewChar;
STATIC LONG			 ReviewColumns,ReviewLines,
				 ReviewTop,ReviewGlobalLines;

STATIC struct SearchInfo	*ReviewSearchInfo;

STATIC UBYTE			*ReviewLineWidths;
STATIC LONG			 ReviewMaxLines;

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

	/* 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		= Buffer;
	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)
	{
		for(i = Line - 1 ; i < ReviewLines ; i++)
			ReviewLineWidths[i] = 0;

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

	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",DrawInfo -> dri_Pens[TEXTPEN]);

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

		DoIO(ReviewWriteRequest);

		ReviewWrites("\33[0;3%ldm",DrawInfo -> dri_Pens[TEXTPEN]);

		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(GetString(FALSE,FALSE,255,LocaleString(MSG_TERMBUFFER_ENTER_SEARCH_STRING_TXT),SearchBuffer))
			{
				if(SearchBuffer[0])
				{
					if(ReviewSearchInfo)
						DeleteSearchInfo(ReviewSearchInfo);

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

		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)
	{
		ReviewWidth	= ReviewWindow -> Width;
		ReviewHeight	= ReviewWindow -> Height;
		ReviewX		= ReviewWindow -> LeftEdge;
		ReviewY		= ReviewWindow -> TopEdge;

		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(ReviewWidth == -1)
	{
		ReviewWidth		= Window -> Width;
		ReviewHeight		= Window -> WScreen -> WBorTop + Window -> WScreen -> WBorBottom + 1 + Window -> WScreen -> Font -> ta_YSize + 5 * CurrentFont -> tf_YSize;
		ReviewX			= Window -> LeftEdge;
		ReviewY			= Window -> TopEdge;

		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 MaxWidth = Window -> WScreen -> WBorLeft + RightBorderWidth + (BUFFER_LINE_WIDTH + 1) * CurrentFont -> tf_XSize;

				if(MaxWidth > Window -> WScreen -> Width)
					MaxWidth = Window -> WScreen -> Width;

				if(MaxWidth < ReviewWidth)
					ReviewWidth = MaxWidth;

				if(ReviewWindow = OpenWindowTags(NULL,
					WA_Left,		ReviewX,
					WA_Top,			ReviewY,
					WA_Width,		ReviewWidth,
					WA_Height,		ReviewHeight,
					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,		MaxWidth,
					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_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[11;12{\33[3%ldm",RenderPens[1] & 7);

											ReviewQuery();

											ObtainSemaphore(BufferSemaphore);

											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 || (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",DrawInfo -> dri_Pens[TEXTPEN]);
		}
		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()
{
	BYTE Result = FALSE;

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

		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:
							case MEN_CLOSEBUF:

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

	if(ReviewPort)
	{
		UBYTE Char;

		if(Char = GetReviewChar(FALSE))
		{
			if(Char == CSI)
			{
				WORD Count = 0;

				switch(Char = GetReviewChar(TRUE))
				{
					case 'A':

						ReviewUp(1);
						break;

					case 'B':

						ReviewDown(1);
						break;

					case 'T':

						ReviewUp(ReviewLines);
						break;

					case 'S':

						ReviewDown(ReviewLines);
						break;

					default:

						SharedBuffer[Count++] = Char;

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

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

						SharedBuffer[Count] = 0;

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

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

								ClipXerox = FALSE;
							}

							if(ClipInput)
							{
								UBYTE	InputBuffer[257];
								WORD	Len;

									/* 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
						{
							if(!strncmp(SharedBuffer,"11",2))
							{
								DeleteReview();

								return(Result);
							}

							if(!strncmp(SharedBuffer,"12",2))
							{
								if(ReviewQuery())
									RefreshReview(ReviewTop);
							}
						}

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

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

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

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

								goto SendIt;

							case LF_ASLFCR:

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

						return(TRUE);
					}

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

								goto SendIt;

							case CR_ASCRLF:

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

						return(TRUE);
					}

						/* Stop in/output. */

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

							return(TRUE);
						}
						else
						{
							if(Config -> SerialConfig -> xONxOFF)
								Status = STATUS_HOLDING;
						}
					}

						/* Restart in/output. */

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

							return(TRUE);
						}
					}

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

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

			return(TRUE);
		}
	}

	return(Result);
}
