/*
**	termMarker.c
**
**	Text block marker routines
**
**	Copyright © 1990-1994 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"
#include "termEmulationProcess.h"

	/* BM_Draw():
	 *
	 *	Redraw or remove marked regions.
	 */

VOID __regargs
BM_Draw(struct BlockMarker *Marker,VOID (*Select)(struct BlockMarker *Marker,LONG Left,LONG Top,LONG Width,LONG Height))
{
		/* Is the first line the same as the last line? If so,
		 * continue and mark only single characters. Else,
		 * determine first line and column to mark and last
		 * line and last column.
		 */

	if(Marker -> FirstLine != Marker -> LastLine)
	{
		LONG	First	= Marker -> FirstLine - Marker -> Top,
			Last	= Marker -> LastLine  - Marker -> Top,
			Lines;

			/* Is the first line visible? If so, mark it. */

		if(First >= 0)
			(*Select)(Marker,Marker -> FirstColumn,First,Marker -> Width - Marker -> FirstColumn,1);
		else
		{
			(*Select)(Marker,0,0,Marker -> Width,1);

			First = 0;
		}

			/* Is the last line visible? If so, mark it. */

		if(Last >= 0 && Last < Marker -> Height)
			(*Select)(Marker,0,Last,Marker -> LastColumn,1);
		else
			Last = Marker -> Height;

			/* Determine the number of lines the selection spans. */

		if((Lines = Last - First - 1) > 0)
			(*Select)(Marker,0,First + 1,Marker -> Width,Lines);
	}
	else
	{
			/* Is the first column different from the last column? */

		if(Marker -> FirstColumn != Marker -> LastColumn)
		{
				/* Is the line visible? If so, mark it. */

			if(Marker -> Top <= Marker -> LastLine && Marker -> LastLine < Marker -> Top + Marker -> Height)
				(*Select)(Marker,Marker -> FirstColumn,Marker -> FirstLine - Marker -> Top,Marker -> LastColumn - Marker -> FirstColumn,1);
		}
	}
}

	/* BM_ClearMark(struct BlockMarker *Marker):
	 *
	 *	Free block marker memory.
	 */

VOID __regargs
BM_ClearMark(struct BlockMarker *Marker)
{
	BM_Draw(Marker,Marker -> Unselect);

	FreeVecPooled(Marker);
}

	/* BM_SetMark():
	 *
	 *	Create block marker structure.
	 */

struct BlockMarker * __regargs
BM_SetMark(APTR Object,VPTR Select,VPTR Unselect,LONG Width,LONG Height,LONG LeftEdge,LONG TopEdge,LONG Top,LONG Lines,LONG X,LONG Y,WORD TextFontWidth,WORD TextFontHeight)
{
	if(Height && Lines)
	{
		struct BlockMarker *Marker;

			/* Allocate marker buffer. */

		if(Marker = (struct BlockMarker *)AllocVecPooled(sizeof(struct BlockMarker),MEMF_ANY | MEMF_CLEAR))
		{
				/* Fill in the object or canvas, usually a RastPort. */

			Marker -> Object	= Object;

				/* Fill in the canvas left and top edge. */

			Marker -> LeftEdge	= LeftEdge;
			Marker -> TopEdge	= TopEdge;

				/* Fill in the select and unselect routines. */

			Marker -> Select	= Select;
			Marker -> Unselect	= Unselect;

				/* Fill in current display window top and number of
				 * lines in the buffer.
				 */

			Marker -> Top		= Top;
			Marker -> Lines		= Lines;

				/* Fill in width and height of the display window. */

			Marker -> Width		= Width;
			Marker -> Height	= Height;

				/* Fill in the marker anchor point. */

			Marker -> FirstColumn	= X;
			Marker -> LastColumn	= X;
			Marker -> FirstLine	= Y + Top;
			Marker -> LastLine	= Y + Top;

				/* Fill in current mouse position. */

			Marker -> LastX		= X;
			Marker -> LastY		= Y;

			Marker -> OriginX	= X;
			Marker -> OriginY	= Y + Top;

				/* Remember text font dimensions. */

			Marker -> TextFontWidth	= TextFontWidth;
			Marker -> TextFontHeight= TextFontHeight;

			Marker -> WriteMask	= ((struct RastPort *)Object) -> Mask;
		}

			/* Return marker buffer. */

		return(Marker);
	}
	else
		return(NULL);
}

	/* BM_ExtendMark(struct BlockMarker *Marker,LONG X,LONG Y,LONG Delta):
	 *
	 *	Extend current block marker. This routine was
	 *	first written by me, but Martin Berndt rewrote it
	 *	after we both realised that it would not work
	 *	properly.
	 */

VOID __regargs
BM_ExtendMark(struct BlockMarker *Marker,LONG X,LONG Y,LONG Delta)
{
	if(Marker -> LastX != X || Marker -> LastY != Y)
	{
		LONG	OldCharPos,NewCharPos,
			CharStart,CharEnd,
			CharOrigin,
			OriginY,
			Lines;
		BYTE	Crossed;

			OriginY = Marker -> OriginY - Marker -> Top;


			/* Deal with illegal X position. */

		if(X < 0)
			X = 0;

		if(X > Marker -> Width)
			X = Marker -> Width;

			/* Deal with illegal Y position. */

		if(Y < 0)
			Y = 0;

		if(Y >= Marker -> Height)
			Y = Marker -> Height - 1;

		if(Y + Marker -> Top >= Marker -> Lines)
			Y = Marker -> Lines - Marker -> Top - 1;

			/* Is the Y position larger than the last line? If so,
			 * truncate it.
			 */

		if(Y > Marker -> Lines - Marker -> Top)
			Y -= Marker -> Lines - Marker -> Top;

			/* Select the text. */

		OldCharPos	= (Marker -> LastY + Marker -> Top) * Marker -> Width + Marker -> LastX;
		NewCharPos	= (Y + Marker -> Top) * Marker -> Width + X;

		CharStart	= Marker -> FirstLine * Marker -> Width + Marker -> FirstColumn;
		CharEnd		= Marker -> LastLine * Marker -> Width + Marker -> LastColumn;

		CharOrigin	= Marker -> OriginY * Marker -> Width + Marker -> OriginX;

		Crossed		= (OldCharPos < CharOrigin) ^ (NewCharPos < CharOrigin);

		if(NewCharPos > OldCharPos)
		{
			if(Delta && Y < OriginY)
				(*Marker -> Select)(Marker,0,Y,Marker -> Width,1);

			if(Crossed)
			{
				if((Lines = OriginY - Marker -> LastY - 1) >= 0)
				{
					(*Marker -> Unselect)(Marker,Marker -> LastX,Marker -> LastY,Marker -> Width - Marker -> LastX,1);

					if(Lines > 0)
						(*Marker -> Unselect)(Marker,0,Marker -> LastY + 1,Marker -> Width,Lines);

					if(Marker -> OriginX)
						(*Marker -> Unselect)(Marker,0,OriginY,Marker -> OriginX,1);
				}
				else
				{
					if(Delta)
						(*Marker -> Unselect)(Marker,0,OriginY,Marker -> LastX,1);
					else
						(*Marker -> Unselect)(Marker,Marker -> LastX,OriginY,Marker -> OriginX - Marker -> LastX,1);
				}

				Marker -> FirstColumn	= Marker -> OriginX;
				Marker -> FirstLine 	= Marker -> OriginY;

				Marker -> LastX		= Marker -> OriginX;
				Marker -> LastY		= OriginY;
			}
			else
			{
				if(OldCharPos < CharOrigin)
				{
					if((Lines = Y - Marker -> LastY - 1) >= 0)
					{
						(*Marker -> Unselect)(Marker,Marker -> LastX,Marker -> LastY,Marker -> Width - Marker -> LastX,1);

						if(Lines > 0)
							(*Marker -> Unselect)(Marker,0,Marker -> LastY + 1,Marker -> Width,Lines);

						(*Marker -> Unselect)(Marker,0,Y,X,1);
					}
					else
					{
						if(Delta)
							(*Marker -> Unselect)(Marker,0,Y,X,1);
						else
							(*Marker -> Unselect)(Marker,Marker -> LastX,Y,X - Marker -> LastX,1);
					}

					Marker -> FirstColumn	= X;
					Marker -> FirstLine	= Y + Marker -> Top;
				}
			}

			if(NewCharPos > CharEnd)
			{
				if((Lines = Y - Marker -> LastY - 1) >= 0)
				{
					(*Marker -> Select)(Marker,Marker -> LastX,Marker -> LastY,Marker -> Width - Marker -> LastX,1);

					if(Lines > 0)
						(*Marker -> Select)(Marker,0,Marker -> LastY + 1,Marker -> Width,Lines);

					(*Marker -> Select)(Marker,0,Y,X,1);
				}
				else
				{
					if(Delta)
						(*Marker -> Select)(Marker,0,Y,X,1);
					else
						(*Marker -> Select)(Marker,Marker -> LastX,Y,X - Marker -> LastX,1);
				}

				Marker -> LastColumn	= X;
				Marker -> LastLine	= Y + Marker -> Top;
			}
		}
		else
		{
			if(Delta && Y > OriginY)
				(*Marker -> Select)(Marker,0,Y,Marker -> Width,1);

			if(Crossed)
			{
				if((Lines = Marker -> LastY - OriginY - 1) >= 0)
				{
					(*Marker -> Unselect)(Marker,0,Marker -> LastY,Marker -> LastX,1);

					if(Lines > 0)
						(*Marker -> Unselect)(Marker,0,OriginY + 1,Marker -> Width,Lines);

					(*Marker -> Unselect)(Marker,Marker -> OriginX,OriginY,Marker -> Width - Marker -> OriginX,1);
				}
				else
				{
					if(Delta)
						(*Marker -> Unselect)(Marker,Marker -> LastX,OriginY,Marker -> Width - Marker -> LastX,1);
					else
						(*Marker -> Unselect)(Marker,Marker -> OriginX,OriginY,Marker -> LastX - Marker -> OriginX,1);
				}

				Marker -> LastColumn	= Marker -> OriginX;
				Marker -> LastLine	= Marker -> OriginY;

				Marker -> LastX		= Marker -> OriginX;
				Marker -> LastY		= OriginY;
			}
			else
			{
				if(OldCharPos > CharOrigin)
				{
					if((Lines = Marker -> LastY - Y - 1) >= 0)
					{
						if(Marker -> LastX)
							(*Marker -> Unselect)(Marker,0,Marker -> LastY,Marker -> LastX,1);

						if(Lines > 0)
							(*Marker -> Unselect)(Marker,0,Y + 1,Marker -> Width,Lines);

						(*Marker -> Unselect)(Marker,X,Y,Marker -> Width - X,1);
					}
					else
					{
						if(Delta)
							(*Marker -> Unselect)(Marker,X,Y,Marker -> Width - X,1);
						else
							(*Marker -> Unselect)(Marker,X,Y,Marker -> LastX - X,1);
					}

					Marker -> LastColumn	= X;
					Marker -> LastLine	= Y + Marker -> Top;
				}
			}

			if(NewCharPos < CharStart)
			{
				if((Lines = Marker -> LastY - Y - 1) >= 0)
				{
					if(Marker -> LastX)
						(*Marker -> Select)(Marker,0,Marker -> LastY,Marker -> LastX,1);

					if(Lines > 0)
						(*Marker -> Select)(Marker,0,Y + 1,Marker -> Width,Lines);

					(*Marker -> Select)(Marker,X,Y,Marker -> Width - X,1);
				}
				else
				{
					if(Delta)
						(*Marker -> Select)(Marker,X,Y,Marker -> Width - X,1);
					else
						(*Marker -> Select)(Marker,X,Y,Marker -> LastX - X,1);
				}

				Marker -> FirstColumn	= X;
				Marker -> FirstLine	= Y + Marker -> Top;
			}
		}

		Marker -> LastX = X;
		Marker -> LastY = Y;
	}
}


	/* ToggleSelect(struct BlockMarker *Marker,LONG Left,LONG Top,LONG Width,LONG Height):
	 *
	 *	Toggle selection subroutine.
	 */

VOID
ToggleSelect(struct BlockMarker *Marker,LONG Left,LONG Top,LONG Width,LONG Height)
{
	struct RastPort *RPort = (struct RastPort *)Marker -> Object;

	if(Width && Height)
	{
		UWORD	OldAPen	= ReadAPen(RPort),
			OldBPen	= ReadBPen(RPort),
			OldDrMd	= ReadDrMd(RPort),
			Mask;

		Left	= Marker -> LeftEdge + Left * Marker -> TextFontWidth;
		Top	= Marker -> TopEdge + Top * Marker -> TextFontHeight;

		Mask = (1L << RPort -> BitMap -> Depth) - 1;

		if(Kick30)
		{
			SetABPenDrMd(RPort,Mask,OldBPen,JAM1 | COMPLEMENT);

			RectFill(RPort,Left,Top,Left + Width * Marker -> TextFontWidth - 1,Top + Height * Marker -> TextFontHeight - 1);

			SetABPenDrMd(RPort,OldAPen,OldBPen,OldDrMd);
		}
		else
		{
			SetAPen(RPort,Mask);
			SetDrMd(RPort,JAM1 | COMPLEMENT);

			RectFill(RPort,Left,Top,Left + Width * Marker -> TextFontWidth - 1,Top + Height * Marker -> TextFontHeight - 1);

			SetAPen(RPort,OldAPen);
			SetDrMd(RPort,OldDrMd);
		}
	}
}

	/* WriteTrimmedString(struct IFFHandle *Handle,STRPTR String,LONG Len):
	 *
	 *	Write a string to the clipboard, sans trailing spaces.
	 */

VOID __regargs
WriteTrimmedString(struct IFFHandle *Handle,STRPTR String,LONG Len)
{
	while(Len > 0 && String[Len - 1] == ' ')
		Len--;

	if(Len)
		WriteChunkBytes(Handle,String,Len);
}

	/* TrimLength(STRPTR String,LONG Len):
	 *
	 *	Determine the length of a string, sans trailing spaces.
	 */

LONG __regargs
TrimLength(STRPTR String,LONG Len)
{
	while(Len > 0)
	{
		if(String[Len - 1] != ' ')
			break;
		else
			Len--;
	}

	return(Len);
}

	/* ClipPage(struct BlockMarker *Marker,BYTE Append):
	 *
	 *	Send the entire marked page to the clipboard.
	 */

STATIC VOID __inline
ClipPage(struct BlockMarker *Marker,BYTE Append)
{
	struct IFFHandle	*Handle;
	APTR			 Buffer;
	LONG			 Size;

	if(Append)
		GetClipContents(Config -> ClipConfig -> ClipboardUnit,&Buffer,&Size);
	else
	{
		Buffer	= NULL;
		Size	= 0;
	}

	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(Buffer)
						{
							WriteChunkBytes(Handle,Buffer,Size);

							FreeVecPooled(Buffer);
						}

						WriteTrimmedString(Handle,&Raster[RasterWidth * Marker -> FirstLine + Marker -> FirstColumn],Marker -> Width - Marker -> FirstColumn);
						WriteChunkBytes(Handle,"\n",1);

						if(Lines > 0)
						{
							STRPTR Line = &Raster[(Marker -> FirstLine + 1) * RasterWidth];

							for(i = 0 ; i < Lines ; i++)
							{
								WriteTrimmedString(Handle,Line,Marker -> Width);
								WriteChunkBytes(Handle,"\n",1);

								Line += RasterWidth;
							}
						}

						WriteTrimmedString(Handle,&Raster[RasterWidth * Marker -> LastLine],Marker -> LastColumn);
						WriteChunkBytes(Handle,"\n",1);

						PopChunk(Handle);
					}

					PopChunk(Handle);
				}

				CloseIFF(Handle);
			}

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

		FreeIFF(Handle);
	}
}

	/* MarkWord(LONG MouseX,LONG MouseY):
	 *
	 *	Mark a single word on the main screen (double-click).
	 */

VOID __regargs
MarkWord(LONG MouseX,LONG MouseY)
{
	LONG FirstX,FirstY;

	FirstX = MouseX / TextFontWidth;
	FirstY = MouseY / TextFontHeight;

	if(FirstX > LastColumn)
		FirstX = LastColumn;

	if(FirstY > LastLine)
		FirstY = LastLine;

	ObtainSemaphore(RasterSemaphore);

	if(Raster[FirstY * RasterWidth + FirstX] != ' ' && Raster[FirstY * RasterWidth + FirstX])
	{
		STRPTR	Line = &Raster[FirstY * RasterWidth];
		LONG	LastX;

		LastX = FirstX;

		while(FirstX > 0 && Line[FirstX - 1] != ' ')
			FirstX--;

		while(LastX < LastColumn && Line[LastX + 1] != ' ')
			LastX++;

		ObtainTerminal();

		if(WindowMarker = BM_SetMark(Window -> RPort,ToggleSelect,ToggleSelect,LastColumn + 1,LastLine + 1,WindowLeft,WindowTop,0,LastLine + 1,FirstX,FirstY,TextFontWidth,TextFontHeight))
		{
			SetWrMsk(RPort,DepthMask);

			Marking = TRUE;

			ReportMouse(TRUE,Window);

			BM_ExtendMark(WindowMarker,LastX + 1,FirstY,0);

			SetClipMenu(TRUE);
		}

		ReleaseTerminal();
	}

	ReleaseSemaphore(RasterSemaphore);
}

	/* SetMarker(LONG MouseX,LONG MouseY):
	 *
	 *	Anchor a marker to the current mouse position.
	 */

VOID __regargs
SetMarker(LONG MouseX,LONG MouseY)
{
	LONG FirstX,FirstY;

	FirstX = MouseX / TextFontWidth;
	FirstY = MouseY / TextFontHeight;

	if(FirstX > LastColumn)
		FirstX = LastColumn;

	if(FirstY > LastLine)
		FirstY = LastLine;

	ObtainTerminal();

	if(WindowMarker = BM_SetMark(Window -> RPort,ToggleSelect,ToggleSelect,LastColumn + 1,LastLine + 1,WindowLeft,WindowTop,0,LastLine + 1,FirstX,FirstY,TextFontWidth,TextFontHeight))
	{
		SetWrMsk(RPort,DepthMask);

		Marking = TRUE;

		ReportMouse(TRUE,Window);

		SetClipMenu(TRUE);
	}

	ReleaseTerminal();
}

	/* MoveMarker(LONG MouseX,LONG MouseY):
	 *
	 *	Move the marker with the mouse.
	 */

VOID __regargs
MoveMarker(LONG MouseX,LONG MouseY)
{
	if(WindowMarker)
	{
		ObtainTerminal();

		BM_ExtendMark(WindowMarker,(MouseX + TextFontWidth - 1) / TextFontWidth,MouseY / TextFontHeight,0);

		ReleaseTerminal();
	}
}

	/* DropMarker():
	 *
	 *	Drop the window marker, restore the window contents.
	 */

VOID
DropMarker()
{
	if(WindowMarker)
	{
		struct RastPort	*RPort	= WindowMarker -> Object;
		UBYTE		 Mask	= WindowMarker -> WriteMask;

		ObtainTerminal();

		BM_ClearMark(WindowMarker);

		ReleaseTerminal();

		SetWrMsk(RPort,Mask);

		ReportMouse(FALSE,Window);

		WindowMarker = NULL;

		Marking = FALSE;

		SetClipMenu(FALSE);
	}
}

	/* FreeMarker():
	 *
	 *	Free the main window marker.
	 */

VOID
FreeMarker()
{
	if(WindowMarker)
	{
		struct RastPort	*RPort	= WindowMarker -> Object;
		UBYTE		 Mask	= WindowMarker -> WriteMask;

		FreeVecPooled(WindowMarker);

		WindowMarker = NULL;

		SetWrMsk(RPort,Mask);

		ReportMouse(FALSE,Window);

		Marking = FALSE;

		SetClipMenu(FALSE);
	}
}

	/* ClipMarker(BYTE Append):
	 *
	 *	Transfer the marked area to the clipboard.
	 */

VOID __regargs
ClipMarker(BYTE Append)
{
	if(WindowMarker)
	{
		ObtainSemaphore(RasterSemaphore);

		SetWait(Window);

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

			WindowMarker -> FirstColumn = 0;
		}

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

			WindowMarker -> LastColumn = WindowMarker -> Width;
		}

		if(WindowMarker -> FirstLine <= WindowMarker -> LastLine)
		{
			if(WindowMarker -> FirstLine != WindowMarker -> LastLine || WindowMarker -> FirstColumn != WindowMarker -> LastColumn)
			{
				if(WindowMarker -> FirstLine == WindowMarker -> LastLine)
				{
					if(Append)
						AddClip(&Raster[RasterWidth * WindowMarker -> FirstLine + WindowMarker -> FirstColumn],WindowMarker -> LastColumn - WindowMarker -> FirstColumn);
					else
						SaveClip(&Raster[RasterWidth * WindowMarker -> FirstLine + WindowMarker -> FirstColumn],WindowMarker -> LastColumn - WindowMarker -> FirstColumn);
				}
				else
					ClipPage(WindowMarker,Append);
			}
		}

		ClrWait(Window);

		ReleaseSemaphore(RasterSemaphore);

		DropMarker();
	}
}
