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

#ifndef _GLOBAL_H
#include "Global.h"
#endif

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

	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
BufferClear(TextBufferInfo *BufferInfo,LONG Left,LONG Top,LONG Right,LONG 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
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
BufferComplement(TextBufferInfo *BufferInfo,LONG SrcX,LONG SrcY,LONG Width,LONG Height)
{
	struct RastPort *RPort = BufferInfo->RPort;

	if(Kick30)
		SetMaxPen(RPort,(ULONG)~0);

	SetPens(RPort,(ULONG)~0,0,JAM1 | COMPLEMENT);

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

	FillBox(RPort,SrcX,SrcY,Width,Height);

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

	SetPens(RPort,BufferInfo->TextFrontPen,BufferInfo->TextBackPen,JAM2);
}

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

STATIC VOID
DeleteScroller(TextBufferInfo *BufferInfo)
{
	DisposeObject(BufferInfo->Scroller);
	DisposeObject(BufferInfo->UpArrow);
	DisposeObject(BufferInfo->DownArrow);
	DisposeObject(BufferInfo->UpImage);
	DisposeObject(BufferInfo->DownImage);
}

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

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

	BOOL			 Result = FALSE;
	LONG			 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
PrintLine(TextBufferInfo *BufferInfo,STRPTR Buffer,LONG LineNumber)
{
	LONG Length = Buffer[-1];

		/* Print the text. */

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

		if(Length > 0)
			PlaceText(BufferInfo->RPort,BufferInfo->Left,BufferInfo->Top + BufferInfo->BufferLineOffsets[LineNumber],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
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)
	{
		LONG 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
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
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
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
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;
		BOOL				 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)
							{
								LONG 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
							{
								LONG 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)
									{
										LONG 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
									{
										LONG 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;
									LONG	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
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)
		{
			ShowRequest(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
		ShowRequest(BufferInfo->Window,LocaleString(MSG_GLOBAL_NOTHING_IN_THE_BUFFER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));

	LT_UnlockWindow(BufferInfo->Window);
}

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

	ULONG					 SignalSet;

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

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

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

	struct TagItem			*TagList;

	STRPTR					 PercentTemplate;

	BOOL					 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(UpdatePercent)
		{
			if(Lines)
				SPrintf(PercentBuffer,PercentTemplate,BufferInfo->TopLine,Lines > BufferInfo->NumBufferLines ? Lines - BufferInfo->NumBufferLines : 0,(100 * (BufferInfo->TopLine + BufferInfo->DisplayedLines)) / Lines);
			else
				PercentBuffer[0] = 0;

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

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

				RPort = BufferInfo->Screen->BarLayer->rp;

				SetPens(RPort,BufferInfo->TitleFrontPen,BufferInfo->TitleBackPen,JAM2);

				Len = strlen(PercentBuffer);

				PlaceText(RPort,BufferInfo->TitleOffset,1,PercentBuffer,Len);

				Width = TextLength(RPort,PercentBuffer,Len);

				if(LastWidth > Width)
				{
					SetAPen(RPort,BufferInfo->TitleBackPen);
					RectFill(RPort,BufferInfo->TitleOffset + Width,1,BufferInfo->TitleOffset + LastWidth - 1,RPort->TxHeight - 1);
				}

				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))
				{
					BOOL 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;

								/* Move one line down. */

							case 2:

								Char = CDN;
								break;

								/* Move one page down. */

							case 3:

								Char = CDN;
								MsgQualifier |= IEQUALIFIER_LSHIFT;
								break;

								/* Jump to top. */

							case 7:

								Char = CUP;
								MsgQualifier |= IEQUALIFIER_CONTROL;
								break;

								/* Move one line up. */

							case 8:

								Char = CUP;
								break;

								/* Move one page up. */

							case 9:

								Char = CUP;
								MsgQualifier |= IEQUALIFIER_LSHIFT;
								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)
			{
				LONG 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(!ShowRequest(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
DeleteBufferInfo(TextBufferInfo *BufferInfo)
{
	if(BufferInfo)
	{
		FreeVecPooled(BufferInfo->BufferLineWidths);

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

			LT_DeleteWindowLock(BufferInfo->Window);

			CloseWindow(BufferInfo->Window);
		}

		DeleteScroller(BufferInfo);

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

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

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

		DeleteMsgQueue(BufferInfo->Queue);

		FreeVecPooled(BufferInfo);
	}
}

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

	if(BufferInfo = (TextBufferInfo *)AllocVecPooled(sizeof(TextBufferInfo),MEMF_ANY | MEMF_CLEAR))
	{
		BOOL						 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);

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

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

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

		CopyMem(&UserFont,&BufferInfo->LocalUserFont,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->LocalTextFontWidth	= BufferInfo->LocalFont->tf_XSize;
			BufferInfo->LocalTextFontHeight	= BufferInfo->LocalFont->tf_YSize;

			Forbid();

			if(Window && DrawInfo)
			{
				LONG Colour;

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

				ColorSpec[0].ColorIndex = 0;

				Colour = GetRGB4(Window->WScreen->ViewPort.ColorMap,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,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 = 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(BufferInfo->BufferDrawInfo = GetScreenDrawInfo(BufferInfo->Screen))
							{
								ULONG *MenuTags;

								CreateMenuGlyphs(BufferInfo->Screen,BufferInfo->BufferDrawInfo,&BufferInfo->BufferAmigaGlyph,&BufferInfo->BufferCheckGlyph);

								if(Parent)
								{
									STATIC ULONG ReviewTags[] =
									{
										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,		(ULONG)NM_BARLABEL,

											LAMN_ItemID,		MSG_TERMREVIEW_CLEAR_BUFFER_MEN,
												LAMN_UserData,	MEN_CLEARBUF_CONTENTS,

											LAMN_ItemText,		(ULONG)NM_BARLABEL,

											LAMN_ItemID,		MSG_TERMREVIEW_CLOSE_BUFFER_MEN,
												LAMN_UserData,	MEN_QUITBUF,

											LAMN_ItemID,		MSG_CLOSE_WINDOW_TXT,
												LAMN_KeyText,	(ULONG)":",
												LAMN_UserData,	MEN_QUITBUF,

										LAMN_TitleID,			MSG_TERMBUFFER_EDIT_MEN,
											LAMN_ItemID,		MSG_TERMBUFFER_PASTE_MEN,
												LAMN_UserData,	MEN_PASTECLIP,

										TAG_DONE
									};

									MenuTags = ReviewTags;
								}
								else
								{
									STATIC ULONG BufferTags[] =
									{
										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,		(ULONG)NM_BARLABEL,

											LAMN_ItemID,		MSG_TERMBUFFER_GO_TO_MAIN_SCREEN_MEN,
												LAMN_UserData,	MEN_GOTO,

											LAMN_ItemText,		(ULONG)NM_BARLABEL,

											LAMN_ItemID,		MSG_TERMBUFFER_CLEAR_BUFFER_MEN,
												LAMN_UserData,	MEN_CLEARBUF_CONTENTS,

											LAMN_ItemText,		(ULONG)NM_BARLABEL,

											LAMN_ItemID,		MSG_TERMBUFFER_CLOSE_BUFFER_MEN,
												LAMN_UserData,	MEN_QUITBUF,

										TAG_DONE
									};

									MenuTags = BufferTags;
								}

								if(BufferInfo->BufferMenuStrip = LT_NewMenuTags(
									LH_LocaleHook,			&LocaleHook,
									LAMN_Screen,			BufferInfo->Screen,
									LAMN_TextAttr,			&BufferInfo->LocalUserFont,
									LAMN_AmigaGlyph,		BufferInfo->BufferAmigaGlyph,
									LAMN_CheckmarkGlyph,	BufferInfo->BufferCheckGlyph,
								TAG_MORE,MenuTags))
								{
									UWORD *Pens = BufferInfo->BufferDrawInfo->dri_Pens;

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

									BufferInfo->TextFrontPen	= Pens[TEXTPEN];
									BufferInfo->TextBackPen		= Pens[BACKGROUNDPEN];

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

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

									if(CreateScroller(BufferInfo,Height * BufferInfo->LocalTextFontHeight))
									{
										LONG Left,Top;

										if(Parent)
										{
											Left	= Window->LeftEdge;
											Top		= Window->TopEdge + Window->BorderTop;
											Width	= Window->Width;
											Height	= Window->Height - Window->BorderTop;

											GetWindowInfo(WINDOW_REVIEW,&Left,&Top,&Width,&Height,Width,Height);
										}
										else
										{
											Left	= 0;
											Top		= BufferInfo->Screen->BarHeight + 2;
											Width	= BufferInfo->Screen->Width;
											Height	= Height * BufferInfo->LocalTextFontHeight;
										}

										if(BufferInfo->Window = OpenWindowTags(NULL,
											WA_Left,			Left,
											WA_Top,				Top,
											WA_Width,			Width,
											WA_Height,			Height,
											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_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,
											WA_CustomScreen,	BufferInfo->Screen,

//											BufferInfo->Screen ? WA_CustomScreen : OpenWindowTag,	BufferInfo->Screen,
											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))
											{
												LONG Index;
												LONG 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)
													SetMaxPen(BufferInfo->RPort,BufferInfo->MaxPen);

												SetPens(BufferInfo->RPort,BufferInfo->TextFrontPen,BufferInfo->TextBackPen,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);

												if(!Parent)
												{
													struct RastPort *RPort = BufferInfo->Screen->BarLayer->rp;

													BufferInfo->TitleOffset = TextLength(RPort,LocaleString(MSG_TERMBUFFER_TERM_BUFFER_TXT),strlen(LocaleString(MSG_TERMBUFFER_TERM_BUFFER_TXT))) + TextLength(RPort,"  ",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,*Me;
	BOOL			 RingBack = TRUE;

		// Wake this guy up

	Me = FindTask(NULL);

	Father = (struct Task *)Me->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.
	 */

BOOL
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;
		BOOL		 Result = FALSE;

		ReleaseSemaphore(&BufferTaskSemaphore);

		Forbid();

			/* Launch the buffer process. */

		if(Child = (struct Task *)CreateNewProcTags(
			NP_Entry,		BufferServer,
			NP_Name,		"term Buffer Process",
			NP_WindowPtr,	-1,
		TAG_END))
		{
			Child->tc_UserData = FindTask(NULL);

			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
MoveBuffer(struct SignalSemaphore *Access,const struct TextBufferInfo **Data,LONG Mode)
{
	struct TextBufferInfo *BufferInfo;

	ObtainSemaphore(Access);

	BufferInfo = (TextBufferInfo *)*Data;

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

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

			InitMsgItem(&Msg,BufferDestructor);

			Msg.Type	= DATAMSGTYPE_MOVEREVIEW;
			Msg.Size	= Mode;
			Msg.Client	= FindTask(NULL);
			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
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,*Me;
	BOOL			 RingBack = TRUE;

		// Wake this guy up

	Me = FindTask(NULL);

	Father = (struct Task *)Me->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);

		if(Config->CaptureConfig->RememberBufferWindow)
			PutWindowInfo(WINDOW_REVIEW,BufferInfo->Window->LeftEdge,BufferInfo->Window->TopEdge,BufferInfo->Window->Width,BufferInfo->Window->Height);

			// 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
MoveReview(LONG 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);
}

BOOL
CreateReview()
{
	BOOL 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_WindowPtr,	-1,
		TAG_END))
		{
			Child->tc_UserData = FindTask(NULL);

			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);

			ObtainSemaphore(&ReviewTaskSemaphore);

			if(ReviewInfoData)
				Result = TRUE;

			ReleaseSemaphore(&ReviewTaskSemaphore);
		}

		Permit();
	}

	if(Result)
		CheckItem(MEN_REVIEW_WINDOW,TRUE);

	return(Result);
}
