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

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

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

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

	FreeVecPooled(Marker);
}

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

struct BlockMarker *
BM_SetMark(APTR Object,VPTR Select,VPTR Unselect,LONG Width,LONG Height,LONG LeftEdge,LONG TopEdge,LONG Top,LONG Lines,LONG X,LONG Y,LONG TextFontWidth,LONG 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
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;
		BOOL	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)
	{
		ULONG	OldAPen	= ReadAPen(RPort),
			OldBPen	= ReadBPen(RPort),
			OldDrMd	= ReadDrMd(RPort),
			Mask;

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

		if(Kick30)
			Mask = (1L << GetBitMapAttr(RPort -> BitMap,BMA_DEPTH)) - 1;
		else
			Mask = (1L << RPort -> BitMap -> Depth) - 1;

		SetPens(RPort,Mask,OldBPen,JAM1 | COMPLEMENT);

		FillBox(RPort,Left,Top,Width * Marker -> TextFontWidth,Height * Marker -> TextFontHeight);

		SetPens(RPort,OldAPen,OldBPen,OldDrMd);
	}
}

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

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

	if(Len)
	{
		if(NeedClipConversion)
		{
			UBYTE	Buffer[256];
			LONG	Size,i;
			STRPTR	Dest;
			UBYTE	c;

			do
			{
				Size = MIN(Len,256);

				Len -= Size;

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

				if(Dest > Buffer)
				{
					Size = (ULONG)Dest - (ULONG)&Buffer[0];

					while(Size > 0 && Buffer[Size - 1] == ' ')
						Size--;

					if(Size > 0)
						WriteChunkBytes(Handle,Buffer,Size);
				}
			}
			while(Len > 0);
		}
		else
			WriteChunkBytes(Handle,String,Len);
	}
}

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

STATIC VOID
ClipPage(struct BlockMarker *Marker,BOOL Append,BOOL NeedClipConversion)
{
	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,NeedClipConversion);
						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,NeedClipConversion);
								WriteChunkBytes(Handle,"\n",1);

								Line += RasterWidth;
							}
						}

						WriteTrimmedString(Handle,&Raster[RasterWidth * Marker -> LastLine],Marker -> LastColumn,NeedClipConversion);
						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
MarkWord(LONG MouseX,LONG MouseY)
{
	LONG FirstX,FirstY;

	FirstX = (MouseX * CharCellDenominator) / (TextFontWidth * CharCellNominator);
	FirstY = MouseY / TextFontHeight;

	if(FirstX > LastPrintableColumn)
		FirstX = LastPrintableColumn;

	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 < LastPrintableColumn && Line[LastX + 1] != ' ')
			LastX++;

		ObtainSemaphore(&TerminalSemaphore);

		if(WindowMarker = BM_SetMark(Window -> RPort,ToggleSelect,ToggleSelect,LastPrintableColumn + 1,LastLine + 1,WindowLeft,WindowTop,0,LastLine + 1,FirstX,FirstY,(TextFontWidth * CharCellNominator) / CharCellDenominator,TextFontHeight))
		{
			SetMask(RPort,DepthMask);

			Marking = TRUE;

			ReportMouse(TRUE,Window);

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

			SetClipMenu(TRUE);
		}

		ReleaseSemaphore(&TerminalSemaphore);
	}

	ReleaseSemaphore(RasterSemaphore);
}

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

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

	FirstX = (MouseX * CharCellDenominator) / (TextFontWidth * CharCellNominator);
	FirstY = MouseY / TextFontHeight;

	if(FirstX > LastPrintableColumn)
		FirstX = LastPrintableColumn;

	if(FirstY > LastLine)
		FirstY = LastLine;

	ObtainSemaphore(&TerminalSemaphore);

	if(WindowMarker = BM_SetMark(Window -> RPort,ToggleSelect,ToggleSelect,LastPrintableColumn + 1,LastLine + 1,WindowLeft,WindowTop,0,LastLine + 1,FirstX,FirstY,(TextFontWidth * CharCellNominator) / CharCellDenominator,TextFontHeight))
	{
		SetMask(RPort,DepthMask);

		Marking = TRUE;

		ReportMouse(TRUE,Window);

		SetClipMenu(TRUE);
	}

	ReleaseSemaphore(&TerminalSemaphore);
}

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

VOID
MoveMarker(LONG MouseX,LONG MouseY)
{
	if(WindowMarker)
	{
		ULONG EffectiveWidth;

		ObtainSemaphore(&TerminalSemaphore);

		EffectiveWidth = (TextFontWidth * CharCellNominator) / CharCellDenominator;

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

		ReleaseSemaphore(&TerminalSemaphore);
	}
}

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

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

		ObtainSemaphore(&TerminalSemaphore);

		BM_ClearMark(WindowMarker);

		ReleaseSemaphore(&TerminalSemaphore);

		SetMask(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;
		LONG		 Mask	= WindowMarker -> WriteMask;

		FreeVecPooled(WindowMarker);

		WindowMarker = NULL;

		SetMask(RPort,Mask);

		ReportMouse(FALSE,Window);

		Marking = FALSE;

		SetClipMenu(FALSE);
	}
}

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

VOID
ClipMarker(BOOL 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(Config -> TerminalConfig -> FontMode != FONT_STANDARD)
					{
						UBYTE	Buffer[256];
						LONG	Len,i;
						STRPTR	Dest,
							Source	= &Raster[RasterWidth * WindowMarker -> FirstLine + WindowMarker -> FirstColumn];
						LONG	Size	= WindowMarker -> LastColumn - WindowMarker -> FirstColumn;
						LONG	c;

						if(Append)
						{
							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)
									AddClip(Buffer,(ULONG)Dest - (ULONG)&Buffer[0]);
							}
							while(Size > 0);
						}
						else
						{
							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
					{
						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,Config -> TerminalConfig -> FontMode != FONT_STANDARD);
			}
		}

		ClrWait(Window);

		ReleaseSemaphore(RasterSemaphore);

		DropMarker();
	}
}
