/*
**	$Id: termMarker.c,v 1.4 92/08/15 20:14:43 olsen Sta Locker: olsen $
**	$Revision: 1.4 $
**	$Date: 92/08/15 20:14:43 $
**
**	Text block marker routines
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

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

VOID
BM_Draw(struct BlockMarker *Marker,VOID (*Select)(struct BlockMarker *Marker,WORD Left,WORD Top,WORD Width,WORD 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)
	{
		WORD	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
BM_ClearMark(struct BlockMarker *Marker)
{
	BM_Draw(Marker,Marker -> Unselect);

	FreeVec(Marker);
}

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

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

			/* Allocate marker buffer. */

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

			Marker -> Object	= Object;

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

			/* Return marker buffer. */

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

	/* BM_ExtendMark(struct BlockMarker *Marker,WORD X,WORD Y,WORD 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
BM_ExtendMark(struct BlockMarker *Marker,WORD X,WORD Y,WORD 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,WORD Left,WORD Top,WORD Width,WORD Height):
	 *
	 *	Toggle selection subroutine.
	 */

VOID
ToggleSelect(struct BlockMarker *Marker,WORD Left,WORD Top,WORD Width,WORD Height)
{
	if(Width && Height)
		ClipBlit(Marker -> Object,0,0,Marker -> Object,Left * Marker -> TextFontWidth,Top * Marker -> TextFontHeight,Width * Marker -> TextFontWidth,Height * Marker -> TextFontHeight,0x50);
}

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

VOID __regargs
WriteTrimmedString(struct IFFHandle *Handle,STRPTR String,WORD Len)
{
	while(Len > 0)
	{
		if(String[Len - 1] != ' ')
			break;
		else
			Len--;
	}

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

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

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

	return(Len);
}

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

STATIC VOID __regargs
ClipPage(struct BlockMarker *Marker)
{
	struct IFFHandle *Handle;

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

			if(!OpenIFF(Handle,IFFF_WRITE))
			{
				if(!PushChunk(Handle,'FTXT','FORM',IFFSIZE_UNKNOWN))
				{
					if(!PushChunk(Handle,0,'CHRS',IFFSIZE_UNKNOWN))
					{
						WORD	Lines = Marker -> LastLine - Marker -> FirstLine - 1,
							i;

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

	/* MarkClip(BYTE SingleChar,BYTE Xerox):
	 *
	 *	The Big One. Mark areas of the main screen and send them
	 *	to the clipboard.
	 */

VOID
MarkClip(BYTE SingleChar,BYTE Xerox)
{
	WORD	FirstX = Window -> MouseX / TextFontWidth,
		FirstY = Window -> MouseY / TextFontHeight;

	if(SingleChar)
	{
		UBYTE Char;

		Char = Raster[FirstY * RasterWidth + FirstX];

		if(Char && Char != ' ')
		{
			SerWrite(&Char,1);

			if(Xerox)
			{
				switch(Config . SendCR)
				{
					case CR_IGNORE:	break;

					case CR_ASCR:	SerWrite("\r",1);
							break;

					case CR_ASCRLF:	SerWrite("\r\n",2);
							break;
				}
			}
		}
	}
	else
	{
		struct BlockMarker	*Marker;
		BYTE			 HasMouse = FALSE;

		ClearCursor();

		if(Marker = BM_SetMark(Window -> RPort,ToggleSelect,ToggleSelect,LastColumn + 1,LastLine + 1,0,LastLine + 1,Window -> MouseX / TextFontWidth,Window -> MouseY / TextFontHeight,TextFontWidth,TextFontHeight))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Code,Class;
			BYTE			 Done = FALSE;
			UBYTE			 Mask = RPort -> Mask;
			WORD			 PlusX = TextFontWidth - 1;
			WORD			 MouseX,MouseY;

			SetWrMsk(RPort,0xFF);

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

				while(Massage = (struct IntuiMessage *)GT_GetIMsg(Window -> UserPort))
				{
					Class	= Massage -> Class;
					Code	= Massage -> Code;
					MouseX	= Massage -> MouseX;
					MouseY	= Massage -> MouseY;

					GT_ReplyIMsg(Massage);

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

						if(Code == MIDDLEUP)
							Xerox = TRUE;

						Done = TRUE;

						break;
					}

					if(Class == IDCMP_MOUSEMOVE)
					{
						if(!HasMouse)
						{
							SetMarker(Window);

							HasMouse = TRUE;
						}

						BM_ExtendMark(Marker,(MouseX + PlusX) / TextFontWidth,MouseY / TextFontHeight,0);
					}
				}
			}

			SetWrMsk(RPort,Mask);

			if(FirstX != Window -> MouseX / TextFontWidth || FirstY != Window -> MouseY / TextFontHeight)
			{
				SetWait(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(Marker -> FirstLine == Marker -> LastLine)
							SaveClip(&Raster[RasterWidth * Marker -> FirstLine + Marker -> FirstColumn],Marker -> LastColumn - Marker -> FirstColumn);
						else
							ClipPage(Marker);

						if(Xerox)
						{
							if(!ClipInput)
							{
								if(!OpenClip())
									ClipInput = ClipXerox = TRUE;
								else
									ClipInput = ClipXerox = FALSE;
							}
						}
					}
				}
			}

			FreeVec(Marker);
		}

		ClearPointer(Window);

		SetCursor();
	}
}
