/*
**	termTextBuffer.c
**
**	Support routines for the text buffer.
**
**	Copyright © 1990-1995 by Olaf `Olsen' Barthel
**		All Rights Reserved
**
**	:ts=4
*/

#include "termGlobal.h"

	/* Gadget ID codes. */

enum	{	GAD_SCROLLER,GAD_UP,GAD_DOWN };

	/* Menu ID codes. */

enum	{	MEN_SEARCH,MEN_REPEAT,MEN_GOTO,MEN_CLEARBUF_CONTENTS,MEN_QUITBUF,MEN_PASTECLIP };

	/* A handy macro to determine the length of a string. */

#define LINE_WIDTH(s)	(s)[-1]

	/* Some private data (render info & window). */

typedef struct TextBufferInfo
{
	UWORD				 Left,Top,
						 Width,Height;

	struct Process		*Buddy;

	struct Window		*Window;
	struct Screen		*Screen;
	struct Menu			*BufferMenuStrip;

	BYTE				 BufferSignal;
	BOOLEAN				 BufferTerminated;

	struct RastPort		*RPort;

	LONG				 NumBufferLines,
						 NumBufferColumns,
						 LastTopLine;

	struct DrawInfo		*BufferDrawInfo;
	struct Image		*BufferAmigaGlyph,
						*BufferCheckGlyph;

	struct TextFont		*LocalFont;

	WORD				 LocalTextFontWidth,
						 LocalTextFontHeight,
						 LocalTextFontBase;

	struct TTextAttr	 LocalTextFont;
	UBYTE				 LocalTextFontName[MAX_FILENAME_LENGTH];

	struct TTextAttr	 LocalUserFont;
	UBYTE				 LocalUserFontName[MAX_FILENAME_LENGTH];

	LONG				 TopLine,
						 DisplayedLines;

	BOOLEAN				 SearchForward,
						 IgnoreCase,
						 WholeWords,
						 NeedClipConversion;

	struct Gadget		*Scroller,
						*UpArrow,
						*DownArrow;

	struct Image		*UpImage,
						*DownImage;

	UWORD				*BufferLineWidths,
						*BufferLineOffsets,
						*BufferColumnOffsets;

	ULONG				 ArrowWidth,
						 ArrowHeight;

	UWORD				 TitleFrontPen,
						 TitleBackPen,
						 TextFrontPen,
						 TextBackPen;

	WORD				 OldColumn,
						 OldLine,
						 OldLength;

	LONG				 TitleOffset;
	LONG				 MaxPen;
	WORD				 RightBorderWidth;

	struct Screen		*Parent;

	UBYTE				 TitleBuffer[100];

	struct MsgQueue		*Queue;
	ULONG				 QueueMask;

	BOOLEAN				*pSearchForward;
	BOOLEAN				*pIgnoreCase;
	BOOLEAN				*pWholeWords;
	LONG				*pTopLine;
} TextBufferInfo;

STATIC VOID __regargs
BufferClear(TextBufferInfo *BufferInfo,WORD Left,WORD Top,WORD Right,WORD Bottom)
{
	struct RastPort	*RPort = BufferInfo -> RPort;

	Left	+= BufferInfo -> Left;
	Top		+= BufferInfo -> Top;
	Right	+= BufferInfo -> Left;
	Bottom	+= BufferInfo -> Top;

	if(Right >= BufferInfo -> Left + BufferInfo -> Width)
		Right = BufferInfo -> Left + BufferInfo -> Width - 1;

	if(Bottom >= BufferInfo -> Top + BufferInfo -> Height)
		Bottom = BufferInfo -> Top + BufferInfo -> Height - 1;

	if(Left <= Right && Top <= Bottom)
	{
		SetAPen(RPort,BufferInfo -> TextBackPen);
		RectFill(RPort,Left,Top,Right,Bottom);
		SetAPen(RPort,BufferInfo -> TextFrontPen);
	}
}

STATIC VOID __regargs
BufferText(TextBufferInfo *BufferInfo,WORD Left,WORD Top,STRPTR Buffer,WORD Len)
{
	struct RastPort *RPort = BufferInfo -> RPort;

	Move(RPort,BufferInfo -> Left + Left,BufferInfo -> Top + Top);
	Text(RPort,Buffer,Len);
}

STATIC VOID __regargs
BufferScroll(TextBufferInfo *BufferInfo,LONG Lines)
{
	if(Lines < 0)
		Lines = -BufferInfo -> BufferLineOffsets[-Lines];
	else
		Lines = BufferInfo -> BufferLineOffsets[Lines];

	ScrollRaster(BufferInfo -> RPort,0,Lines,BufferInfo -> Left,BufferInfo -> Top,BufferInfo -> Left + BufferInfo -> Width - 1,BufferInfo -> Top + BufferInfo -> Height - 1);
}

STATIC VOID __regargs
BufferComplement(TextBufferInfo *BufferInfo,WORD SrcX,WORD SrcY,WORD Width,WORD Height)
{
	struct RastPort *RPort = BufferInfo -> RPort;

	if(Kick30)
	{
		SetMaxPen(RPort,(ULONG)~0);
		SetABPenDrMd(RPort,(ULONG)~0,0,JAM1 | COMPLEMENT);
	}
	else
	{
		SetAPen(RPort,(ULONG)~0);
		SetDrMd(RPort,JAM1 | COMPLEMENT);
	}

	SrcX	+= BufferInfo -> Left;
	SrcY	+= BufferInfo -> Top;

	RectFill(RPort,SrcX,SrcY,SrcX + Width - 1,SrcY + Height - 1);

	if(Kick30)
	{
		SetABPenDrMd(RPort,BufferInfo -> TextFrontPen,BufferInfo -> TextBackPen,JAM2);
		SetMaxPen(RPort,BufferInfo -> MaxPen);
	}
	else
	{
		SetAPen(RPort,BufferInfo -> TextFrontPen);
		SetDrMd(RPort,JAM2);
	}
}

	/* DeleteScroller(VOID):
	 *
	 *	Delete scroller and arrow objects.
	 */

STATIC VOID __regargs
DeleteScroller(TextBufferInfo *BufferInfo)
{
	if(BufferInfo -> Scroller)
		DisposeObject(BufferInfo -> Scroller);

	if(BufferInfo -> UpArrow)
		DisposeObject(BufferInfo -> UpArrow);

	if(BufferInfo -> DownArrow)
		DisposeObject(BufferInfo -> DownArrow);

	if(BufferInfo -> UpImage)
		DisposeObject(BufferInfo -> UpImage);

	if(BufferInfo -> DownImage)
		DisposeObject(BufferInfo -> DownImage);
}

	/* CreateScroller(LONG Height):
	 *
	 *	Create scroller and arrow objects.
	 */

STATIC BOOLEAN __regargs
CreateScroller(TextBufferInfo *BufferInfo,LONG Height)
{
	STATIC struct TagItem ArrowMappings[] = { GA_ID,GA_ID,TAG_END };

	BOOLEAN			 Result = FALSE;
	UWORD			 SizeType;
	struct Screen	*Screen;

	if(BufferInfo -> Parent)
		Screen = BufferInfo -> Parent;
	else
		Screen = BufferInfo -> Screen;

	if(Screen -> Flags & SCREENHIRES)
		SizeType = SYSISIZE_MEDRES;
	else
		SizeType = SYSISIZE_LOWRES;

	if(!BufferInfo -> Parent)
	{
		if(BufferInfo -> UpImage = (struct Image *)NewObject(NULL,SYSICLASS,
			SYSIA_Size,		SizeType,
			SYSIA_Which,	UPIMAGE,
			SYSIA_DrawInfo,	BufferInfo -> BufferDrawInfo,
		TAG_DONE))
		{
			if(BufferInfo -> DownImage = (struct Image *)NewObject(NULL,SYSICLASS,
				SYSIA_Size,		SizeType,
				SYSIA_Which,	DOWNIMAGE,
				SYSIA_DrawInfo,	BufferInfo -> BufferDrawInfo,
			TAG_DONE))
			{
				LONG ScrollerHeight,LeftEdge;

				GetAttr(IA_Height,	BufferInfo -> UpImage,&BufferInfo -> ArrowHeight);
				GetAttr(IA_Width,	BufferInfo -> UpImage,&BufferInfo -> ArrowWidth);

				ScrollerHeight = Height - 2 * BufferInfo -> ArrowHeight;

				LeftEdge = BufferInfo -> Screen -> Width - BufferInfo -> ArrowWidth;

				if(BufferInfo -> Scroller = NewObject(NULL,PROPGCLASS,
					GA_ID,			GAD_SCROLLER,

					GA_Top,			0,
					GA_Left,		LeftEdge,
					GA_Width,		BufferInfo -> ArrowWidth,
					GA_Height,		ScrollerHeight,
					GA_Immediate,	TRUE,
					GA_FollowMouse,	TRUE,
					GA_RelVerify,	TRUE,

					PGA_Freedom,	FREEVERT,
					PGA_NewLook,	TRUE,

					PGA_Visible,	1,
					PGA_Total,		1,
				TAG_DONE))
				{
					if(BufferInfo -> UpArrow = NewObject(NULL,BUTTONGCLASS,
						GA_ID,			GAD_UP,
						GA_Image,		BufferInfo -> UpImage,
						GA_Left,		LeftEdge,
						GA_Top,			ScrollerHeight,
						GA_Height,		BufferInfo -> ArrowHeight,
						GA_Width,		BufferInfo -> ArrowWidth,
						GA_Immediate,	TRUE,
						GA_RelVerify,	TRUE,
						GA_Previous,	BufferInfo -> Scroller,

						ICA_TARGET,		ICTARGET_IDCMP,
						ICA_MAP,		ArrowMappings,
					TAG_DONE))
					{
						if(BufferInfo -> DownArrow = NewObject(NULL,BUTTONGCLASS,
							GA_ID,			GAD_DOWN,
							GA_Image,		BufferInfo -> DownImage,
							GA_Left,		LeftEdge,
							GA_Top,			ScrollerHeight + BufferInfo -> ArrowHeight,
							GA_Height,		BufferInfo -> ArrowHeight,
							GA_Width,		BufferInfo -> ArrowWidth,
							GA_Immediate,	TRUE,
							GA_RelVerify,	TRUE,
							GA_Previous,	BufferInfo -> UpArrow,

							ICA_TARGET,		ICTARGET_IDCMP,
							ICA_MAP,		ArrowMappings,
						TAG_DONE))
							Result = TRUE;
					}
				}
			}
		}
	}
	else
	{
		struct Image *SizeImage;

		if(SizeImage = (struct Image *)NewObject(NULL,SYSICLASS,
			SYSIA_Size,		SizeType,
			SYSIA_Which,	SIZEIMAGE,
			SYSIA_DrawInfo,	BufferInfo -> BufferDrawInfo,
		TAG_DONE))
		{
			ULONG SizeWidth,SizeHeight;

			GetAttr(IA_Width,	SizeImage,&SizeWidth);
			GetAttr(IA_Height,	SizeImage,&SizeHeight);

			DisposeObject(SizeImage);

			BufferInfo -> RightBorderWidth = SizeWidth;

			if(BufferInfo -> UpImage = (struct Image *)NewObject(NULL,SYSICLASS,
				SYSIA_Size,		SizeType,
				SYSIA_Which,	UPIMAGE,
				SYSIA_DrawInfo,	BufferInfo -> BufferDrawInfo,
			TAG_DONE))
			{
				GetAttr(IA_Height,BufferInfo -> UpImage,&BufferInfo -> ArrowHeight);

				if(BufferInfo -> DownImage = (struct Image *)NewObject(NULL,SYSICLASS,
					SYSIA_Size,		SizeType,
					SYSIA_Which,	DOWNIMAGE,
					SYSIA_DrawInfo,	BufferInfo -> BufferDrawInfo,
				TAG_DONE))
				{
					if(BufferInfo -> 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 * BufferInfo -> 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))
					{
						if(BufferInfo -> UpArrow = NewObject(NULL,BUTTONGCLASS,
							GA_ID,			GAD_UP,

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

							ICA_TARGET,		ICTARGET_IDCMP,
							ICA_MAP,		ArrowMappings,
						TAG_DONE))
						{
							if(BufferInfo -> DownArrow = NewObject(NULL,BUTTONGCLASS,
								GA_ID,			GAD_DOWN,

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

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

	if(!Result)
		DeleteScroller(BufferInfo);

	return(Result);
}

	/* PrintLine(STRPTR Buffer,LONG LineNumber):
	 *
	 *	Print a line at a given line number in the displayed area.
	 */

STATIC VOID __regargs
PrintLine(TextBufferInfo *BufferInfo,STRPTR Buffer,LONG LineNumber)
{
	WORD Length = Buffer[-1];

		/* Print the text. */

	if(Length > 0)
	{
		if(Length > BufferInfo -> NumBufferColumns)
			Length = BufferInfo -> NumBufferColumns;

		if(Length > 0)
			BufferText(BufferInfo,0,BufferInfo -> BufferLineOffsets[LineNumber] + BufferInfo -> LocalTextFontBase,Buffer,Length);
	}

	if(Length < 0)
		Length = 0;

		/* The line doesn't exactly fill the displayed line,
		 * so erase the remaining columns.
		 */

	if(Length < BufferInfo -> BufferLineWidths[LineNumber])
		BufferClear(BufferInfo,BufferInfo -> BufferColumnOffsets[Length],BufferInfo -> BufferLineOffsets[LineNumber],BufferInfo -> BufferColumnOffsets[BufferInfo -> BufferLineWidths[LineNumber]] - 1,BufferInfo -> BufferLineOffsets[LineNumber + 1] - 1);

	BufferInfo -> BufferLineWidths[LineNumber] = Length;
}

	/* RedrawScreen(LONG FirstLine):
	 *
	 *	Redraw the contents of the entire screen and return the
	 *	number of lines actually drawn.
	 */

STATIC LONG __regargs
RedrawScreen(TextBufferInfo *BufferInfo,LONG FirstLine)
{
	LONG i,Last,Line = 0,Result;

	ObtainSemaphore(BufferSemaphore);

		/* Determine last line to display. */

	if((Last = FirstLine + BufferInfo -> NumBufferLines) >= Lines)
	{
		Last = Lines;

		if((FirstLine = Last - BufferInfo -> NumBufferLines) < 0)
			FirstLine = 0;

		BufferInfo -> TopLine = FirstLine;
	}

	if(Last > FirstLine + BufferInfo -> NumBufferLines)
		Last = FirstLine + BufferInfo -> NumBufferLines;

	Result = Last - FirstLine;

	if(Lines)
	{
		if(BufferInfo -> LastTopLine != -1)
		{
			LONG Delta = FirstLine - BufferInfo -> LastTopLine;

			if(ABS(Delta) < BufferInfo -> NumBufferLines)
			{
					/* No change? */

				if(!Delta)
				{
					ReleaseSemaphore(BufferSemaphore);

					return(Result);
				}
				else
				{
					BufferInfo -> LastTopLine = FirstLine;

						/* Scrolled up? */

					if(Delta < 0)
					{
						for(i = BufferInfo -> NumBufferLines - 1 ; i >= -Delta ; i--)
							BufferInfo -> BufferLineWidths[i] = BufferInfo -> BufferLineWidths[i + Delta];

						BufferScroll(BufferInfo,Delta);

						Last = FirstLine - Delta;
					}
					else
					{
						for(i = Delta ; i < BufferInfo -> NumBufferLines ; i++)
							BufferInfo -> BufferLineWidths[i - Delta] = BufferInfo -> BufferLineWidths[i];

							/* Scrolled down. */

						BufferScroll(BufferInfo,Delta);

						FirstLine += BufferInfo -> NumBufferLines - Delta;

						Line = BufferInfo -> NumBufferLines - Delta;
					}
				}
			}
			else
				BufferInfo -> LastTopLine = FirstLine;
		}
		else
			BufferInfo -> LastTopLine = FirstLine;

		if(BufferLines)
		{
			for(i = FirstLine ; i < Last ; i++)
				PrintLine(BufferInfo,BufferLines[i],Line++);
		}
	}

	ReleaseSemaphore(BufferSemaphore);

		/* We didn't fill the whole screen, so clear the rest. */

	if(Result < BufferInfo -> NumBufferLines)
	{
		WORD i;

		for(i = Result ; i < BufferInfo -> NumBufferLines ; i++)
			BufferInfo -> BufferLineWidths[i] = 0;

		BufferClear(BufferInfo,0,BufferInfo -> BufferLineOffsets[Result],BufferInfo -> BufferColumnOffsets[BufferInfo -> NumBufferColumns] - 1,BufferInfo -> BufferLineOffsets[BufferInfo -> NumBufferLines] - 1);
	}

	return(Result);
}

	/* MarkArea(LONG Column,LONG Line,LONG Length):
	 *
	 *	Mark an area in the term Buffer window.
	 */

STATIC VOID __regargs
MarkArea(TextBufferInfo *BufferInfo,LONG Column,LONG Line,LONG Length)
{
	if(BufferInfo -> OldColumn != -1)
		BufferComplement(BufferInfo,BufferInfo -> BufferColumnOffsets[BufferInfo -> OldColumn],BufferInfo -> BufferLineOffsets[BufferInfo -> OldLine],BufferInfo -> BufferColumnOffsets[BufferInfo -> OldLength],BufferInfo -> LocalTextFontHeight);

	if(Column != -1)
	{
		if(BufferInfo -> OldColumn != Column || BufferInfo -> OldLine != Line || BufferInfo -> OldLength != Length)
			BufferComplement(BufferInfo,BufferInfo -> BufferColumnOffsets[Column],BufferInfo -> BufferLineOffsets[Line],BufferInfo -> BufferColumnOffsets[Length],BufferInfo -> LocalTextFontHeight);
	}

	BufferInfo -> OldColumn	= Column;
	BufferInfo -> OldLine	= Line;
	BufferInfo -> OldLength	= Length;
}

STATIC VOID __stdargs
BufferDestructor(struct DataMsg *Item)
{
	Signal(Item -> Client,Item -> Mask);
}

STATIC VOID __regargs
BufferSerWrite(TextBufferInfo *BufferInfo,APTR Data,LONG Size)
{
	struct DataMsg Msg;

	InitMsgItem(&Msg,BufferDestructor);

	Msg . Type		= DATAMSGTYPE_WRITE;
	Msg . Data		= Data;
	Msg . Size		= Size;
	Msg . Client	= FindTask(NULL);
	Msg . Mask		= 1UL << BufferInfo -> BufferSignal;

	Forbid();

	ClrSignal(Msg . Mask);

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

	Wait(Msg . Mask);

	Permit();
}

	/* BufferClipPage(struct BlockMarker *Marker):
	 *
	 *	Send the marked area to the clipboard.
	 */

STATIC VOID __regargs
BufferClipPage(TextBufferInfo *BufferInfo,struct BlockMarker *Marker)
{
	if(BufferLines)
	{
		struct IFFHandle *Handle;

		if(Handle = AllocIFF())
		{
			if(Handle -> iff_Stream = (ULONG)OpenClipboard(Config -> ClipConfig -> ClipboardUnit))
			{
				InitIFFasClip(Handle);

				if(!OpenIFF(Handle,IFFF_WRITE))
				{
					if(!PushChunk(Handle,ID_FTXT,ID_FORM,IFFSIZE_UNKNOWN))
					{
						if(!PushChunk(Handle,0,ID_CHRS,IFFSIZE_UNKNOWN))
						{
							LONG Lines = Marker -> LastLine - Marker -> FirstLine - 1,i;

							if(LINE_WIDTH(BufferLines[Marker -> FirstLine]) > Marker -> FirstColumn)
								WriteTrimmedString(Handle,&BufferLines[Marker -> FirstLine][Marker -> FirstColumn],LINE_WIDTH(BufferLines[Marker -> FirstLine]) - Marker -> FirstColumn,BufferInfo -> NeedClipConversion);

							WriteChunkBytes(Handle,"\n",1);

							if(Lines > 0)
							{
								for(i = 0 ; i < Lines ; i++)
								{
									if(LINE_WIDTH(BufferLines[Marker -> FirstLine + 1 + i]))
										WriteTrimmedString(Handle,BufferLines[Marker -> FirstLine + 1 + i],LINE_WIDTH(BufferLines[Marker -> FirstLine + 1 + i]),BufferInfo -> NeedClipConversion);

									WriteChunkBytes(Handle,"\n",1);
								}
							}

							if(Marker -> LastColumn > LINE_WIDTH(BufferLines[Marker -> LastLine]))
								WriteTrimmedString(Handle,BufferLines[Marker -> LastLine],LINE_WIDTH(BufferLines[Marker -> LastLine]),BufferInfo -> NeedClipConversion);
							else
								WriteTrimmedString(Handle,BufferLines[Marker -> LastLine],Marker -> LastColumn,BufferInfo -> NeedClipConversion);

							WriteChunkBytes(Handle,"\n",1);

							PopChunk(Handle);
						}

						PopChunk(Handle);
					}

					CloseIFF(Handle);
				}

				CloseClipboard((struct ClipboardHandle *)Handle -> iff_Stream);
			}

			FreeIFF(Handle);
		}
	}
}

	/* BufferClip(VOID):
	 *
	 *	Start buffer marking process.
	 */

STATIC VOID __regargs
BufferClip(TextBufferInfo *BufferInfo)
{
	struct BlockMarker	*Marker;
	LONG				 FirstX,FirstY;

	FirstX = (BufferInfo -> Window -> MouseX - BufferInfo -> Left)	/ BufferInfo -> LocalTextFontWidth,
	FirstY = (BufferInfo -> Window -> MouseY - BufferInfo -> Top)	/ BufferInfo -> LocalTextFontHeight;

	if(Kick30)
		SetMaxPen(BufferInfo -> RPort,(ULONG)~0);

	if(Marker = BM_SetMark(BufferInfo -> Window -> RPort,ToggleSelect,ToggleSelect,BufferInfo -> NumBufferColumns,BufferInfo -> NumBufferLines,BufferInfo -> Left,BufferInfo -> Top,BufferInfo -> TopLine,Lines,FirstX,FirstY,BufferInfo -> LocalTextFontWidth,BufferInfo -> LocalTextFontHeight))
	{
		struct IntuiMessage	*Massage;
		ULONG				 Code,IClass;
		BOOLEAN				 Done = FALSE,Aborted = FALSE;
		LONG				 PlusX = BufferInfo -> LocalTextFontWidth - 1,
							 MouseX,MouseY,
							 Delta = 0;

		ReportMouse(TRUE,BufferInfo -> Window);

		while(!Done)
		{
			WaitPort(BufferInfo -> Window -> UserPort);

			while(Massage = (struct IntuiMessage *)GetMsg(BufferInfo -> Window -> UserPort))
			{
				IClass	= Massage -> Class;
				Code	= Massage -> Code;
				MouseX	= Massage -> MouseX - BufferInfo -> Left;
				MouseY	= Massage -> MouseY - BufferInfo -> Top;

				ReplyMsg(Massage);

				if(IClass == IDCMP_INACTIVEWINDOW)
				{
					Done = Aborted = TRUE;

					break;
				}

				if(IClass == IDCMP_INTUITICKS && Delta != 0)
				{
					if(BufferLines)
					{
						if((Delta > 0 && BufferInfo -> TopLine + BufferInfo -> NumBufferLines < Lines) || (Delta < 0 && BufferInfo -> TopLine > 0))
						{
							if(Delta < 0)
							{
								WORD i;

								for(i = BufferInfo -> NumBufferLines - 1 ; i > -Delta ; i--)
									BufferInfo -> BufferLineWidths[i] = BufferInfo -> BufferLineWidths[i + Delta];

								if(BufferInfo -> DisplayedLines)
									BufferScroll(BufferInfo,Delta);

								BufferInfo -> BufferLineWidths[0] = BufferInfo -> NumBufferColumns;

								PrintLine(BufferInfo,BufferLines[--BufferInfo -> TopLine],0);
							}
							else
							{
								WORD i;

								for(i = Delta ; i < BufferInfo -> NumBufferLines ; i++)
									BufferInfo -> BufferLineWidths[i - Delta] = BufferInfo -> BufferLineWidths[i];

								if(BufferInfo -> DisplayedLines)
									BufferScroll(BufferInfo,Delta);

								BufferInfo -> BufferLineWidths[BufferInfo -> NumBufferLines - 1] = BufferInfo -> NumBufferColumns;

								PrintLine(BufferInfo,BufferLines[BufferInfo -> TopLine + BufferInfo -> NumBufferLines],BufferInfo -> NumBufferLines - 1);

								BufferInfo -> TopLine++;
							}

							Marker -> Top 	+= Delta;
							Marker -> LastY	-= Delta;

							BM_ExtendMark(Marker,(MouseX + PlusX) / BufferInfo -> LocalTextFontWidth,MouseY / BufferInfo -> LocalTextFontHeight,Delta);
						}
						else
							Delta = 0;
					}
				}

				if(IClass == IDCMP_MOUSEBUTTONS && (Code & IECODE_UP_PREFIX))
				{
					BM_Draw(Marker,Marker -> Unselect);

					Done = TRUE;

					break;
				}

				if(IClass == IDCMP_MOUSEMOVE)
				{
					BM_ExtendMark(Marker,(MouseX + PlusX) / BufferInfo -> LocalTextFontWidth,MouseY / BufferInfo -> LocalTextFontHeight,0);

					if(MouseY < 1)
					{
						if(BufferInfo -> TopLine > 0)
							Delta = -1;
					}
					else
					{
						if(MouseY >= BufferInfo -> Height - 1 && BufferInfo -> TopLine + BufferInfo -> NumBufferLines < Lines)
							Delta = 1;
					}

					while(Delta)
					{
						MouseX	= BufferInfo -> Window -> MouseX - BufferInfo -> Left;
						MouseY	= BufferInfo -> Window -> MouseY - BufferInfo -> Top;

						if((Delta < 0 && MouseY > 0) || (Delta > 0 && MouseY < BufferInfo -> Height - 1))
							break;
						else
						{
							if(BufferLines)
							{
								if((Delta > 0 && BufferInfo -> TopLine + BufferInfo -> NumBufferLines < Lines) || (Delta < 0 && BufferInfo -> TopLine > 0))
								{
									if(Delta < 0)
									{
										WORD i;

										for(i = BufferInfo -> NumBufferLines - 1 ; i > -Delta ; i--)
											BufferInfo -> BufferLineWidths[i] = BufferInfo -> BufferLineWidths[i + Delta];

										if(BufferInfo -> DisplayedLines)
											BufferScroll(BufferInfo,Delta);

										BufferInfo -> BufferLineWidths[0] = BufferInfo -> NumBufferColumns;

										PrintLine(BufferInfo,BufferLines[--BufferInfo -> TopLine],0);
									}
									else
									{
										WORD i;

										for(i = Delta ; i < BufferInfo -> NumBufferLines ; i++)
											BufferInfo -> BufferLineWidths[i - Delta] = BufferInfo -> BufferLineWidths[i];

										if(BufferInfo -> DisplayedLines)
											BufferScroll(BufferInfo,Delta);

										BufferInfo -> BufferLineWidths[BufferInfo -> NumBufferLines - 1] = BufferInfo -> NumBufferColumns;

										PrintLine(BufferInfo,BufferLines[BufferInfo -> TopLine + BufferInfo -> NumBufferLines],BufferInfo -> NumBufferLines - 1);

										BufferInfo -> TopLine++;
									}

									Marker -> Top	+= Delta;
									Marker -> LastY	-= Delta;

									BM_ExtendMark(Marker,(MouseX + PlusX) / BufferInfo -> LocalTextFontWidth,MouseY / BufferInfo -> LocalTextFontHeight,Delta);

									SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
										PGA_Top,BufferInfo -> TopLine,
									TAG_DONE);
								}
								else
									break;
							}
							else
								break;
						}
					}

					Delta = 0;
				}
			}
		}

		ReportMouse(FALSE,BufferInfo -> Window);

		while(Massage = (struct IntuiMessage *)GetMsg(BufferInfo -> Window -> UserPort))
			ReplyMsg(Massage);

		if(!Aborted)
		{
			LONG ThisX,ThisY;

			ThisX = (BufferInfo -> Window -> MouseX - BufferInfo -> Left)	/ BufferInfo -> LocalTextFontWidth;
			ThisY = (BufferInfo -> Window -> MouseY - BufferInfo -> Top)	/ BufferInfo -> LocalTextFontHeight;

			if(FirstX != ThisX || FirstY != ThisY)
			{
				SetWait(BufferInfo -> Window);

				if(Marker -> FirstColumn == Marker -> Width)
				{
					Marker -> FirstLine++;

					Marker -> FirstColumn = 0;
				}

				if(Marker -> LastColumn == 0)
				{
					Marker -> LastLine--;

					Marker -> LastColumn = Marker -> Width;
				}

				if(Marker -> FirstLine <= Marker -> LastLine)
				{
					if(Marker -> FirstLine != Marker -> LastLine || Marker -> FirstColumn != Marker -> LastColumn)
					{
						if(BufferLines)
						{
							if(Marker -> FirstLine == Marker -> LastLine)
							{
								if(BufferInfo -> NeedClipConversion)
								{
									UBYTE	Buffer[256];
									LONG	Len,i;
									STRPTR	Dest,
											Source	= &BufferLines[Marker -> FirstLine][Marker -> FirstColumn];
									LONG	Size	= Marker -> LastColumn - Marker -> FirstColumn;
									UBYTE	c;

									BOOL	FirstWrite = TRUE;

									do
									{
										Len = MIN(Size,256);

										Size -= Len;

										for(i = 0, Dest = Buffer ; i < Len ; i++)
										{
											if(c = ISOConversion[*Source++])
												*Dest++ = c;
											else
												*Dest++ = ' ';
										}

										if(Dest > Buffer)
										{
											if(FirstWrite)
											{
												FirstWrite = FALSE;

												SaveClip(Buffer,(ULONG)Dest - (ULONG)&Buffer[0]);
											}
											else
												AddClip(Buffer,(ULONG)Dest - (ULONG)&Buffer[0]);
										}
									}
									while(Size > 0);
								}
								else
									SaveClip(&BufferLines[Marker -> FirstLine][Marker -> FirstColumn],Marker -> LastColumn - Marker -> FirstColumn);
							}
							else
								BufferClipPage(BufferInfo,Marker);
						}
					}
				}

				ClrWait(BufferInfo -> Window);
			}
		}

		FreeVecPooled(Marker);
	}

	if(Kick30)
		SetMaxPen(BufferInfo -> RPort,BufferInfo -> MaxPen);
}

STATIC VOID __regargs
StartSearch(TextBufferInfo *BufferInfo,struct SearchInfo *SearchInfo,STRPTR SearchBuffer)
{
	LT_LockWindow(BufferInfo -> Window);

	if(Lines)
	{
		LONG LineNumber;

		ObtainSemaphore(BufferSemaphore);

		LineNumber = SearchTextBuffer(SearchInfo);

		ReleaseSemaphore(BufferSemaphore);

		if(LineNumber == -1)
		{
			MyEasyRequest(BufferInfo -> Window,LocaleString(MSG_TERMBUFFER_DID_NOT_FIND_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),SearchBuffer);

			FlushMsg(BufferInfo -> Window);

			SearchInfo -> FoundY = -1;

			MarkArea(BufferInfo,-1,-1,-1);
		}
		else
		{
			if(LineNumber < BufferInfo -> TopLine)
			{
				MarkArea(BufferInfo,-1,-1,-1);

				BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,BufferInfo -> TopLine = LineNumber);

				SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
					PGA_Top,BufferInfo -> TopLine,
				TAG_DONE);
			}
			else
			{
				if(LineNumber > BufferInfo -> TopLine + BufferInfo -> DisplayedLines - 1)
				{
					MarkArea(BufferInfo,-1,-1,-1);

					if(LineNumber >= Lines - BufferInfo -> NumBufferLines)
					{
						LONG NewCurrentLine;

						if((NewCurrentLine = Lines - BufferInfo -> NumBufferLines) < 0)
							NewCurrentLine = 0;

						if(BufferInfo -> TopLine != NewCurrentLine)
							BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,BufferInfo -> TopLine = NewCurrentLine);
					}
					else
						BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,BufferInfo -> TopLine = LineNumber);

					SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
						PGA_Top,BufferInfo -> TopLine,
					TAG_DONE);
				}
			}

			MarkArea(BufferInfo,SearchInfo -> FoundX,LineNumber - BufferInfo -> TopLine,SearchInfo -> PatternWidth);
		}
	}
	else
		MyEasyRequest(BufferInfo -> Window,LocaleString(MSG_GLOBAL_NOTHING_IN_THE_BUFFER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

	LT_UnlockWindow(BufferInfo -> Window);
}

STATIC BOOLEAN __regargs
HandleBuffer(struct SignalSemaphore *Access,TextBufferInfo **Data)
{
	ULONG					 LastSeconds	= 0,
							 LastMicros		= 0,
							 Seconds,
							 Micros;
	BOOLEAN					 ClickAndActivate,
							 UpdatePercent	= TRUE;

	ULONG					 SignalSet;

	struct IntuiMessage		*Massage;
	ULONG					 MsgClass;
	UWORD					 MsgCode,MsgQualifier,LastQualifier = NULL;
	WORD					 MouseX,MouseY;

	UBYTE					 Char,LastChar = 0,FullChar;
	LONG					 LastWidth = 0;

	UBYTE					 PercentBuffer[80];
	UBYTE					 SearchBuffer[256];

	struct TagItem			*TagList;

	STRPTR					 PercentTemplate;

	BOOLEAN					 RingBack = FALSE;

	struct SearchContext	*Context	= NULL;
	struct SearchInfo		*SearchInfo	= NULL;

	struct Hook				 HistoryHook;
	TextBufferInfo			*BufferInfo;

	BufferInfo = *Data;

	HistoryHook . h_Data = &TextBufferHistory;

	if(LocaleBase)
		PercentTemplate = "%lD/%lD (%ld%%)";
	else
		PercentTemplate = "%ld/%ld (%ld%%)";

	FOREVER
	{
			/* Show where we are. */

		if(Lines && UpdatePercent)
		{
			SPrintf(PercentBuffer,PercentTemplate,BufferInfo -> TopLine,Lines > BufferInfo -> NumBufferLines ? Lines - BufferInfo -> NumBufferLines : 0,(100 * (BufferInfo -> TopLine + BufferInfo -> DisplayedLines)) / Lines);

			if(BufferInfo -> Parent)
			{
				SPrintf(BufferInfo -> TitleBuffer,"%s  %s",LocaleString(MSG_TERMBUFFER_TERM_BUFFER_TXT),PercentBuffer);

				SetWindowTitles(BufferInfo -> Window,BufferInfo -> TitleBuffer,(STRPTR)~0);
			}
			else
			{
				LONG Width,Len;

				if(Kick30)
					SetABPenDrMd(BufferInfo -> Screen -> BarLayer -> rp,BufferInfo -> TitleFrontPen,BufferInfo -> TitleBackPen,JAM2);
				else
				{
					SetAPen(BufferInfo -> Screen -> BarLayer -> rp,BufferInfo -> TitleFrontPen);
					SetBPen(BufferInfo -> Screen -> BarLayer -> rp,BufferInfo -> TitleBackPen);
					SetDrMd(BufferInfo -> Screen -> BarLayer -> rp,JAM2);
				}

				Len = strlen(PercentBuffer);

				Move(BufferInfo -> Screen -> BarLayer -> rp,BufferInfo -> TitleOffset,BufferInfo -> Screen -> BarLayer -> rp -> Font -> tf_Baseline + 1);
				Text(BufferInfo -> Screen -> BarLayer -> rp,PercentBuffer,Len);

				Width = TextLength(BufferInfo -> Screen -> BarLayer -> rp,PercentBuffer,Len);

				if(LastWidth > Width)
				{
					SetAPen(BufferInfo -> Screen -> BarLayer -> rp,BufferInfo -> TitleBackPen);
					RectFill(BufferInfo -> Screen -> BarLayer -> rp,BufferInfo -> TitleOffset + Width,1,BufferInfo -> TitleOffset + LastWidth - 1,BufferInfo -> Screen -> BarLayer -> rp -> Font -> tf_YSize);
				}

				LastWidth = Width;
			}

			UpdatePercent = FALSE;
		}

		if(BufferInfo -> BufferTerminated)
		{
			ObtainSemaphore(Access);

			SignalSet = SetSignal(0,SIG_KILL | SIG_TOFRONT | SIG_UPDATE | SIG_MOVEUP | PORTMASK(BufferInfo -> Window -> UserPort) | BufferInfo -> QueueMask) & (SIG_KILL | SIG_TOFRONT | SIG_UPDATE | SIG_MOVEUP | PORTMASK(BufferInfo -> Window -> UserPort) | BufferInfo -> QueueMask);

				// Disconnect

			if(!SignalSet)
			{
				BufferInfo -> Buddy = NULL;

				*Data = NULL;

				ReleaseSemaphore(Access);

				break;
			}
			else
				ReleaseSemaphore(Access);
		}
		else
			SignalSet = Wait(SIG_KILL | SIG_TOFRONT | SIG_UPDATE | SIG_MOVEUP | PORTMASK(BufferInfo -> Window -> UserPort) | BufferInfo -> QueueMask);

			/* Leave the town? */

		if(SignalSet & SIG_KILL)
			BufferInfo -> BufferTerminated = RingBack = TRUE;

			/* Bring our window to the front. */

		if(SignalSet & SIG_TOFRONT)
		{
			if(Context)
				LT_ShowWindow(Context -> SearchHandle,TRUE);

			BumpWindow(BufferInfo -> Window);
		}

			/* We've got one more line in the
			 * buffer.
			 */

		if(SignalSet & SIG_UPDATE)
		{
			MarkArea(BufferInfo,-1,-1,-1);

			if(BufferLines && Lines)
			{
				if(Lines - BufferInfo -> TopLine > BufferInfo -> DisplayedLines && BufferInfo -> DisplayedLines < BufferInfo -> NumBufferLines)
				{
					LONG i = BufferInfo -> TopLine + BufferInfo -> DisplayedLines;

					do
						PrintLine(BufferInfo,BufferLines[i++],BufferInfo -> DisplayedLines++);
					while(BufferInfo -> DisplayedLines < BufferInfo -> NumBufferLines && i < Lines);
				}
			}
			else
				BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,BufferInfo -> TopLine = 0);

			SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
				PGA_Total,		Lines,
				PGA_Visible,	BufferInfo -> DisplayedLines,
			TAG_DONE);

			UpdatePercent = TRUE;

			Signal(ThisProcess,SIG_HANDSHAKE);
		}

			/* The contents of the buffer have moved
			 * up a line.
			 */

		if(SignalSet & SIG_MOVEUP)
		{
			MarkArea(BufferInfo,-1,-1,-1);

			if(BufferInfo -> TopLine > 0)
			{
				BufferInfo -> LastTopLine = --BufferInfo -> TopLine;

				SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
					PGA_Top,BufferInfo -> TopLine,
				TAG_DONE);
			}
			else
			{
				LONG i;

				for(i = 0 ; i < BufferInfo -> NumBufferLines - 1 ; i++)
					BufferInfo -> BufferLineWidths[i] = BufferInfo -> BufferLineWidths[i + 1];

				BufferScroll(BufferInfo,-1);

				PrintLine(BufferInfo,BufferLines[BufferInfo -> NumBufferLines - 1],BufferInfo -> NumBufferLines - 1);
			}

			UpdatePercent = TRUE;

			Signal(ThisProcess,SIG_HANDSHAKE);
		}

			// A request from the queue?

		if(SignalSet & BufferInfo -> QueueMask)
		{
			struct DataMsg	*Msg;
			LONG			 NewCurrentLine;

			while(Msg = (struct DataMsg *)GetMsgItem(BufferInfo -> Queue))
			{
				switch(Msg -> Size)
				{
					case REVIEW_MOVE_TOP:

						MarkArea(BufferInfo,-1,-1,-1);

						if(BufferInfo -> TopLine)
						{
							BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,0);

							BufferInfo -> TopLine = 0;
							LastChar = 0;

							SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
								PGA_Top,BufferInfo -> TopLine,
							TAG_DONE);

							UpdatePercent = TRUE;
						}

						break;

					case REVIEW_MOVE_BOTTOM:

						if((NewCurrentLine = Lines - BufferInfo -> NumBufferLines) < 0)
							NewCurrentLine = 0;

						MarkArea(BufferInfo,-1,-1,-1);

						if(BufferInfo -> TopLine != NewCurrentLine)
						{
							BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,NewCurrentLine);

							BufferInfo -> TopLine = NewCurrentLine;

							LastChar = 0;

							SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
								PGA_Top,BufferInfo -> TopLine,
							TAG_DONE);

							UpdatePercent = TRUE;
						}

						break;

					case REVIEW_MOVE_UP:

						if((NewCurrentLine = BufferInfo -> TopLine - BufferInfo -> NumBufferLines) < 0)
							NewCurrentLine = 0;

						MarkArea(BufferInfo,-1,-1,-1);

						if(NewCurrentLine != BufferInfo -> TopLine)
						{
							BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,NewCurrentLine);

							BufferInfo -> TopLine = NewCurrentLine;

							LastChar = 0;

							SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
								PGA_Top,BufferInfo -> TopLine,
							TAG_DONE);

							UpdatePercent = TRUE;
						}

						break;

					case REVIEW_MOVE_DOWN:

						if((NewCurrentLine = BufferInfo -> TopLine + (2 * BufferInfo -> NumBufferLines)) > Lines)
							NewCurrentLine = Lines;

						if((NewCurrentLine = NewCurrentLine - BufferInfo -> NumBufferLines) < 0)
							NewCurrentLine = 0;

						MarkArea(BufferInfo,-1,-1,-1);

						if(NewCurrentLine != BufferInfo -> TopLine)
						{
							BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,NewCurrentLine);

							BufferInfo -> TopLine = NewCurrentLine;

							LastChar = 0;

							SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
								PGA_Top,BufferInfo -> TopLine,
							TAG_DONE);

							UpdatePercent = TRUE;
						}

						break;
				}

				DeleteMsgItem(Msg);
			}
		}

			/* Process the incoming window
			 * input.
			 */

		while(Massage = (struct IntuiMessage *)GetMsg(BufferInfo -> Window -> UserPort))
		{
			if(Context && Context -> SearchWindow == Massage -> IDCMPWindow)
			{
				MsgClass = NULL;

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

					DeleteSearchContext(Context);

					Context = NULL;

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

						if(SearchInfo = CreateSearchInfo(SearchBuffer,BufferInfo -> SearchForward,BufferInfo -> IgnoreCase,BufferInfo -> WholeWords))
							StartSearch(BufferInfo,SearchInfo,SearchBuffer);
					}
					else
					{
						if(SearchInfo)
							DeleteSearchInfo(SearchInfo);

						SearchInfo = NULL;
					}
				}
			}
			else
			{
				MsgClass		= Massage -> Class;
				MsgCode			= Massage -> Code;
				MsgQualifier	= Massage -> Qualifier;
				MouseX			= Massage -> MouseX - BufferInfo -> Left;
				MouseY			= Massage -> MouseY - BufferInfo -> Top;
				TagList			= (struct TagItem *)Massage -> IAddress;
				Seconds			= Massage -> Seconds;
				Micros			= Massage -> Micros;

				ClickAndActivate = FALSE;

				if(Seconds == LastSeconds && Micros == LastMicros)
				{
					if(Massage -> Class == IDCMP_ACTIVEWINDOW || Massage -> Class == IDCMP_MOUSEBUTTONS)
						ClickAndActivate = TRUE;
				}

				LastSeconds	= Seconds;
				LastMicros	= Micros;

					/* This hack is necessary to obtain the
					 * character codes generated for the cursor
					 * keys. A control or alternate qualifier
					 * would spoil the result (i.e. we would
					 * not get a valid key code).
					 */

				Massage -> Qualifier = NULL;

				Char = KeyConvert(Massage,NULL,NULL);

					// Put the qualifier back and do it all again

				Massage -> Qualifier = MsgQualifier;

				FullChar = KeyConvert(Massage,NULL,NULL);

				ReplyMsg((struct Message *)Massage);
			}

			if(MsgClass == IDCMP_ACTIVEWINDOW)
				UpdatePercent = TRUE;

			if(MsgClass == IDCMP_IDCMPUPDATE)
			{
				switch(GetTagData(GA_ID,0,TagList))
				{
					case GAD_UP:

						if(BufferInfo -> TopLine > 0)
						{
							BufferInfo -> TopLine--;

							MarkArea(BufferInfo,-1,-1,-1);

							SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
								PGA_Top,BufferInfo -> TopLine,
							TAG_DONE);

							BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,BufferInfo -> TopLine);

							UpdatePercent = TRUE;
						}

						break;

					case GAD_DOWN:

						if(BufferInfo -> TopLine + BufferInfo -> NumBufferLines < Lines)
						{
							BufferInfo -> TopLine++;

							MarkArea(BufferInfo,-1,-1,-1);

							SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
								PGA_Top,BufferInfo -> TopLine,
							TAG_DONE);

							BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,BufferInfo -> TopLine);

							UpdatePercent = TRUE;
						}

						break;
				}
			}

			if(MsgClass == IDCMP_MOUSEMOVE || MsgClass == IDCMP_GADGETDOWN || MsgClass == IDCMP_GADGETUP)
			{
				LONG Position;

				GetAttr(PGA_Top,BufferInfo -> Scroller,(ULONG *)&Position);

				if(Position != BufferInfo -> TopLine)
				{
					MarkArea(BufferInfo,-1,-1,-1);

					BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,BufferInfo -> TopLine = Position);

					UpdatePercent = TRUE;
				}
			}

			if(MsgClass == IDCMP_RAWKEY)
			{
				if(MsgCode == HELP_CODE)
					GuideDisplay(CONTEXT_TEXTBUFFER);

				if(LastChar)
				{
					if((MsgCode & IECODE_UP_PREFIX) || !(LastQualifier & IEQUALIFIER_REPEAT))
					{
						SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
							PGA_Top,BufferInfo -> TopLine,
						TAG_DONE);

						UpdatePercent = TRUE;
					}
				}

				if(LastChar = Char)
				{
						/* Use the numeric keypad keys to
						 * move through the buffer.
						 */

					if(MsgQualifier & IEQUALIFIER_NUMERICPAD)
					{
							/* Remove the numpad qualifier. */

						MsgQualifier &= ~IEQUALIFIER_NUMERICPAD;

						switch(Char - '0')
						{
								/* Jump to bottom. */

							case 1:

								Char = CDN;
								MsgQualifier |= IEQUALIFIER_CONTROL;
								break;

								/* Jump to top. */

							case 7:

								Char = CUP;
								MsgQualifier |= IEQUALIFIER_CONTROL;
								break;

								/* Move one page down. */

							case 3:

								Char = CDN;
								MsgQualifier |= IEQUALIFIER_LSHIFT;
								break;

								/* Move one page up. */

							case 9:

								Char = CUP;
								MsgQualifier |= IEQUALIFIER_LSHIFT;
								break;

								/* Move one line down. */

							case 2:

								Char = CDN;
								break;

								/* Move one line up. */

							case 8:

								Char = CUP;
								break;
						}
					}

						/* Check cursor keys. */

					switch(Char)
					{
							/* Scroll the buffer up. */

						case CUP:

							if(MsgQualifier & (IEQUALIFIER_CONTROL | IEQUALIFIER_LALT | IEQUALIFIER_RALT))
							{
								MarkArea(BufferInfo,-1,-1,-1);

								if(BufferInfo -> TopLine)
								{
									BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,0);

									BufferInfo -> TopLine = 0;
									LastChar = 0;

									SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
										PGA_Top,BufferInfo -> TopLine,
									TAG_DONE);

									UpdatePercent = TRUE;
								}

								break;
							}

							if(MsgQualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
							{
								LONG NewCurrentLine;

								if((NewCurrentLine = BufferInfo -> TopLine - BufferInfo -> NumBufferLines) < 0)
									NewCurrentLine = 0;

								MarkArea(BufferInfo,-1,-1,-1);

								if(NewCurrentLine != BufferInfo -> TopLine)
								{
									BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,NewCurrentLine);

									BufferInfo -> TopLine = NewCurrentLine;

									LastChar = 0;

									SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
										PGA_Top,BufferInfo -> TopLine,
									TAG_DONE);

									UpdatePercent = TRUE;
								}

								break;
							}

							if(BufferInfo -> TopLine)
							{
								MarkArea(BufferInfo,-1,-1,-1);

								BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,--BufferInfo -> TopLine);

								UpdatePercent = TRUE;
							}

							break;

							/* Scroll the buffer down. */

						case CDN:

							if(MsgQualifier & (IEQUALIFIER_CONTROL | IEQUALIFIER_LALT | IEQUALIFIER_RALT))
							{
								LONG NewCurrentLine;

								if((NewCurrentLine = Lines - BufferInfo -> NumBufferLines) < 0)
									NewCurrentLine = 0;

								MarkArea(BufferInfo,-1,-1,-1);

								if(BufferInfo -> TopLine != NewCurrentLine)
								{
									BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,NewCurrentLine);

									BufferInfo -> TopLine = NewCurrentLine;

									LastChar = 0;

									SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
										PGA_Top,BufferInfo -> TopLine,
									TAG_DONE);

									UpdatePercent = TRUE;
								}

								break;
							}

							if(MsgQualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
							{
								LONG NewCurrentLine;

								if((NewCurrentLine = BufferInfo -> TopLine + (2 * BufferInfo -> NumBufferLines)) > Lines)
									NewCurrentLine = Lines;

								if((NewCurrentLine = NewCurrentLine - BufferInfo -> NumBufferLines) < 0)
									NewCurrentLine = 0;

								MarkArea(BufferInfo,-1,-1,-1);

								if(NewCurrentLine != BufferInfo -> TopLine)
								{
									BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,NewCurrentLine);

									BufferInfo -> TopLine = NewCurrentLine;

									LastChar = 0;

									SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
										PGA_Top,BufferInfo -> TopLine,
									TAG_DONE);

									UpdatePercent = TRUE;
								}

								break;
							}

							if(BufferInfo -> TopLine + BufferInfo -> NumBufferLines < Lines)
							{
								MarkArea(BufferInfo,-1,-1,-1);

								BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,BufferInfo -> TopLine + 1);

								BufferInfo -> TopLine++;

								UpdatePercent = TRUE;
							}

							break;

						default:

							if(BufferInfo -> Parent)
							{
								Char = FullChar;

								if(Config -> SerialConfig -> StripBit8)
									Char &= 0x7F;

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

										Status = STATUS_READY;
									}
								}
								else
								{
										// Convert chars as appropriate

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

												goto SendIt;

											case EOL_LFCR:

												BufferSerWrite(BufferInfo,"\n\r",2);
												break;

											case EOL_CRLF:

												BufferSerWrite(BufferInfo,"\r\n",2);
												break;

											case EOL_CR:

												BufferSerWrite(BufferInfo,"\r",1);
												break;
										}
									}

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

												goto SendIt;

											case EOL_LFCR:

												BufferSerWrite(BufferInfo,"\n\r",2);
												break;

											case EOL_CRLF:

												BufferSerWrite(BufferInfo,"\r\n",2);
												break;

											case EOL_LF:

												BufferSerWrite(BufferInfo,"\n",1);
												break;
										}
									}

										/* Stop in/output. */

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

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

										/* Restart in/output. */

									if(Char == XOF)
									{
										if(Config -> SerialConfig -> PassThrough)
											BufferSerWrite(BufferInfo,&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])
											BufferSerWrite(BufferInfo,&IBMConversion[Char],1);
										else
											BufferSerWrite(BufferInfo,&Char,1);
									}
									else
										BufferSerWrite(BufferInfo,&Char,1);
							}

							break;
						}
					}

					LastQualifier = MsgQualifier;
				}
				else
					LastQualifier = NULL;

				continue;
			}

				/* User hit a mouse button. */

			if(MsgClass == IDCMP_MOUSEBUTTONS && (!ClickAndActivate || MsgCode != SELECTDOWN) && !(MsgCode & IECODE_UP_PREFIX))
			{
				MarkArea(BufferInfo,-1,-1,-1);

					/* Reasonable dimensions? */

				if(MouseY / BufferInfo -> LocalTextFontHeight < BufferInfo -> DisplayedLines && MouseX / BufferInfo -> LocalTextFontWidth < BufferInfo -> NumBufferColumns)
				{
					ObtainSemaphore(BufferSemaphore);

					BufferClip(BufferInfo);

					UpdatePercent = TRUE;

					BufferInfo -> LastTopLine = BufferInfo -> TopLine;

					ReleaseSemaphore(BufferSemaphore);

					if(MsgQualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
					{
						struct DataMsg Msg;

						InitMsgItem(&Msg,BufferDestructor);

						Msg . Type		= DATAMSGTYPE_WRITECLIP;
						Msg . Size		= Config -> ClipConfig -> ClipboardUnit;
						Msg . Client	= FindTask(NULL);
						Msg . Mask		= 1UL << BufferInfo -> BufferSignal;

						Forbid();

						ClrSignal(1L << BufferInfo -> BufferSignal);

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

						Wait(1L << BufferInfo -> BufferSignal);

						Permit();
					}

					break;
				}
			}

			if(MsgClass == IDCMP_MENUHELP)
				GuideDisplay(CONTEXT_BUFFER_MENU);

			if(MsgClass == IDCMP_CLOSEWINDOW)
				BufferInfo -> BufferTerminated = TRUE;

			if(MsgClass == IDCMP_NEWSIZE)
			{
				UWORD Width,Height;

				MarkArea(BufferInfo,-1,-1,-1);

				BufferInfo -> LastTopLine = -1;

				BufferInfo -> Width				= BufferInfo -> Window -> Width		- (BufferInfo -> Window -> BorderLeft	+ BufferInfo -> Window -> BorderRight);
				BufferInfo -> Height			= BufferInfo -> Window -> Height	- (BufferInfo -> Window -> BorderTop	+ BufferInfo -> Window -> BorderBottom);

				Width							= BufferInfo -> Width	- (BufferInfo -> Width	% BufferInfo -> LocalTextFontWidth);
				Height							= BufferInfo -> Height	- (BufferInfo -> Height	% BufferInfo -> LocalTextFontHeight);

				BufferInfo -> NumBufferColumns	= BufferInfo -> Width / BufferInfo -> LocalTextFontWidth;
				BufferInfo -> NumBufferLines	= BufferInfo -> Height / BufferInfo -> LocalTextFontHeight;

				if(Width < BufferInfo -> Width)
					BufferClear(BufferInfo,Width,0,BufferInfo -> Width - 1,BufferInfo -> Height - 1);

				if(Height < BufferInfo -> Height)
					BufferClear(BufferInfo,0,Height,BufferInfo -> Width - 1,BufferInfo -> Height - 1);

				BufferInfo -> Width				= Width;
				BufferInfo -> Height			= Height;

				BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,BufferInfo -> TopLine);

				SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
					PGA_Total,		Lines,
					PGA_Top,		BufferInfo -> TopLine,
					PGA_Visible,	BufferInfo -> DisplayedLines,
				TAG_DONE);

				UpdatePercent = TRUE;
			}

			if(MsgClass == IDCMP_MENUPICK)
			{
				struct MenuItem	*MenuItem;
				struct DataMsg	 Msg;

				while(MsgCode != MENUNULL)
				{
					if(MenuItem = ItemAddress(BufferInfo -> BufferMenuStrip,MsgCode))
					{
						switch((ULONG)GTMENUITEM_USERDATA(MenuItem))
						{
							case MEN_PASTECLIP:

								InitMsgItem(&Msg,BufferDestructor);

								Msg . Type		= DATAMSGTYPE_WRITECLIP;
								Msg . Size		= Config -> ClipConfig -> ClipboardUnit;
								Msg . Client	= FindTask(NULL);
								Msg . Mask		= 1UL << BufferInfo -> BufferSignal;

								Forbid();

								ClrSignal(Msg . Mask);

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

								Wait(Msg . Mask);

								Permit();
								break;

							case MEN_SEARCH:

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

								UpdatePercent = TRUE;

								break;

							case MEN_REPEAT:

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

								UpdatePercent = TRUE;

								break;

							case MEN_GOTO:

								if(Window)
									BumpWindow(Window);

								break;

							case MEN_QUITBUF:

								BufferInfo -> BufferTerminated = TRUE;
								break;

							case MEN_CLEARBUF_CONTENTS:

								if(Lines)
								{
									struct DataMsg *Msg;

									if(Msg = (struct DataMsg *)CreateMsgItem(sizeof(struct DataMsg)))
									{
										Msg -> Type = DATAMSGTYPE_CLEARBUFFER;

										UpdatePercent = TRUE;

										LT_LockWindow(BufferInfo -> Window);

										if(MsgQualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
										{
											PutMsgItem(SpecialQueue,(struct MsgItem *)Msg);
											Msg = NULL;
										}
										else
										{
											if(Config -> MiscConfig -> ProtectiveMode)
											{
												if(!MyEasyRequest(BufferInfo -> Window,LocaleString(MSG_TERMBUFFER_BUFFER_STILL_HOLDS_LINES_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT),Lines))
												{
													LT_UnlockWindow(BufferInfo -> Window);

													break;
												}
											}

											PutMsgItem(SpecialQueue,(struct MsgItem *)Msg);
											Msg = NULL;
										}

										DeleteMsgItem(Msg);
									}

									LT_UnlockWindow(BufferInfo -> Window);

									FlushMsg(BufferInfo -> Window);
								}

								break;
						}

						MsgCode = MenuItem -> NextSelect;
					}
					else
						break;
				}
			}

		}
	}

	if(Context)
		DeleteSearchContext(Context);

	if(SearchInfo)
		DeleteSearchInfo(SearchInfo);

		// Put the stuff back

	*BufferInfo -> pTopLine			= BufferInfo -> TopLine;
	*BufferInfo -> pSearchForward	= BufferInfo -> SearchForward;
	*BufferInfo -> pIgnoreCase		= BufferInfo -> IgnoreCase;
	*BufferInfo -> pWholeWords		= BufferInfo -> WholeWords;

	return(RingBack);
}

STATIC VOID __regargs
DeleteBufferInfo(TextBufferInfo *BufferInfo)
{
	if(BufferInfo)
	{
		FreeVecPooled(BufferInfo -> BufferLineWidths);

		if(BufferInfo -> Window)
		{
			ClearMenuStrip(BufferInfo -> Window);

			LT_DeleteWindowLock(BufferInfo -> Window);

			CloseWindow(BufferInfo -> Window);
		}

		if(BufferInfo -> Scroller)
			DeleteScroller(BufferInfo);

		if(BufferInfo -> BufferAmigaGlyph)
			DisposeObject(BufferInfo -> BufferAmigaGlyph);

		if(BufferInfo -> BufferCheckGlyph)
			DisposeObject(BufferInfo -> BufferCheckGlyph);

		if(BufferInfo -> BufferDrawInfo)
			FreeScreenDrawInfo(BufferInfo -> Screen,BufferInfo -> BufferDrawInfo);

		if(BufferInfo -> BufferMenuStrip)
			LT_DisposeMenu(BufferInfo -> BufferMenuStrip);

		if(BufferInfo -> Screen && !BufferInfo -> Parent)
			CloseScreen(BufferInfo -> Screen);

		if(BufferInfo -> LocalFont)
			CloseFont(BufferInfo -> LocalFont);

		if(BufferInfo -> BufferSignal != -1)
			FreeSignal(BufferInfo -> BufferSignal);

		if(BufferInfo -> Queue)
			DeleteMsgQueue(BufferInfo -> Queue);

		FreeVecPooled(BufferInfo);
	}
}

STATIC TextBufferInfo * __regargs
CreateBufferInfo(struct Screen *Parent,BOOLEAN *pSearchForward,BOOLEAN *pIgnoreCase,BOOLEAN *pWholeWords,LONG *pTopLine)
{
	TextBufferInfo *BufferInfo;

	if(BufferInfo = (TextBufferInfo *)AllocVecPooled(sizeof(TextBufferInfo),MEMF_ANY | MEMF_CLEAR))
	{
		BOOLEAN					 Defaults;

		struct ColorSpec		 ColorSpec[3];

		LONG					 Width,Height;
		ULONG					 DisplayMode;

		struct Rectangle		 DisplayClip;
		struct DimensionInfo	 DimensionInfo;

		BufferInfo -> pSearchForward	= pSearchForward;
		BufferInfo -> pIgnoreCase		= pIgnoreCase;
		BufferInfo -> pWholeWords		= pWholeWords;
		BufferInfo -> pTopLine			= pTopLine;

		BufferInfo -> TopLine		= *pTopLine;
		BufferInfo -> LastTopLine	= -1;

		BufferInfo -> SearchForward	= *pSearchForward;
		BufferInfo -> IgnoreCase	= *pIgnoreCase;
		BufferInfo -> WholeWords	= *pWholeWords;

		BufferInfo -> OldColumn		= -1;
		BufferInfo -> OldLine		= -1;
		BufferInfo -> OldLength		= -1;

		BufferInfo -> Buddy			= (struct Process *)FindTask(NULL);

		memcpy(&BufferInfo -> LocalTextFont,&TextFont,sizeof(struct TTextAttr));

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

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

		memcpy(&BufferInfo -> LocalUserFont,&UserFont,sizeof(struct TTextAttr));

		BufferInfo -> LocalUserFont . tta_Name = BufferInfo -> LocalUserFontName;

		strcpy(BufferInfo -> LocalUserFontName,UserFont . tta_Name);

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

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

			BufferInfo -> NeedClipConversion = TRUE;
		}
		else
			BufferInfo -> NeedClipConversion = FALSE;

		if(BufferInfo -> LocalFont = OpenDiskFont(&BufferInfo -> LocalTextFont))
		{
			BufferInfo -> LocalTextFontBase		= BufferInfo -> LocalFont -> tf_Baseline;
			BufferInfo -> LocalTextFontWidth	= BufferInfo -> LocalFont -> tf_XSize;
			BufferInfo -> LocalTextFontHeight	= BufferInfo -> LocalFont -> tf_YSize;

			Forbid();

			if(Window && DrawInfo)
			{
				UWORD Colour;

					/* Set up the startup colours for our buffer screen. */

				ColorSpec[0] . ColorIndex = 0;

				Colour = GetRGB4(Window -> WScreen -> ViewPort . ColorMap,DrawInfo -> dri_Pens[BACKGROUNDPEN]);

				ColorSpec[0] . Red		= (Colour >> 8) & 0xF;
				ColorSpec[0] . Green	= (Colour >> 4) & 0xF;
				ColorSpec[0] . Blue		= (Colour     ) & 0xF;

				ColorSpec[1] . ColorIndex = 1;

				Colour = GetRGB4(Window -> WScreen -> ViewPort . ColorMap,DrawInfo -> dri_Pens[TEXTPEN]);

				ColorSpec[1] . Red		= (Colour >> 8) & 0xF;
				ColorSpec[1] . Green	= (Colour >> 4) & 0xF;
				ColorSpec[1] . Blue		= (Colour     ) & 0xF;

				ColorSpec[2] . ColorIndex = -1;

				Defaults = FALSE;
			}
			else
				Defaults = TRUE;

			Permit();

			DisplayMode = Config -> CaptureConfig -> BufferScreenMode;

			if(ModeNotAvailable(DisplayMode))
				DisplayMode = Config -> ScreenConfig -> DisplayMode;

			if(ModeNotAvailable(DisplayMode))
			{
				struct Screen *PubScreen = LockPubScreen(NULL);

				if(PubScreen)
				{
					DisplayMode = GetVPModeID(&PubScreen -> ViewPort);

					UnlockPubScreen(NULL,PubScreen);
				}
				else
					DisplayMode = DEFAULT_MONITOR_ID | HIRES_KEY;
			}

				/* Set up the actual width of the screen we want. */

			Width = Config -> CaptureConfig -> BufferWidth * BufferInfo -> LocalTextFontWidth + BufferInfo -> ArrowWidth + 1;

			if((BufferInfo -> BufferSignal = AllocSignal(-1)) != -1)
			{
					/* Get the mode dimension info. */

				if(GetDisplayInfoData(NULL,(APTR)&DimensionInfo,sizeof(struct DimensionInfo),DTAG_DIMS,DisplayMode))
				{
						/* Determine maximum text overscan width. */

					LONG TextWidth = DimensionInfo . TxtOScan . MaxX - DimensionInfo . TxtOScan . MinX + 1;

						/* Too small? */

					if(Width < DimensionInfo . MinRasterWidth)
						Width = DimensionInfo . MinRasterWidth;

						/* Far too large? */

					if(Width > DimensionInfo . MaxRasterWidth)
						Width = DimensionInfo . MaxRasterWidth;

						/* A bit too large? */

					if(Width > TextWidth)
						Width = TextWidth;

						/* Inquire the text overscan dimensions. */

					if(QueryOverscan(DisplayMode,&DisplayClip,OSCAN_TEXT))
					{
							/* Centre the buffer screen. */

						if(DisplayClip . MaxX - DisplayClip . MinX + 1 > Width)
						{
							LONG Differ = (DisplayClip . MaxX - DisplayClip . MinX + 1 - Width) / 2;

							switch(Config -> CaptureConfig -> BufferScreenPosition)
							{
								case SCREEN_LEFT:

									DisplayClip . MaxX = DisplayClip . MinX + Width;
									break;

								case SCREEN_RIGHT:

									DisplayClip . MinX = DisplayClip . MaxX - Width;
									break;

								case SCREEN_CENTRE:

									DisplayClip . MinX += Differ;
									DisplayClip . MaxX -= Differ;

									break;
							}
						}

							/* Open a single bitplane clone of the main screen. */

						if(Parent)
						{
							BufferInfo -> Parent = Parent;
							BufferInfo -> Screen = Parent;

							if(BufferInfo -> Queue = CreateMsgQueue(NULL,0))
								BufferInfo -> QueueMask = BufferInfo -> Queue -> SigMask;
							else
								BufferInfo -> Screen = NULL;
						}
						else
						{
							BufferInfo -> Screen = (struct Screen *)OpenScreenTags(NULL,
								SA_Title,		LocaleString(MSG_TERMBUFFER_TERM_BUFFER_TXT),
								SA_Depth,		1,
								SA_Left,		DisplayClip . MinX,
								SA_DClip,		&DisplayClip,
								SA_DisplayID,	DisplayMode,
								SA_Font,		&BufferInfo -> LocalUserFont,
								SA_Behind,		TRUE,
								SA_AutoScroll,	TRUE,

								Defaults ? TAG_IGNORE : SA_Colors,ColorSpec,
							TAG_END);
						}

						if(BufferInfo -> Screen)
						{
							if(Parent)
							{
								BufferInfo -> BufferMenuStrip = LT_NewMenuTags(
									LH_LocaleHook,	&LocaleHook,
									LAMN_Screen,	BufferInfo -> Screen,
									LAMN_TextAttr,	&BufferInfo -> LocalUserFont,
									AmigaGlyph ? LAMN_AmigaGlyph		: TAG_IGNORE, AmigaGlyph,
									CheckGlyph ? LAMN_CheckmarkGlyph	: TAG_IGNORE, CheckGlyph,

									LAMN_TitleID,	MSG_TERMREVIEW_PROJECT_MEN,
										LAMN_ItemID,	MSG_TERMREVIEW_SEARCH_MEN,
											LAMN_UserData,	MEN_SEARCH,
										LAMN_ItemID,	MSG_TERMREVIEW_REPEAT_SEARCH_MEN,
											LAMN_UserData,	MEN_REPEAT,

										LAMN_ItemText,	NM_BARLABEL,

										LAMN_ItemID,	MSG_TERMREVIEW_CLEAR_BUFFER_MEN,
											LAMN_UserData,	MEN_CLEARBUF_CONTENTS,

										LAMN_ItemText,	NM_BARLABEL,

										LAMN_ItemID,	MSG_TERMREVIEW_CLOSE_BUFFER_MEN,
											LAMN_UserData,	MEN_QUITBUF,

										LAMN_ItemID,	MSG_CLOSE_WINDOW_TXT,
											LAMN_KeyText,	":",
											LAMN_UserData,	MEN_QUITBUF,

									LAMN_TitleID,	MSG_TERMBUFFER_EDIT_MEN,
										LAMN_ItemID,	MSG_TERMBUFFER_PASTE_MEN,
											LAMN_UserData,	MEN_PASTECLIP,
								TAG_DONE);
							}
							else
							{
								BufferInfo -> BufferMenuStrip = LT_NewMenuTags(
									LH_LocaleHook,	&LocaleHook,
									LAMN_Screen,	BufferInfo -> Screen,
									LAMN_TextAttr,	&BufferInfo -> LocalUserFont,
									AmigaGlyph ? LAMN_AmigaGlyph		: TAG_IGNORE, AmigaGlyph,
									CheckGlyph ? LAMN_CheckmarkGlyph	: TAG_IGNORE, CheckGlyph,

									LAMN_TitleID,	MSG_TERMBUFFER_PROJECT_MEN,
										LAMN_ItemID,	MSG_TERMBUFFER_SEARCH_MEN,
											LAMN_UserData,	MEN_SEARCH,
										LAMN_ItemID,	MSG_TERMBUFFER_REPEAT_SEARCH_MEN,
											LAMN_UserData,	MEN_REPEAT,

										LAMN_ItemText,	NM_BARLABEL,

										LAMN_ItemID,	MSG_TERMBUFFER_GO_TO_MAIN_SCREEN_MEN,
											LAMN_UserData,	MEN_GOTO,

										LAMN_ItemText,	NM_BARLABEL,

										LAMN_ItemID,	MSG_TERMBUFFER_CLEAR_BUFFER_MEN,
											LAMN_UserData,	MEN_CLEARBUF_CONTENTS,

										LAMN_ItemText,	NM_BARLABEL,

										LAMN_ItemID,	MSG_TERMBUFFER_CLOSE_BUFFER_MEN,
											LAMN_UserData,	MEN_QUITBUF,
								TAG_DONE);
							}

							if(BufferInfo -> BufferMenuStrip)
							{
								if(BufferInfo -> BufferDrawInfo = GetScreenDrawInfo(BufferInfo -> Screen))
								{
									if(BufferInfo -> BufferDrawInfo -> dri_NumPens > BARDETAILPEN)
									{
										BufferInfo -> TitleFrontPen	= BufferInfo -> BufferDrawInfo -> dri_Pens[BARDETAILPEN];
										BufferInfo -> TitleBackPen	= BufferInfo -> BufferDrawInfo -> dri_Pens[BARBLOCKPEN];
									}
									else
									{
										BufferInfo -> TitleFrontPen	= BufferInfo -> BufferDrawInfo -> dri_Pens[DETAILPEN];
										BufferInfo -> TitleBackPen	= BufferInfo -> BufferDrawInfo -> dri_Pens[BLOCKPEN];
									}

									BufferInfo -> TextFrontPen	= BufferInfo -> BufferDrawInfo -> dri_Pens[TEXTPEN];
									BufferInfo -> TextBackPen	= BufferInfo -> BufferDrawInfo -> dri_Pens[BACKGROUNDPEN];

									if(!CreateMenuGlyphs(BufferInfo -> Screen,BufferInfo -> BufferDrawInfo,&BufferInfo -> BufferAmigaGlyph,&BufferInfo -> BufferCheckGlyph))
									{
										BufferInfo -> BufferAmigaGlyph	= NULL;
										BufferInfo -> BufferCheckGlyph	= NULL;
									}

									BufferInfo -> MaxPen = MAX(BufferInfo -> TextFrontPen,BufferInfo -> TextBackPen);

									Height = (BufferInfo -> Screen -> Height - (BufferInfo -> Screen -> BarHeight + 2)) / BufferInfo -> LocalTextFontHeight;

									if(CreateScroller(BufferInfo,Height * BufferInfo -> LocalTextFontHeight))
									{
										if(BufferInfo -> Window = OpenWindowTags(NULL,
											WA_Top,				Parent ? Window -> TopEdge + Window -> BorderTop : BufferInfo -> Screen -> BarHeight + 2,
											WA_Left,			Parent ? Window -> LeftEdge : 0,
											WA_Width,			Parent ? Window -> Width : BufferInfo -> Screen -> Width,
											WA_Height,			Parent ? Window -> Height - Window -> BorderTop : Height * BufferInfo -> LocalTextFontHeight,
											WA_Backdrop,		Parent == NULL,
											WA_Borderless,		Parent == NULL,
											WA_DepthGadget,		Parent != NULL,
											WA_DragBar,			Parent != NULL,
											WA_CloseGadget,		Parent != NULL,
											WA_SizeGadget,		Parent != NULL,
											WA_NoCareRefresh,	TRUE,
											WA_CustomScreen,	BufferInfo -> Screen,
											WA_NewLookMenus,	TRUE,
											WA_RptQueue,		1,
											WA_IDCMP,			IDCMP_IDCMPUPDATE | IDCMP_RAWKEY | IDCMP_INACTIVEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_MOUSEBUTTONS | IDCMP_MENUPICK | IDCMP_GADGETUP | IDCMP_GADGETDOWN | IDCMP_MOUSEMOVE | IDCMP_MENUHELP | IDCMP_SIZEVERIFY | IDCMP_NEWSIZE | IDCMP_CLOSEWINDOW,
											WA_MenuHelp,		TRUE,
											WA_Gadgets,			BufferInfo -> Scroller,
											BackfillTag,		Parent ? &BackfillHook : NULL,

											BufferInfo -> BufferAmigaGlyph ? WA_AmigaKey  : TAG_IGNORE, BufferInfo -> BufferAmigaGlyph,
											BufferInfo -> BufferCheckGlyph ? WA_Checkmark : TAG_IGNORE, BufferInfo -> BufferCheckGlyph,
										TAG_DONE))
										{
											SetMenuStrip(BufferInfo -> Window,BufferInfo -> BufferMenuStrip);

											BufferInfo -> Left		= BufferInfo -> Window -> BorderLeft;
											BufferInfo -> Top		= BufferInfo -> Window -> BorderTop;
											BufferInfo -> Width		= BufferInfo -> Window -> Width		- (BufferInfo -> Window -> BorderLeft	+ BufferInfo -> Window -> BorderRight);
											BufferInfo -> Height	= BufferInfo -> Window -> Height	- (BufferInfo -> Window -> BorderTop	+ BufferInfo -> Window -> BorderBottom);

											if(Parent)
											{
												BufferInfo -> Width		-= BufferInfo -> Width	% BufferInfo -> LocalTextFontWidth;
												BufferInfo -> Height	-= BufferInfo -> Height	% BufferInfo -> LocalTextFontHeight;

												WindowLimits(BufferInfo -> Window,BufferInfo -> Window -> BorderLeft + 20 * BufferInfo -> LocalTextFontWidth + BufferInfo -> Window -> BorderRight,BufferInfo -> Window -> BorderTop + BufferInfo -> LocalTextFontHeight + BufferInfo -> Window -> BorderBottom,BufferInfo -> Screen -> Width,BufferInfo -> Screen -> Height);
											}
											else
												BufferInfo -> Width -= BufferInfo -> ArrowWidth;

											if(BufferInfo -> BufferLineWidths = (UWORD *)AllocVecPooled(2 * sizeof(UWORD) * (BufferInfo -> Window -> WScreen -> Height / BufferInfo -> LocalTextFontHeight + 1) + sizeof(UWORD) * (BufferInfo -> Window -> WScreen -> Width / BufferInfo -> LocalTextFontWidth + 1),MEMF_ANY | MEMF_CLEAR))
											{
												UWORD	Index;
												WORD	i;

												BufferInfo -> BufferLineOffsets		= &BufferInfo -> BufferLineWidths[BufferInfo -> Window -> WScreen -> Height / BufferInfo -> LocalTextFontHeight + 1];
												BufferInfo -> BufferColumnOffsets	= &BufferInfo -> BufferLineOffsets[BufferInfo -> Window -> WScreen -> Height / BufferInfo -> LocalTextFontHeight + 1];

												for(i = Index = 0 ; i < BufferInfo -> Window -> WScreen -> Height / BufferInfo -> LocalTextFontHeight + 1 ; i++)
												{
													BufferInfo -> BufferLineOffsets[i] = Index;

													Index += BufferInfo -> LocalTextFontHeight;
												}

												for(i = Index = 0 ; i < BufferInfo -> Window -> WScreen -> Width / BufferInfo -> LocalTextFontWidth + 1 ; i++)
												{
													BufferInfo -> BufferColumnOffsets[i] = Index;

													Index += BufferInfo -> LocalTextFontWidth;
												}

													/* Determine maximum dimensions of
													 * the buffer screen (in rows and
													 * columns).
													 */

												if(Parent)
													BufferInfo -> NumBufferColumns = BufferInfo -> Width / BufferInfo -> LocalTextFontWidth;
												else
													BufferInfo -> NumBufferColumns = (BufferInfo -> Window -> Width - (BufferInfo -> ArrowWidth + 1)) / BufferInfo -> LocalTextFontWidth;

												BufferInfo -> NumBufferLines = BufferInfo -> Height / BufferInfo -> LocalTextFontHeight;

												if(BufferInfo -> TopLine == -1 || !Config -> CaptureConfig -> RememberBufferScreen)
												{
													switch(Config -> CaptureConfig -> OpenBufferScreen)
													{
														case BUFFER_TOP:

															BufferInfo -> TopLine = 0;
															break;

														case BUFFER_END:

															if((BufferInfo -> TopLine = Lines - BufferInfo -> NumBufferLines) < 0)
																BufferInfo -> TopLine = 0;

															break;

														default:

															BufferInfo -> TopLine = 0;
															break;
													}
												}

												if(BufferInfo -> TopLine > Lines - BufferInfo -> NumBufferLines || BufferInfo -> TopLine < 0)
													BufferInfo -> TopLine = 0;

												BufferInfo -> RPort = BufferInfo -> Window -> RPort;

												SetFont(BufferInfo -> RPort,BufferInfo -> LocalFont);

													/* Bring the screen to the front. */

												BumpWindow(BufferInfo -> Window);

													/* Set the drawing pens for the window. */

												if(Kick30)
												{
													SetABPenDrMd(BufferInfo -> RPort,BufferInfo -> TextFrontPen,BufferInfo -> TextBackPen,JAM2);
													SetMaxPen(BufferInfo -> RPort,BufferInfo -> MaxPen);
												}
												else
												{
													SetAPen(BufferInfo -> RPort,BufferInfo -> TextFrontPen);
													SetBPen(BufferInfo -> RPort,BufferInfo -> TextBackPen);
													SetDrMd(BufferInfo -> RPort,JAM2);
												}

													/* Initial creation of the buffer display. */

												BufferInfo -> DisplayedLines = RedrawScreen(BufferInfo,BufferInfo -> TopLine);

												SetGadgetAttrs(BufferInfo -> Scroller,BufferInfo -> Window,NULL,
													PGA_Top,		BufferInfo -> TopLine,
													PGA_Total,		Lines,
													PGA_Visible,	BufferInfo -> NumBufferLines,
												TAG_DONE);

												BufferInfo -> TitleOffset = TextLength(BufferInfo -> Screen -> BarLayer -> rp,LocaleString(MSG_TERMBUFFER_TERM_BUFFER_TXT),strlen(LocaleString(MSG_TERMBUFFER_TERM_BUFFER_TXT))) + TextLength(BufferInfo -> Screen -> BarLayer -> rp,"  ",1) + 4;

												return(BufferInfo);
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	DeleteBufferInfo(BufferInfo);

	return(NULL);
}

	/* BufferServer():
	 *
	 *	Asynchronous task to display the data stored in the
	 *	scrollback display buffer.
	 */

STATIC VOID __saveds
BufferServer(VOID)
{
	STATIC BOOLEAN	SearchForward	= TRUE,
					IgnoreCase		= TRUE,
					WholeWords		= FALSE;
	STATIC LONG		TopLine			= -1;

	TextBufferInfo	*BufferInfo;
	struct Task		*Father;
	BOOLEAN			 RingBack = TRUE;

		// Wake this guy up

	Father = (struct Task *)SysBase -> ThisTask -> tc_UserData;

		// Snap our fingers...

	if(BufferInfo = CreateBufferInfo(NULL,&SearchForward,&IgnoreCase,&WholeWords,&TopLine))
	{
			// Open wide, here I come!

		ObtainSemaphore(&BufferTaskSemaphore);

		BufferInfoData = BufferInfo;

		ReleaseSemaphore(&BufferTaskSemaphore);

			// Up and running

		Signal(Father,SIG_HANDSHAKE);

		Father = NULL;

			// Swish the tinsel

		RingBack = HandleBuffer(&BufferTaskSemaphore,&BufferInfoData);

			// Activate the main window again

		if(Window)
			BumpWindow(Window);

			// The wrecking crew

		DeleteBufferInfo(BufferInfo);
	}

		// Shutdown in an orderly fashion

	Forbid();

	if(RingBack)
	{
		if(Father)
			Signal(Father,SIG_HANDSHAKE);
		else
			Signal(ThisProcess,SIG_HANDSHAKE);
	}
}

	/* LaunchBuffer():
	 *
	 *	Launch the buffer process.
	 */

BYTE
LaunchBuffer()
{
	ObtainSemaphore(&BufferTaskSemaphore);

		/* Is the buffer process already running? */

	if(BufferInfoData)
	{
			/* Tell it to bring its screen to the front. */

		Signal(BufferInfoData -> Buddy,SIG_TOFRONT);

		ReleaseSemaphore(&BufferTaskSemaphore);

			/* Return success. */

		return(TRUE);
	}
	else
	{
		struct Task	*Child;
		BYTE		 Result = FALSE;

		ReleaseSemaphore(&BufferTaskSemaphore);

		Forbid();

			/* Launch the buffer process. */

		if(Child = (struct Task *)CreateNewProcTags(
			NP_Entry,		BufferServer,
			NP_Name,		"term Buffer Process",
			NP_Priority,	SysBase -> ThisTask -> tc_Node . ln_Pri,
			NP_StackSize,	4000,
			NP_WindowPtr,	-1,
		TAG_END))
		{
			Child -> tc_UserData = SysBase -> ThisTask;

			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);

			ObtainSemaphore(&BufferTaskSemaphore);

			if(BufferInfoData)
				Result = TRUE;

			ReleaseSemaphore(&BufferTaskSemaphore);
		}

		Permit();

			/* Return the result. */

		return(Result);
	}
}

	/* TerminateBuffer():
	 *
	 *	Terminate the buffer process.
	 */

VOID
TerminateBuffer()
{
	ObtainSemaphore(&BufferTaskSemaphore);

	if(BufferInfoData)
	{
		Forbid();

		Signal(BufferInfoData -> Buddy,SIG_KILL);

		ReleaseSemaphore(&BufferTaskSemaphore);

		ClrSignal(SIG_HANDSHAKE);

		Wait(SIG_HANDSHAKE);

		Permit();
	}
	else
		ReleaseSemaphore(&BufferTaskSemaphore);
}

	/* MoveBuffer(TextBufferInfo *BufferInfo,BYTE Mode):
	 *
	 *	Move the currently displayed buffer area somewhere.
	 */

VOID __regargs
MoveBuffer(struct SignalSemaphore *Access,const struct TextBufferInfo **Data,BYTE Mode)
{
	struct TextBufferInfo *BufferInfo;

	ObtainSemaphore(Access);

	BufferInfo = (TextBufferInfo *)*Data;

	if(BufferInfo && BufferInfo -> Queue)
	{
		BYTE SigBit;

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

			InitMsgItem(&Msg,BufferDestructor);

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

			ClrSignal(Msg . Mask);

			PutMsgItem(BufferInfo -> Queue,(struct MsgItem *)&Msg);

			ReleaseSemaphore(Access);

			Wait(Msg . Mask);

			FreeSignal(SigBit);

			return;
		}
	}

	ReleaseSemaphore(Access);
}

	/* NotifyBuffer(struct SignalSemaphore *Access,const struct TextBufferInfo **Data,ULONG Signals):
	 *
	 *	Send a signal to the buffer process.
	 */

VOID __regargs
NotifyBuffer(struct SignalSemaphore *Access,const struct TextBufferInfo **Data,ULONG Signals)
{
	struct TextBufferInfo *BufferInfo;

	ObtainSemaphore(Access);

	BufferInfo = (TextBufferInfo *)*Data;

	if(BufferInfo && BufferInfo -> Buddy)
	{
		Forbid();

		ReleaseSemaphore(Access);

		ClrSignal(SIG_HANDSHAKE);

		Signal(BufferInfo -> Buddy,Signals);

		Wait(SIG_HANDSHAKE);

		Permit();
	}
	else
		ReleaseSemaphore(Access);
}


/*****************************************************************************/


STATIC VOID __saveds
ReviewServer(VOID)
{
	STATIC BOOLEAN	SearchForward	= TRUE,
					IgnoreCase		= TRUE,
					WholeWords		= FALSE;
	STATIC LONG		TopLine			= -1;

	TextBufferInfo	*BufferInfo;
	struct Task		*Father;
	BOOLEAN			 RingBack = TRUE;

		// Wake this guy up

	Father = (struct Task *)SysBase -> ThisTask -> tc_UserData;

		// Snap our fingers...

	if(BufferInfo = CreateBufferInfo(Window -> WScreen,&SearchForward,&IgnoreCase,&WholeWords,&TopLine))
	{
			// Open wide, here I come!

		ObtainSemaphore(&ReviewTaskSemaphore);

		ReviewInfoData = BufferInfo;

		ReleaseSemaphore(&ReviewTaskSemaphore);

			// Up and running

		Signal(Father,SIG_HANDSHAKE);

		Father = NULL;

			// Swish the tinsel

		RingBack = HandleBuffer(&ReviewTaskSemaphore,&ReviewInfoData);

			// Activate the main window again

		if(Window)
			BumpWindow(Window);

			// The wrecking crew

		DeleteBufferInfo(BufferInfo);

		Forbid();

		CheckItem(MEN_REVIEW_WINDOW,FALSE);

		Permit();
	}

		// Shutdown in an orderly fashion

	Forbid();

	if(RingBack)
	{
		if(Father)
			Signal(Father,SIG_HANDSHAKE);
		else
			Signal(ThisProcess,SIG_HANDSHAKE);
	}
}

VOID __regargs
MoveReview(BYTE Mode)
{
	MoveBuffer(&ReviewTaskSemaphore,&ReviewInfoData,Mode);
}

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

	ObtainSemaphore(&ReviewTaskSemaphore);

	if(ReviewInfoData)
	{
		Forbid();

		Signal(ReviewInfoData -> Buddy,SIG_KILL);

		ReleaseSemaphore(&ReviewTaskSemaphore);

		ClrSignal(SIG_HANDSHAKE);

		Wait(SIG_HANDSHAKE);

		Permit();
	}
	else
		ReleaseSemaphore(&ReviewTaskSemaphore);
}

BYTE
CreateReview()
{
	BYTE Result = FALSE;

	ObtainSemaphore(&ReviewTaskSemaphore);

		/* Is the Review process already running? */

	if(ReviewInfoData)
	{
			/* Tell it to bring its screen to the front. */

		Signal(ReviewInfoData -> Buddy,SIG_TOFRONT);

		ReleaseSemaphore(&ReviewTaskSemaphore);

			/* Return success. */

		Result = TRUE;
	}
	else
	{
		struct Task	*Child;

		ReleaseSemaphore(&ReviewTaskSemaphore);

		Forbid();

			/* Launch the Review process. */

		if(Child = (struct Task *)CreateNewProcTags(
			NP_Entry,		ReviewServer,
			NP_Name,		"term Review Process",
			NP_Priority,	SysBase -> ThisTask -> tc_Node . ln_Pri,
			NP_StackSize,	4000,
			NP_WindowPtr,	-1,
		TAG_END))
		{
			Child -> tc_UserData = SysBase -> ThisTask;

			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);

			ObtainSemaphore(&ReviewTaskSemaphore);

			if(ReviewInfoData)
				Result = TRUE;

			ReleaseSemaphore(&ReviewTaskSemaphore);
		}

		Permit();
	}

	if(Result)
		CheckItem(MEN_REVIEW_WINDOW,TRUE);

	return(Result);
}
