/*
**	termStatusDisplay.c
**
**	Status information display routines
**
**	Copyright © 1990-1994 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* The information to be displayed. */

enum	{	INFO_STATUS,INFO_BUFFER,INFO_PROTOCOL,INFO_EMULATION,INFO_BAUDRATE,
		INFO_PARAMETERS,INFO_CURRENTTIME,INFO_ONLINETIME,INFO_ONLINECOST };

	/* The current status line display mode. */

enum	{	MODE_SCREEN_NORMAL,MODE_SCREEN_COMPRESSED,MODE_WB_NORMAL,MODE_WB_COMPRESSED };

	/* Enumerated text boxes. */

enum	{	STATUSBOX_STATUS_FONT,STATUSBOX_PROTOCOL_TERMINAL,
		STATUSBOX_RATE_PARAMETERS,STATUSBOX_TIME_ONLINE };

	/* The tags our private status gadget class responds to. */

#define SG_Status	(TAG_USER+1)
#define SG_Font		(TAG_USER+2)
#define SG_Protocol	(TAG_USER+3)
#define SG_Emulation	(TAG_USER+4)
#define SG_BaudRate	(TAG_USER+5)
#define SG_Parameters	(TAG_USER+6)
#define SG_CurrentTime	(TAG_USER+7)
#define SG_OnlineTime	(TAG_USER+8)
#define SG_OnlineCost	(TAG_USER+9)

	/* Special private information maintained and required
	 * by the status gadget class.
	 */

struct StatusInfo
{
	struct TextBox		*BoxArray[4],
				*BoxList;

	LONG			 FullWidth;
	LONG			 FirstColumn;

	UWORD			 Mask;
	UWORD			 Mode;

	LONG			 LastWidth;

	UBYTE			 Strings[8][20];

	struct SignalSemaphore	 Semaphore;

	LONG			 WindowWidth,
				 WindowHeight,
				 WindowFlags;
};

	/* The status server passes this structure to the
	 * rendering routine if the status information
	 * is to be updated.
	 */

struct ObjectCarrier
{
	struct RastPort	 *RPort;
	struct TextBox	**BoxArray;
};

	/* A custom message type for the display update server. */

struct UpdateMessage
{
	struct Message	VanillaMessage;

	APTR		Object;
	UBYTE		Mode,
			Type;
};

	/* The following static strings are displayed in the status
	 * window.
	 */

STATIC STRPTR	ConfigBufferState[3],
		ConfigEmulation[6],
		ConfigParity[6],
		ConfigStatus[8];

	/* Width of the status line text, required in case the user interface
	 * font happens to be proportional-spaced.
	 */

STATIC LONG			 StatusLineWidth;

	/* Our local private status gadget class. */

STATIC struct IClass		*StatusGadgetClass;

	/* The status display update task. */

STATIC struct Task		*StatusDisplayTask;
STATIC struct MsgPort		*StatusDisplayPort;

	/* Prototypes for this module. */

STATIC ULONG __regargs		SetStatusGadget(struct IClass *class,Object *object,struct opSet *SetInfo);
STATIC ULONG __regargs		GetStatusGadget(struct IClass *class,Object *object,struct opGet *GetInfo);
STATIC ULONG __regargs		RenderStatusGadget(struct IClass *class,Object *object,struct gpRender *RenderInfo);
STATIC ULONG __regargs		DelStatusGadget(struct IClass *class,Object *object,Msg msg);
STATIC ULONG __regargs		NewStatusGadget(struct IClass *class,Object *object,struct opSet *SetMethod);
STATIC ULONG __saveds __asm	StatusGadgetDispatch(register __a0 struct IClass *class,register __a2 Object *object,register __a1 Msg msg);
STATIC VOID			FreeStatusGadgetClass(VOID);
STATIC BYTE			NewStatusGadgetClass(VOID);

	/* GetStatusGadget():
	 *
	 *	Query information on the status gadget.
	 */

STATIC ULONG __regargs
GetStatusGadget(struct IClass *class,Object *object,struct opGet *GetInfo)
{
	struct StatusInfo *StatusInfo = INST_DATA(class,object);

	switch(GetInfo -> opg_AttrID)
	{
		case SGA_FullWidth:

			*GetInfo -> opg_Storage = (ULONG)StatusInfo -> FullWidth;
			return(1);

		default:

			return(DoSuperMethodA(class,object,(Msg)GetInfo));
	}
}

	/* SetStatusGadget():
	 *
	 *	Set certain attributes of the status gadget.
	 */

STATIC ULONG __regargs
SetStatusGadget(struct IClass *class,Object *object,struct opSet *SetInfo)
{
	struct StatusInfo	*StatusInfo = INST_DATA(class,object);
	LONG			 i,j;
	struct RastPort		*RPort;
	struct TagItem		*Tag;

	DoSuperMethodA(class,object,(Msg)SetInfo);

		/* Make sure that the information is available and
		 * nobody else tries to modify it.
		 */

	ObtainSemaphore(&StatusInfo -> Semaphore);

		/* Obtain the new text to be displayed in the gadget. */

	for(i = SG_Status, j = 0 ; i <= SG_OnlineCost ; i++, j++)
	{
		if(Tag = FindTagItem(i,SetInfo -> ops_AttrList))
		{
			strcpy(StatusInfo -> Strings[j],(STRPTR)Tag -> ti_Data);

			StatusInfo -> Mask |= (1L << j);
		}
	}

		/* Release the lock. */

	ReleaseSemaphore(&StatusInfo -> Semaphore);

	if(AttemptLockLayerRom(Window -> RPort -> Layer))
	{
			/* Obtain the drawing area lock. */

		RPort = ObtainGIRPort(SetInfo -> ops_GInfo);

		UnlockLayerRom(Window -> RPort -> Layer);

		if(RPort)
		{
				/* Normal, uncompressed status line? */

			if(StatusInfo -> Mode == MODE_WB_NORMAL)
			{
					/* Take a look at the strings which have
					 * changed and render the corresponding
					 * text.
					 */

				for(i = 0 ; i < 8 ; i++)
				{
					if(StatusInfo -> Mask & (1L << i))
					{
						SZ_SetLine(RPort,StatusInfo -> BoxArray[i / 2],i % 2,StatusInfo -> Strings[i]);

						StatusInfo -> Mask &= ~(1L << i);
					}
				}
			}
			else
			{
				STATIC BYTE Offsets[8] =
				{
					 0,
					-1,
					26,
					11,
					40,
					53,
					61,
					72
				};

				struct Gadget	*Gadget;
				UBYTE		 LineBuffer[90];
				WORD		 i,j,k;
				LONG		 Width,
						 Left,
						 Top;

					/* Get the real gadget. */

				Gadget = (struct Gadget *)object;

					/* Fill the line with defaults. */

				strcpy(LineBuffer,"         ·              ·             ·            ·       ·          ·         ");

					/* Add the information strings. */

				for(i = 0 ; i < 8 ; i++)
				{
					if(Offsets[i] >= 0)
					{
						j = strlen(StatusInfo -> Strings[i]);

						for(k = 0 ; k < j ; k++)
							LineBuffer[Offsets[i] + k] = StatusInfo -> Strings[i][k];
					}
				}

					/* Determine the pixel width of the string. */

				Width = TextLength(RPort,LineBuffer,80);

					/* Get the rendering top offset. */

				Top = SetInfo -> ops_GInfo -> gi_Window -> Height + Gadget -> TopEdge;

				SetDrMd(RPort,JAM2);

					/* Are we to clear the area the last rendering
					 * operation obscured?
					 */

				if(StatusInfo -> LastWidth && StatusInfo -> LastWidth != Width)
				{
					Left = (SetInfo -> ops_GInfo -> gi_Window -> Width - StatusInfo -> LastWidth) / 2;

						/* Set the approriate rendering pen. */

					if(SetInfo -> ops_GInfo -> gi_Window -> Flags & WFLG_WINDOWACTIVE)
						SetAPen(RPort,SetInfo -> ops_GInfo -> gi_DrInfo -> dri_Pens[FILLPEN]);
					else
						SetAPen(RPort,SetInfo -> ops_GInfo -> gi_DrInfo -> dri_Pens[BACKGROUNDPEN]);

						/* Clear the area. */

					RectFill(RPort,Left,Top,Left + StatusInfo -> LastWidth - 1,Top + Gadget -> Height - 3);
				}

					/* Set the approriate rendering pens. */

				if(SetInfo -> ops_GInfo -> gi_Window -> Flags & WFLG_WINDOWACTIVE)
				{
					SetAPen(RPort,SetInfo -> ops_GInfo -> gi_DrInfo -> dri_Pens[FILLTEXTPEN]);
					SetBPen(RPort,SetInfo -> ops_GInfo -> gi_DrInfo -> dri_Pens[FILLPEN]);
				}
				else
				{
					SetAPen(RPort,SetInfo -> ops_GInfo -> gi_DrInfo -> dri_Pens[TEXTPEN]);
					SetBPen(RPort,SetInfo -> ops_GInfo -> gi_DrInfo -> dri_Pens[BACKGROUNDPEN]);
				}

					/* Remember the text pixel width. */

				StatusInfo -> LastWidth = Width;

					/* Display the status line. */

				Move(RPort,(SetInfo -> ops_GInfo -> gi_Window -> Width - StatusInfo -> LastWidth) / 2,Top + RPort -> Font -> tf_Baseline);
				Text(RPort,LineBuffer,80);
			}

				/* Release the rendering area. */

			ReleaseGIRPort(RPort);
		}
	}

	return(1);
}

	/* RenderStatusGadget():
	 *
	 *	Redraw the entire status gadget.
	 */

STATIC ULONG __regargs
RenderStatusGadget(struct IClass *class,Object *object,struct gpRender *RenderInfo)
{
	struct StatusInfo	*StatusInfo	= INST_DATA(class,object);
	struct RastPort		*RPort		= RenderInfo -> gpr_RPort;
	BYTE			 MustRender,
				 UpdateOnly;
	LONG			 i;

	MustRender = TRUE;
	UpdateOnly = FALSE;

	StatusInfo -> Mask |= 0xFF;

	if(MustRender)
	{
			/* Normal, uncompressed display? */

		if(StatusInfo -> Mode == MODE_WB_NORMAL)
		{
				/* Refresh the entire imagery (the default)? */

			if(RenderInfo -> gpr_Redraw == GREDRAW_REDRAW && !UpdateOnly)
			{
				struct Gadget	*Gadget = (struct Gadget *)object;
				LONG		 X;

					/* Don't let anyone modify the text. */

				ObtainSemaphore(&StatusInfo -> Semaphore);

					/* Get the new gadget left offset. */

				if((X = (RenderInfo -> gpr_GInfo -> gi_Window -> Width - StatusInfo -> FullWidth) / 2) < 0)
					X = 0;

				X += StatusInfo -> FirstColumn - SZ_GetBoxInfo(StatusInfo -> BoxList,BOX_LEFT);

					/* Move the status line to the new location. */

				SZ_SetBoxes(StatusInfo -> BoxList,-1,RenderInfo -> gpr_GInfo -> gi_Window -> Height + Gadget -> TopEdge);

				if(X)
					SZ_MoveBoxes(StatusInfo -> BoxList,X,0);

					/* Set the box title rendering pen. */

				if(RenderInfo -> gpr_GInfo -> gi_Window -> Flags & WFLG_WINDOWACTIVE)
					SZ_SetTitlePen(StatusInfo -> BoxList,RenderInfo -> gpr_GInfo -> gi_DrInfo -> dri_Pens[FILLTEXTPEN],RenderInfo -> gpr_GInfo -> gi_DrInfo -> dri_Pens[FILLPEN]);
				else
					SZ_SetTitlePen(StatusInfo -> BoxList,RenderInfo -> gpr_GInfo -> gi_DrInfo -> dri_Pens[TEXTPEN],RenderInfo -> gpr_GInfo -> gi_DrInfo -> dri_Pens[BACKGROUNDPEN]);

					/* Redraw the boxes. */

				SZ_DrawBoxes(RPort,StatusInfo -> BoxList);

					/* Redraw all the strings. */

				ReleaseSemaphore(&StatusInfo -> Semaphore);
			}

				/* Update the information which has changed. */

			for(i = 0 ; i < 8 ; i++)
			{
				if(StatusInfo -> Mask & (1L << i))
				{
					SZ_SetLine(RPort,StatusInfo -> BoxArray[i / 2],i % 2,StatusInfo -> Strings[i]);

					StatusInfo -> Mask &= ~(1L << i);
				}
			}
		}
		else
		{
			STATIC BYTE Offsets[8] =
			{
				 0,
				-1,
				26,
				11,
				40,
				53,
				61,
				72
			};

			struct Gadget	*Gadget;
			UBYTE		 LineBuffer[90];
			WORD		 i,j,k;
			LONG		 Width,
					 Left,
					 Top;

				/* Grab the access semaphore. */

			ObtainSemaphore(&StatusInfo -> Semaphore);

				/* Get the real gadget info. */

			Gadget = (struct Gadget *)object;

				/* Fill the line buffer with defaults. */

			strcpy(LineBuffer,"         ·              ·             ·            ·       ·          ·         ");

				/* Fill in the information strings. */

			for(i = 0 ; i < 8 ; i++)
			{
				if(Offsets[i] >= 0)
				{
					j = strlen(StatusInfo -> Strings[i]);

					for(k = 0 ; k < j ; k++)
						LineBuffer[Offsets[i] + k] = StatusInfo -> Strings[i][k];
				}
			}

				/* Determine the pixel width of the line. */

			Width = TextLength(RPort,LineBuffer,80);

				/* Get the line top offset. */

			Top = RenderInfo -> gpr_GInfo -> gi_Window -> Height + Gadget -> TopEdge;

			SetDrMd(RPort,JAM2);

				/* Clear the previously written text if necessary. */

			if(StatusInfo -> LastWidth && RenderInfo -> gpr_Redraw != GREDRAW_REDRAW)
			{
				Left = (RenderInfo -> gpr_GInfo -> gi_Window -> Width - StatusInfo -> LastWidth) / 2;

				if(RenderInfo -> gpr_GInfo -> gi_Window -> Flags & WFLG_WINDOWACTIVE)
					SetAPen(RPort,RenderInfo -> gpr_GInfo -> gi_DrInfo -> dri_Pens[FILLPEN]);
				else
					SetAPen(RPort,RenderInfo -> gpr_GInfo -> gi_DrInfo -> dri_Pens[BACKGROUNDPEN]);

				RectFill(RPort,Left,Top,Left + StatusInfo -> LastWidth - 1,Top + Gadget -> Height - 3);
			}

				/* Set the approriate rendering colours. */

			if(RenderInfo -> gpr_GInfo -> gi_Window -> Flags & WFLG_WINDOWACTIVE)
			{
				SetAPen(RPort,RenderInfo -> gpr_GInfo -> gi_DrInfo -> dri_Pens[FILLTEXTPEN]);
				SetBPen(RPort,RenderInfo -> gpr_GInfo -> gi_DrInfo -> dri_Pens[FILLPEN]);
			}
			else
			{
				SetAPen(RPort,RenderInfo -> gpr_GInfo -> gi_DrInfo -> dri_Pens[TEXTPEN]);
				SetBPen(RPort,RenderInfo -> gpr_GInfo -> gi_DrInfo -> dri_Pens[BACKGROUNDPEN]);
			}

				/* Remember last pixel width. */

			StatusInfo -> LastWidth = Width;

				/* Render the status line. */

			Move(RPort,(RenderInfo -> gpr_GInfo -> gi_Window -> Width - StatusInfo -> LastWidth) / 2,Top + RPort -> Font -> tf_Baseline);
			Text(RPort,LineBuffer,80);

				/* Release the lock. */

			ReleaseSemaphore(&StatusInfo -> Semaphore);
		}
	}

	return(0);
}

	/* DelStatusGadget():
	 *
	 *	Deallocate the status gadget.
	 */

STATIC ULONG __regargs
DelStatusGadget(struct IClass *class,Object *object,Msg msg)
{
	struct StatusInfo *StatusInfo = INST_DATA(class,object);

	if(StatusInfo -> BoxList)
		SZ_FreeBoxes(StatusInfo -> BoxList);

	return(DoSuperMethodA(class,object,msg));
}

	/* NewStatusGadget():
	 *
	 *	Create a new status gadget.
	 */

STATIC ULONG __regargs
NewStatusGadget(struct IClass *class,Object *object,struct opSet *SetMethod)
{
	struct Gadget	*NewGadget;
	struct TagItem	*Item;
	LONG		 Mode;
	struct Screen	*LocalScreen;

		/* Determine the status line mode (compressed or normal). */

	if(Item = FindTagItem(SGA_Mode,SetMethod -> ops_AttrList))
	{
		switch(Item -> ti_Data)
		{
			case MODE_WB_NORMAL:
			case MODE_WB_COMPRESSED:

				Mode = Item -> ti_Data;
				break;

			default:

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

		/* Set up the defaults, note that this class
		 * implementation can, strictly speaking, not
		 * be considered reentrant.
		 */

	if(SharedScreen)
		LocalScreen = SharedScreen;
	else
		LocalScreen = DefaultPubScreen;

	SZ_SizeSetup(LocalScreen,&UserFont);

		/* Create the new object. */

	if(NewGadget = (struct Gadget *)DoSuperMethodA(class,object,(Msg)SetMethod))
	{
		struct StatusInfo	*StatusInfo = INST_DATA(class,NewGadget);
		LONG			 SizeGadgetWidth;

		StatusInfo -> WindowWidth = StatusInfo -> WindowHeight = 0;

			/* Determine the width of the window sizing gadget,
			 * hopefully these values are never going to be
			 * changed.
			 */

		if(LocalScreen -> Flags & SCREENHIRES)
			SizeGadgetWidth = SIZE_GADGET_WIDTH_HIGH;
		else
			SizeGadgetWidth = SIZE_GADGET_WIDTH_LOW;

			/* Clear the extra information area. */

		memset(StatusInfo,0,sizeof(struct StatusInfo));

			/* Initialize the signal semaphore. */

		InitSemaphore(&StatusInfo -> Semaphore);

			/* Is it the `big one'? */

		if((StatusInfo -> Mode = Mode) == MODE_WB_NORMAL)
		{
			WORD		 ColumnLeft[4],
					 ColumnWidth[4];
			struct TextBox	*Box;
			LONG		 i,Max = 0,Len,FullWidth,BoxCounter = 0;

			ColumnLeft[0] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_STATUS_TXT,MSG_TERMSTATUSDISPLAY_FONT_TXT,-1);
			ColumnLeft[1] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_PROTOCOL_TXT,MSG_TERMSTATUSDISPLAY_TERMINAL_TXT,-1);
			ColumnLeft[2] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_BAUDRATE_TXT,MSG_TERMSTATUSDISPLAY_PARAMETERS_TXT,-1);
			ColumnLeft[3] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_TIME_TXT,MSG_TERMSTATUSDISPLAY_ONLINE_TXT,-1);

			for(i = 0 ; ConfigStatus[i] ; i++)
			{
				if((Len = SZ_BoxWidth(strlen(ConfigStatus[i]))) > Max)
					Max = Len;
			}

			for(i = 0 ; ConfigBufferState[i] ; i++)
			{
				if((Len = SZ_BoxWidth(strlen(ConfigBufferState[i]))) > Max)
					Max = Len;
			}

			ColumnWidth[0] = Max;

			Max = SZ_BoxWidth(12);

			for(i = 0 ; ConfigEmulation[i] ; i++)
			{
				if((Len = SZ_BoxWidth(strlen(ConfigEmulation[i]))) > Max)
					Max = Len;
			}

			ColumnWidth[1] = Max;

			Max = SZ_BoxWidth(10);

			for(i = 0 ; ConfigParity[i] ; i++)
			{
				if((Len = SZ_BoxWidth(4 + strlen(ConfigParity[i]))) > Max)
					Max = Len;
			}

			ColumnWidth[2] = Max;

			ColumnWidth[3] = SZ_BoxWidth(8);

			FullWidth = 0;

			for(i = 0 ; i < 4 ; i++)
				FullWidth += ColumnWidth[i] + ColumnLeft[i];

			FullWidth += 3 * InterWidth;

			StatusInfo -> FullWidth		= FullWidth + 2 * SizeGadgetWidth;
			StatusInfo -> FirstColumn	= ColumnLeft[0];

			if(FullWidth > LocalScreen -> Width)
				SZ_SetLeftEdge(ColumnLeft[0]);
			else
				SZ_SetLeftEdge((LocalScreen -> Width - FullWidth) / 2 + ColumnLeft[0]);

			SZ_SetAbsoluteTop(2);
			SZ_SetTopEdge(2);

			SZ_SetWidth(ColumnWidth[0]);

			StatusInfo -> BoxArray[BoxCounter++] = Box = SZ_CreateTextBox(&StatusInfo -> BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_STATUS_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_FONT_TXT),NULL);

			SZ_SetWidth(ColumnWidth[1]);
			SZ_AddLeftOffset(ColumnLeft[1]);

			StatusInfo -> BoxArray[BoxCounter++] = Box = SZ_CreateTextBox(&StatusInfo -> BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
				SZ_NewColumn,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_PROTOCOL_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_TERMINAL_TXT),NULL);

			SZ_SetWidth(ColumnWidth[2]);
			SZ_AddLeftOffset(ColumnLeft[2]);

			StatusInfo -> BoxArray[BoxCounter++] = Box = SZ_CreateTextBox(&StatusInfo -> BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
				SZ_NewColumn,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_BAUDRATE_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_PARAMETERS_TXT),NULL);

			SZ_SetWidth(ColumnWidth[3]);
			SZ_AddLeftOffset(ColumnLeft[3]);

			StatusInfo -> BoxArray[BoxCounter] = Box = SZ_CreateTextBox(&StatusInfo -> BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
				SZ_NewColumn,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_TIME_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_ONLINE_TXT),NULL);

			if(!Box)
			{
				SZ_FreeBoxes(StatusInfo -> BoxList);

				DoSuperMethod(class,(Object *)NewGadget,OM_DISPOSE);

				NewGadget = NULL;
			}
		}
		else
			StatusInfo -> FullWidth = SZ_BoxWidth(80) + 2 * SizeGadgetWidth;
	}

	return((ULONG)NewGadget);
}

	/* StatusGadgetDispatch():
	 *
	 *	Dispatch an object message.
	 */

STATIC ULONG __saveds __asm
StatusGadgetDispatch(register __a0 struct IClass *class,register __a2 Object *object,register __a1 Msg msg)
{
	switch(msg -> MethodID)
	{
			/* Create new gadget. */

		case OM_NEW:

			return(NewStatusGadget(class,object,(struct opSet *)msg));

			/* Set attributes. */

		case OM_SET:

			return(SetStatusGadget(class,object,(struct opSet *)msg));

			/* Get attributes. */

		case OM_GET:

			return(GetStatusGadget(class,object,(struct opGet *)msg));

			/* Dispose gadget. */

		case OM_DISPOSE:

			return(DelStatusGadget(class,object,msg));

			/* Render the gadget. */

		case GM_RENDER:

			return(RenderStatusGadget(class,object,(struct gpRender *)msg));

			/* Do nothing. */

		case GM_HITTEST:

			return(0);

			/* Catchall... */

		default:

			return(DoSuperMethodA(class,object,msg));
	}
}

	/* FreeStatusGadgetClass():
	 *
	 *	Free the private status gadget class.
	 */

STATIC VOID
FreeStatusGadgetClass()
{
	if(StatusGadgetClass)
	{
		FreeClass(StatusGadgetClass);

		StatusGadgetClass = NULL;
	}
}

	/* NewStatusGadgetClass():
	 *
	 *	Create a new status gadget class.
	 */

STATIC BYTE
NewStatusGadgetClass()
{
		/* Release the old instance. */

	FreeStatusGadgetClass();

		/* Localize the title texts. */

	LocalizeString(ConfigBufferState,MSG_TERMSTATUSDISPLAY_FROZEN_TXT,MSG_TERMSTATUSDISPLAY_RECORDING_TXT);
	LocalizeString(ConfigEmulation,MSG_TERMAUX_ANSI_VT102_TXT,MSG_TERMAUX_HEX_TXT);
	LocalizeString(ConfigParity,MSG_TERMAUX_NONE_TXT,MSG_TERMAUX_SPACE_TXT);
	LocalizeString(ConfigStatus,MSG_TERMAUX_READY_TXT,MSG_TERMAUX_HANG_UP_TXT);

		/* Create the class. */

	if(StatusGadgetClass = MakeClass(NULL,GADGETCLASS,NULL,sizeof(struct StatusInfo),0))
	{
			/* Install the dispatcher. */

		StatusGadgetClass -> cl_Dispatcher . h_Entry = (LONG (*)())StatusGadgetDispatch;

		return(TRUE);
	}
	else
		return(FALSE);
}

	/* DeleteStatusGadget(struct Gadget *Gadget):
	 *
	 *	Delete the status gadget.
	 */

VOID __regargs
DeleteStatusGadget(struct Gadget *Gadget)
{
	DisposeObject(Gadget);

	FreeStatusGadgetClass();
}

	/* CreateStatusGadget(LONG Width,LONG ID):
	 *
	 *	Create the status gadget.
	 */

struct Gadget * __regargs
CreateStatusGadget(LONG Width,LONG ID)
{
		/* Create the new class. */

	if(NewStatusGadgetClass())
	{
		struct Gadget	*Gadget;
		UBYTE		 Mode;
		LONG		 Height;
		struct Screen	*LocalScreen;

		if(SharedScreen)
			LocalScreen = SharedScreen;
		else
			LocalScreen = DefaultPubScreen;

			/* Determine the mode of operation. */

		if(Config -> ScreenConfig -> StatusLine == STATUSLINE_COMPRESSED)
			Mode = MODE_WB_COMPRESSED;
		else
			Mode = MODE_WB_NORMAL;

		SZ_SizeSetup(LocalScreen,&UserFont);

			/* Determine object height. */

		if(Mode == MODE_WB_NORMAL)
			Height = 3 + SZ_BoxHeight(2);
		else
			Height = 2 + UserFontHeight;

			/* Create the new gadget. */

		if(Gadget = NewObject(StatusGadgetClass,NULL,
			GA_ID,			ID,
			GA_Left,		0,
			GA_Width,		Width,
			GA_Height,		Height,
			GA_RelBottom,		-Height + 1,
			GA_BottomBorder,	TRUE,
			GA_Highlight,		GFLG_GADGHNONE,

			SGA_Mode,		Mode,
		TAG_DONE))
			return(Gadget);
		else
			FreeStatusGadgetClass();
	}

	return(NULL);
}

	/* DoStatusInfo(APTR Object,UBYTE Mode,UBYTE Type,STRPTR String):
	 *
	 *	Display information in the status line area.
	 */

STATIC VOID __regargs
DoStatusInfo(APTR Object,UBYTE Mode,UBYTE Type,STRPTR String)
{
		/* What mode of operation is the status area in? */

	switch(Mode)
	{
			/* Uses the status gadget class. */

		case MODE_WB_NORMAL:
		case MODE_WB_COMPRESSED:

			SetGadgetAttrs(Object,Window,NULL,
				SG_Status + Type,String,
			TAG_DONE);

			break;

			/* Compressed mode. */

		case MODE_SCREEN_COMPRESSED:
		{
			struct RastPort *RPort = Object;

			STATIC BYTE Offsets[8] =
			{
				 0,
				-1,	/* Not supported */
				26,
				11,
				40,
				53,
				61,
				72
			};

			STATIC UBYTE Strings[8][20];
			UBYTE LineBuffer[90];
			LONG i,j,k,Width;

			strcpy(Strings[Type],String);

			strcpy(LineBuffer,"         ·              ·             ·            ·       ·          ·         ");

			for(i = 0 ; i < 8 ; i++)
			{
				if(Offsets[i] >= 0)
				{
					j = strlen(Strings[i]);

					for(k = 0 ; k < j ; k++)
						LineBuffer[Offsets[i] + k] = Strings[i][k];
				}
			}

			Width = TextLength(RPort,LineBuffer,80);

			if(StatusLineWidth && StatusLineWidth != Width)
				SetRast(RPort,DrawInfo -> dri_Pens[TEXTPEN]);

			StatusLineWidth = Width;

			if(SharedScreen || DefaultPubScreen)
				Move(RPort,(StatusWindow -> GZZWidth - Width) / 2 + 1,1 + UserFontBase);
			else
				Move(RPort,(StatusWindow -> Width - Width) / 2,UserFontBase);

			Text(RPort,LineBuffer,80);
		}

		break;

			/* Normal mode. */

		case MODE_SCREEN_NORMAL:
		{
			STATIC UBYTE Codes[8][2] =
			{
				STATUSBOX_STATUS_FONT,		0,
				STATUSBOX_STATUS_FONT,		1,

				STATUSBOX_PROTOCOL_TERMINAL,	0,
				STATUSBOX_PROTOCOL_TERMINAL,	1,

				STATUSBOX_RATE_PARAMETERS,	0,
				STATUSBOX_RATE_PARAMETERS,	1,

				STATUSBOX_TIME_ONLINE,		0,
				STATUSBOX_TIME_ONLINE,		1
			};

			struct ObjectCarrier *Carrier = (struct ObjectCarrier *)Object;

			SZ_PrintLine(Carrier -> RPort,Carrier -> BoxArray[Codes[Type][0]],Codes[Type][1],String);
		}

		break;
	}
}

	/* DoInfo(APTR Object,UBYTE Mode,UBYTE Type,STRPTR String):
	 *
	 *	Post an update message to the status display server.
	 */

STATIC VOID __regargs
DoInfo(APTR Object,UBYTE Mode,UBYTE Type,STRPTR String)
{
	struct UpdateMessage	*Msg;
	WORD			 Len = strlen(String) + 1;

		/* Allocate enough space to hold both the string
		 * and the message.
		 */

	if(Msg = (struct UpdateMessage *)AllocVecPooled(sizeof(struct UpdateMessage) + Len,MEMF_ANY | MEMF_PUBLIC | MEMF_CLEAR))
	{
			/* Fill in the message head. */

		Msg -> VanillaMessage . mn_Length = sizeof(struct UpdateMessage) + Len;

			/* Set up the name pointer. */

		Msg -> VanillaMessage . mn_Node . ln_Name = (STRPTR)(Msg + 1);

			/* Copy the string. */

		strcpy(Msg -> VanillaMessage . mn_Node . ln_Name,String);

			/* Fill in the remaining data. */

		Msg -> Object	= Object;
		Msg -> Mode	= Mode;
		Msg -> Type	= Type;

			/* Post the message. */

		PutMsg(StatusDisplayPort,(struct Message *)Msg);
	}
}

	/* UpdateInfo(APTR Object,UBYTE Mode,UBYTE Type,...):
	 *
	 *	Update the information displayed in the status
	 *	area.
	 */

STATIC VOID __stdargs
UpdateInfo(APTR Object,UBYTE Mode,UBYTE Type,...)
{
	if(Object)
	{
		UBYTE	 MiniBuffer[50];
		STRPTR	*String;
		LONG	*Numeral;
		va_list	 VarArgs;

		va_start(VarArgs,Type);

		String	= (STRPTR *)VarArgs;
		Numeral	= (LONG *)VarArgs;

		switch(Type)
		{
			case INFO_STATUS:

				strcpy(MiniBuffer,ConfigStatus[Numeral[0]]);
				strcat(MiniBuffer,"         ");

				MiniBuffer[9] = 0;

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;

			case INFO_BUFFER:

				if(Mode == MODE_SCREEN_NORMAL || Mode == MODE_WB_NORMAL)
					DoInfo(Object,Mode,Type,ConfigBufferState[Numeral[0]]);

				break;

			case INFO_ONLINECOST:

				strcpy(MiniBuffer,String[0]);
				strcat(MiniBuffer,"          ");

				MiniBuffer[8] = 0;

				DoInfo(Object,Mode,INFO_ONLINETIME,MiniBuffer);

				break;

			case INFO_CURRENTTIME:

				FormatTime(MiniBuffer,Numeral[0],Numeral[1],Numeral[2]);

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;

			case INFO_ONLINETIME:

				SPrintf(MiniBuffer,"%02ld:%02ld:%02ld",Numeral[0],Numeral[1],Numeral[2]);

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;

			case INFO_BAUDRATE:

				if(LocaleBase)
					SPrintf(MiniBuffer,"%lD        ",Numeral[0]);
				else
					SPrintf(MiniBuffer,"%ld        ",Numeral[0]);

				MiniBuffer[7] = 0;

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;

			case INFO_PROTOCOL:
			case INFO_EMULATION:

				strcpy(MiniBuffer,String[0]);

				strcat(MiniBuffer,"           ");

				MiniBuffer[12] = 0;

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;

			case INFO_PARAMETERS:

				if(Mode == MODE_SCREEN_COMPRESSED || Mode == MODE_WB_COMPRESSED)
				{
					STATIC UBYTE Parities[5] =
					{
						'N','E','O','M','S'
					};

					SPrintf(MiniBuffer,"%ld-%lc-%ld",Numeral[0],Parities[Numeral[1]],Numeral[2]);
				}
				else
					SPrintf(MiniBuffer,"%ld-%s-%ld",Numeral[0],ConfigParity[Numeral[1]],Numeral[2]);

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;
		}

		va_end(VarArgs);
	}
}

	/* Raise(UWORD Colour):
	 *
	 *	Make an RGB value brighter.
	 */

STATIC UWORD __inline
Raise(UWORD Colour)
{
	UWORD R,G,B;

	R =  (Colour >> 8)        + 4;
	G = ((Colour >> 4) & 0xF) + 4;
	B = ((Colour     ) & 0xF) + 4;

	if(R > 15)
		R = 15;

	if(G > 15)
		G = 15;

	if(B > 15)
		B = 15;

	return((UWORD)(R << 8 | G << 4 | B));
}

	/* VisualBeep():
	 *
	 *	Handle the visual part of the display beep.
	 */

STATIC BYTE
VisualBeep(VOID)
{
	struct UCopList	*UserCopperList;

		/* Create a user copper list. */

	if(UserCopperList = (struct UCopList *)AllocMem(sizeof(struct UCopList),MEMF_ANY|MEMF_CLEAR))
	{
			/* Initialize for 35 commands. */

		if(UCopperListInit(UserCopperList,1 + 16 + 1 + 16 + 1))
		{
			WORD i;

				/* Wait until first line of window. */

			CWAIT(UserCopperList,Window -> TopEdge,0);

				/* Set the light colours. */

			for(i = 0 ; i < 16 ; i++)
				CMOVE(UserCopperList,custom . color[i],Raise(GetRGB4(Screen -> ViewPort . ColorMap,i)));

				/* Wait until bottom of window. */

			CWAIT(UserCopperList,Window -> TopEdge + Window -> Height - 1,0);

				/* Set the standard colours. */

			for(i = 0 ; i < 16 ; i++)
				CMOVE(UserCopperList,custom . color[i],GetRGB4(Screen -> ViewPort . ColorMap,i));

				/* Finish list. */

			CEND(UserCopperList);

				/* Install user copper list... */

			Screen -> ViewPort . UCopIns = UserCopperList;

				/* ...and display it. */

			RethinkDisplay();

			return(TRUE);
		}
		else
			FreeMem(UserCopperList,sizeof(struct UCopList));
	}

	return(FALSE);
}

	/* StatusDisplayServer(VOID):
	 *
	 *	Yet another asynchronous background task to display
	 *	some information.
	 */

STATIC VOID __saveds
StatusDisplayServer(VOID)
{
		/* Create the interface port. */

	if(StatusDisplayPort = CreateMsgPort())
	{
		struct UpdateMessage	*Msg;
		ULONG			 Signals;

			/* Ring back... */

		Signal(StatusProcess,SIG_HANDSHAKE);

			/* Wait for messages or termination signal. */

		FOREVER
		{
			Signals = Wait(SIG_KILL | PORTMASK(StatusDisplayPort));

				/* Termination? */

			if(Signals & SIG_KILL)
				break;

				/* Message arrival? */

			if(Signals & PORTMASK(StatusDisplayPort))
			{
					/* Process all pending messages. */

				while(Msg = (struct UpdateMessage *)GetMsg(StatusDisplayPort))
				{
					DoStatusInfo(Msg -> Object,Msg -> Mode,Msg -> Type,Msg -> VanillaMessage . mn_Node . ln_Name);

					FreeVecPooled(Msg);
				}
			}
		}

			/* Remove all pending messages. */

		while(Msg = (struct UpdateMessage *)GetMsg(StatusDisplayPort))
			FreeVecPooled(Msg);

			/* Remove the msgport. */

		DeleteMsgPort(StatusDisplayPort);
	}

		/* Lock & quit... */

	Forbid();

	Signal(StatusProcess,SIG_HANDSHAKE);

	RemTask(StatusDisplayTask = NULL);
}

	/* StatusServer():
	 *
	 *	Asynchronous process to continuosly display the current
	 *	terminal settings.
	 */

VOID __saveds
StatusServer()
{
	STATIC struct timeval	 OnlineTime,
				 LastTime,
				 TempTime;
	STATIC BYTE		 GotOnline		= FALSE,
				 WasOnline		= FALSE,
				 ShowPay		= FALSE,
				 FlagBit		= FALSE;
	STATIC LONG		 SecCount		= 0,
				 MinCount		= 0,
				 BeepCount		= 0;

	struct TextBox		*BoxArray[4],
				*BoxList = NULL,
				*Box;

	struct RastPort		*RPort;

	APTR			 SomeObject;
	struct ObjectCarrier	 Carrier;

	struct timerequest	*StatusTimeRequest;
	struct MsgPort		*StatusTimePort;

	BYTE			 Background		= FALSE,
				 FlashIt		= FALSE,
				 SetColours		= FALSE,
				 StandardColours	= TRUE,
				 KeepGoing		= TRUE,
				 Beeping		= FALSE,
				 StatusMode		= Config -> ScreenConfig -> StatusLine;

	BYTE			 LastProtocol[40],
				 ProtocolBuffer[40],
				 LastEmulationName[40];

	BYTE			 LastFrozen		= -1,
				 LastEmulation		= -1,
				 LastBitsPerChar	= -1,
				 LastParity		= -1,
				 LastStopBits		= -1,
				 LastStatus		= -1;

	LONG			 LastBaud		= -1;

	LONG			 i,
				 ThisHour,
				 ThisMinute,
				 BoxCounter = 0,
				 FullWidth;
	WORD			 ColumnLeft[4],
				 ColumnWidth[4],
				 Max,
				 Len;
	BYTE			 AllFine = TRUE;
	UBYTE			 Mode;

	StatusLineWidth = 0;

	LastProtocol[0] = 0;

	LastEmulationName[0] = 0;

	LocalizeString(ConfigBufferState,MSG_TERMSTATUSDISPLAY_FROZEN_TXT,MSG_TERMSTATUSDISPLAY_RECORDING_TXT);
	LocalizeString(ConfigEmulation,MSG_TERMAUX_ANSI_VT102_TXT,MSG_TERMAUX_HEX_TXT);
	LocalizeString(ConfigParity,MSG_TERMAUX_NONE_TXT,MSG_TERMAUX_SPACE_TXT);
	LocalizeString(ConfigStatus,MSG_TERMAUX_READY_TXT,MSG_TERMAUX_RECORD_LINE_TXT);

	if(StatusWindow)
	{
		RPort = StatusWindow -> RPort;

		SetRast(RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);

			/* Render the information. */

		SZ_SizeSetup(StatusWindow -> WScreen,&UserFont);

		if(StatusMode == STATUSLINE_COMPRESSED)
		{
			StatusOffset = (StatusWindow -> Width - StatusWindow -> BorderLeft - StatusWindow -> BorderRight - 80 * UserFontWidth) / 2;

			SetAPen(RPort,DrawInfo -> dri_Pens[TEXTPEN]);

			RectFill(RPort,0,0,StatusWindow -> Width - 1,StatusWindow -> Height - 1);

			SetDrMd(RPort,JAM2);
			SetAPen(RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);
			SetBPen(RPort,DrawInfo -> dri_Pens[TEXTPEN]);

			SetFont(RPort,UserTextFont);
		}
		else
		{
			SetBPen(RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);

				/* Draw a separating line. */

			if(!Config -> ScreenConfig -> SplitStatus)
			{
				SetAPen(RPort,DrawInfo -> dri_Pens[SHADOWPEN]);
				Move(RPort,0,0);
				Draw(RPort,StatusWindow -> Width - 1,0);

				SetAPen(RPort,DrawInfo -> dri_Pens[SHINEPEN]);
				Move(RPort,0,1);
				Draw(RPort,StatusWindow -> Width - 1,1);
			}

			SetAPen(RPort,DrawInfo -> dri_Pens[TEXTPEN]);

			ColumnLeft[0] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_STATUS_TXT,MSG_TERMSTATUSDISPLAY_FONT_TXT,-1);
			ColumnLeft[1] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_PROTOCOL_TXT,MSG_TERMSTATUSDISPLAY_TERMINAL_TXT,-1);
			ColumnLeft[2] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_BAUDRATE_TXT,MSG_TERMSTATUSDISPLAY_PARAMETERS_TXT,-1);
			ColumnLeft[3] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_TIME_TXT,MSG_TERMSTATUSDISPLAY_ONLINE_TXT,-1);

			Max = 0;

			for(i = 0 ; ConfigStatus[i] ; i++)
			{
				if((Len = SZ_BoxWidth(strlen(ConfigStatus[i]))) > Max)
					Max = Len;
			}

			for(i = 0 ; ConfigBufferState[i] ; i++)
			{
				if((Len = SZ_BoxWidth(strlen(ConfigBufferState[i]))) > Max)
					Max = Len;
			}

			ColumnWidth[0] = Max;

			Max = SZ_BoxWidth(12);

			for(i = 0 ; ConfigEmulation[i] ; i++)
			{
				if((Len = SZ_BoxWidth(strlen(ConfigEmulation[i]))) > Max)
					Max = Len;
			}

			ColumnWidth[1] = Max;

			Max = SZ_BoxWidth(10);

			for(i = 0 ; ConfigParity[i] ; i++)
			{
				if((Len = SZ_BoxWidth(4 + strlen(ConfigParity[i]))) > Max)
					Max = Len;
			}

			ColumnWidth[2] = Max;

			ColumnWidth[3] = SZ_BoxWidth(8);

			FullWidth = 0;

			for(i = 0 ; i < 4 ; i++)
				FullWidth += ColumnWidth[i] + ColumnLeft[i];

			FullWidth += 3 * InterWidth;

			if(!Config -> ScreenConfig -> SplitStatus)
			{
				if(FullWidth > Screen -> Width)
					SZ_SetLeftEdge(ColumnLeft[0]);
				else
					SZ_SetLeftEdge((Screen -> Width - FullWidth) / 2 + ColumnLeft[0]);

				SZ_SetAbsoluteTop(3);
				SZ_SetTopEdge(3);
			}
			else
			{
				SZ_SetLeftEdge(ColumnLeft[0] + 1);

				SZ_SetAbsoluteTop(1);
				SZ_SetTopEdge(1);
			}

			SZ_SetWidth(ColumnWidth[0]);

			BoxArray[BoxCounter++] = Box = SZ_CreateTextBox(&BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_STATUS_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_FONT_TXT),NULL);

			SZ_SetWidth(ColumnWidth[1]);
			SZ_AddLeftOffset(ColumnLeft[1]);

			BoxArray[BoxCounter++] = Box = SZ_CreateTextBox(&BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
				SZ_NewColumn,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_PROTOCOL_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_TERMINAL_TXT),NULL);

			SZ_SetWidth(ColumnWidth[2]);
			SZ_AddLeftOffset(ColumnLeft[2]);

			BoxArray[BoxCounter++] = Box = SZ_CreateTextBox(&BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
				SZ_NewColumn,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_BAUDRATE_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_PARAMETERS_TXT),NULL);

			SZ_SetWidth(ColumnWidth[3]);
			SZ_AddLeftOffset(ColumnLeft[3]);

			BoxArray[BoxCounter] = Box = SZ_CreateTextBox(&BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
				SZ_NewColumn,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_TIME_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_ONLINE_TXT),NULL);

			if(!Box)
				AllFine = FALSE;
			else
				SZ_DrawBoxes(RPort,BoxList);
		}
	}
	else
	{
		if((Config -> ScreenConfig -> UseWorkbench || SharedScreen) && StatusGadget)
			AllFine = TRUE;
	}

		/* Everything fine so far? */

	if(AllFine)
	{
		Forbid();

			/* Create the display server task. */

		if(StatusDisplayTask = CreateTask("term Status Display Task",5,StatusDisplayServer,4000))
		{
			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);
		}

		Permit();
	}

		/* Is the display server task up and running? */

	if(StatusDisplayTask)
	{
			/* Create a timer device request. */

		if(StatusTimePort = (struct MsgPort *)CreateMsgPort())
		{
			if(StatusTimeRequest = (struct timerequest *)CreateIORequest(StatusTimePort,sizeof(struct timerequest)))
			{
				if(!OpenDevice(TIMERNAME,UNIT_VBLANK,StatusTimeRequest,0))
				{
						/* Signal our father process
						 * that we're running.
						 */

					Signal(ThisProcess,SIG_HANDSHAKE);

					if(StatusWindow)
					{
						if(StatusMode == STATUSLINE_COMPRESSED)
						{
							Mode = MODE_SCREEN_COMPRESSED;

							SomeObject = RPort;
						}
						else
						{
							Mode = MODE_SCREEN_NORMAL;

							Carrier . RPort		= RPort;
							Carrier . BoxArray	= BoxArray;

							SomeObject = &Carrier;
						}

						UpdateInfo(SomeObject,Mode,INFO_ONLINETIME,0,0,0);
					}
					else
					{
						if(StatusGadget)
						{
							SomeObject = StatusGadget;

							if(StatusMode == STATUSLINE_COMPRESSED)
								Mode = MODE_WB_COMPRESSED;
							else
								Mode = MODE_WB_NORMAL;

							UpdateInfo(SomeObject,Mode,INFO_ONLINETIME,0,0,0);
						}
						else
							SomeObject = NULL;
					}

						/* Keep on displaying. */

					while(KeepGoing)
					{
							/* Are we to quit? */

						if(CheckSignal(SIG_KILL))
							KeepGoing = FALSE;

							/* Get the current time. */

						StatusTimeRequest -> tr_node . io_Command = TR_GETSYSTIME;

						DoIO(StatusTimeRequest);

							/* A connection has just
							 * been established.
							 */

						if(Online && !GotOnline)
						{
							OnlineTime = StatusTimeRequest -> tr_time;

								/* Add up connection time. */

							if(OnlinePlus)
							{
								struct timeval GetBack;

								GetBack . tv_secs	= (OnlinePlus / 6) * 60 + (OnlinePlus % 6) * 10;
								GetBack . tv_micro	= 0;

								OnlinePlus = 0;

								SubTime(&OnlineTime,&GetBack);
							}

							GotOnline = TRUE;

							SecCount = 0;

							FlagBit = FALSE;
						}

							/* Print the current time. */

						ThisHour	= (StatusTimeRequest -> tr_time . tv_secs % 86400) / 3600;
						ThisMinute	= (StatusTimeRequest -> tr_time . tv_secs % 3600) / 60;

						UpdateInfo(SomeObject,Mode,INFO_CURRENTTIME,ThisHour,ThisMinute,StatusTimeRequest -> tr_time . tv_secs % 60);

							/* Handle routine checkup actions. */

						if(!(SecCount & 1))
							Signal(ThisProcess,SIG_CHECK);

						if(Online)
						{
							WasOnline = TRUE;

							if(ChosenEntry)
							{
								if(!(ThisMinute % 10) && !(StatusTimeRequest -> tr_time . tv_secs % 60))
								{
									ChosenInUse = TRUE;

									SelectTime(ChosenEntry);

									ChosenInUse = FALSE;
								}

								if(!CurrentPay)
									CurrentPay = PayPerUnit[DT_FIRST_UNIT];

								FlagBit ^= TRUE;

								if(!FlagBit)
								{
									if(SecPerUnit[WhichUnit] && SecCount == SecPerUnit[WhichUnit])
									{
										SecCount = 0;

										WhichUnit = DT_NEXT_UNIT;

										CurrentPay += PayPerUnit[DT_NEXT_UNIT];
									}

									SecCount++;
								}
							}

								/* Show the time
								 * we have been online
								 * yet.
								 */

							TempTime = StatusTimeRequest -> tr_time;

							SubTime(&TempTime,&OnlineTime);

							if(StatusTimeRequest -> tr_time . tv_secs != LastTime . tv_secs)
							{
								LastTime = StatusTimeRequest -> tr_time;

								switch(Config -> ScreenConfig -> TimeMode)
								{
									case ONLINETIME_TIME:	ShowPay = FALSE;
												break;

									case ONLINETIME_COST:	ShowPay = TRUE;
												break;

									case ONLINETIME_BOTH:	if(TempTime . tv_secs && !(TempTime . tv_secs % 5))
													ShowPay ^= TRUE;

												break;
								}

								if(ShowPay && ChosenEntry)
									UpdateInfo(SomeObject,Mode,INFO_ONLINECOST,CreateSum(CurrentPay,FALSE));
								else
									UpdateInfo(SomeObject,Mode,INFO_ONLINETIME,(TempTime . tv_secs % 86400) / 3600,(TempTime . tv_secs % 3600) / 60,TempTime . tv_secs % 60);

								OnlineMinutes = TempTime . tv_secs / 60;
							}
						}
						else
						{
							if(WasOnline)
							{
								WasOnline = FALSE;

								UpdateInfo(SomeObject,Mode,INFO_ONLINETIME,(TempTime . tv_secs % 86400) / 3600,(TempTime . tv_secs % 3600) / 60,TempTime . tv_secs % 60);
							}

							if(GotOnline)
								GotOnline = FALSE;
						}

							/* Take care of the visual beep
							 * if enabled.
							 */

						if(Beeping)
						{
							if(!(BeepCount--))
							{
								Beeping = FALSE;

									/* Remove the copper list. */

								FreeVPortCopLists(&Screen -> ViewPort);

									/* Really remove it. */

								RemakeDisplay();

									/* Clear the signal bit. */

								ClrSignal(SIG_BELL);
							}
						}

							/* Are we to show a visual beep? */

						if(CheckSignal(SIG_BELL))
						{
							if(Config -> TerminalConfig -> BellMode == BELL_SYSTEM)
								DisplayBeep(Window -> WScreen);
							else
							{
								if(Screen && !Config -> ScreenConfig -> UseWorkbench && !Beeping && (Config -> TerminalConfig -> BellMode == BELL_VISIBLE || Config -> TerminalConfig -> BellMode == BELL_BOTH))
								{
									if(VisualBeep())
									{
										Beeping = TRUE;

										BeepCount = 1;
									}
								}
							}
						}

							/* Display the current terminal
							 * status.
							 */

						if(LastStatus != Status)
							UpdateInfo(SomeObject,Mode,INFO_STATUS,LastStatus = Status);

							/* Show the current transfer
							 * protocol.
							 */

						if(strcmp(LastProtocol,FilePart(LastXprLibrary)))
						{
							strcpy(LastProtocol,FilePart(LastXprLibrary));

							if((Len = strlen(LastProtocol)) > 3)
							{
								strcpy(ProtocolBuffer,&LastProtocol[3]);

								for(i = 0 ; i < Len - 3 ; i++)
								{
									if(ProtocolBuffer[i] == '.')
									{
										ProtocolBuffer[i] = 0;

										break;
									}
								}

								ProtocolBuffer[0] = ToUpper(ProtocolBuffer[0]);

								UpdateInfo(SomeObject,Mode,INFO_PROTOCOL,ProtocolBuffer);
							}
						}

							/* Show the current baud
							 * rate.
							 */

						if(LastBaud != Config -> SerialConfig -> BaudRate)
							UpdateInfo(SomeObject,Mode,INFO_BAUDRATE,LastBaud = Config -> SerialConfig -> BaudRate);

							/* Show the current
							 * terminal font.
							 */

						if(LastFrozen != BufferFrozen)
						{
							LastFrozen = BufferFrozen;

							UpdateInfo(SomeObject,Mode,INFO_BUFFER,LastFrozen != TRUE);
						}

							/* Show the current terminal
							 * emulation.
							 */

						if(LastEmulation != Config -> TerminalConfig -> EmulationMode || (Config -> TerminalConfig -> EmulationMode == EMULATION_EXTERNAL && Stricmp(EmulationName,LastEmulationName)))
						{
							LastEmulation = Config -> TerminalConfig -> EmulationMode;

							if(Config -> TerminalConfig -> EmulationMode == EMULATION_EXTERNAL)
							{
								UpdateInfo(SomeObject,Mode,INFO_EMULATION,EmulationName);

								strcpy(LastEmulationName,EmulationName);
							}
							else
								UpdateInfo(SomeObject,Mode,INFO_EMULATION,ConfigEmulation[LastEmulation]);
						}

							/* Show the current serial
							 * parameters (parity, etc).
							 */

						if(LastBitsPerChar != Config -> SerialConfig -> BitsPerChar || LastParity != Config -> SerialConfig -> Parity || LastStopBits != Config -> SerialConfig -> StopBits)
						{
							LastBitsPerChar	= Config -> SerialConfig -> BitsPerChar;
							LastParity	= Config -> SerialConfig -> Parity;
							LastStopBits	= Config -> SerialConfig -> StopBits;

							UpdateInfo(SomeObject,Mode,INFO_PARAMETERS,LastBitsPerChar,LastParity,LastStopBits);
						}

							/* Wait another half a second. */

						if(KeepGoing)
						{
							ULONG	Mask;
							BOOLEAN	ResetTime = FALSE;

							StatusTimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
							StatusTimeRequest -> tr_time . tv_secs		= 0;
							StatusTimeRequest -> tr_time . tv_micro		= MILLION / 2;

							SendIO(StatusTimeRequest);

							FOREVER
							{
								Mask = Wait(SIG_BELL | PORTMASK(StatusTimePort) | SIG_CLOSEWINDOW | SIG_RESETTIME);

									/* Close the window and keep quiet? */

								if(Mask & SIG_CLOSEWINDOW)
								{
									Forbid();

									if(StatusDisplayTask)
									{
										Signal(StatusDisplayTask,SIG_KILL);

										ClrSignal(SIG_HANDSHAKE);

										Wait(SIG_HANDSHAKE);
									}

									if(BoxList)
									{
										SZ_FreeBoxes(BoxList);

										BoxList = NULL;
									}

									SomeObject = NULL;

									Signal(ThisProcess,SIG_HANDSHAKE);

									Config -> ScreenConfig -> StatusLine = STATUSLINE_DISABLED;

									Permit();
								}

								if(Mask & SIG_BELL)
								{
									if(Config -> TerminalConfig -> BellMode == BELL_SYSTEM)
										DisplayBeep(Window -> WScreen);
									else
									{
										if(Screen && !Config -> ScreenConfig -> UseWorkbench && !Beeping && (Config -> TerminalConfig -> BellMode == BELL_VISIBLE || Config -> TerminalConfig -> BellMode == BELL_BOTH))
										{
											if(VisualBeep())
											{
												Beeping = TRUE;

												BeepCount = 1;
											}
										}
									}
								}

									/* Reset the online time counter. */

								if(Mask & SIG_RESETTIME)
									ResetTime = TRUE;

								if(Mask & PORTMASK(StatusTimePort))
								{
									WaitIO(StatusTimeRequest);

									break;
								}
							}

								/* Reset the online time. */

							if(ResetTime)
							{
								StatusTimeRequest -> tr_node . io_Command = TR_GETSYSTIME;

								DoIO(StatusTimeRequest);

								OnlineTime = StatusTimeRequest -> tr_time;
							}

								/* Count up to a minute. */

							if(MinCount++ == 120)
							{
								MinCount = 0;

									/* Time limit present? */

								if(LimitCount > 0)
									LimitCount--;

									/* Limit reached? */

								if(!LimitCount)
									Signal(ThisProcess,SIG_CHECK);
							}
						}

							/* Make the colours blink. */

						if(Screen && !Config -> ScreenConfig -> UseWorkbench)
						{
							if(Screen == IntuitionBase -> FirstScreen)
							{
									/* No main screen window active? */

								if(StatusWindow)
								{
									if(!(Window -> Flags & WFLG_WINDOWACTIVE) && !(StatusWindow -> Flags & WFLG_WINDOWACTIVE))
										StandardColours = TRUE;
								}
								else
								{
									if(!(Window -> Flags & WFLG_WINDOWACTIVE))
										StandardColours = TRUE;
								}

									/* Menu button pressed or window disabled? */

								if(Window -> Flags & (WFLG_MENUSTATE | WFLG_INREQUEST))
									StandardColours = TRUE;

									/* User is currently dragging the
									 * mouse in order to mark something
									 * on the screen?
									 */

								if(Marking)
									StandardColours = TRUE;

								Background = FALSE;
							}
							else
							{
								if(!Background)
									StandardColours = TRUE;

								Background = TRUE;
							}

							if(StandardColours)
							{
								if(!SetColours)
								{
									LoadRGB4(VPort,NormalColours,PaletteSize);

									SetColours = TRUE;
								}

								StandardColours = FlashIt = FALSE;
							}
							else
							{
									/* Are we to flash the display? */

								if(Config -> ScreenConfig -> Blinking)
								{
									if(Screen == IntuitionBase -> FirstScreen)
									{
										if(FlashIt)
										{
											LoadRGB4(VPort,BlinkColours,PaletteSize);

											SetColours = FALSE;
										}
										else
										{
											LoadRGB4(VPort,NormalColours,PaletteSize);

											SetColours = TRUE;
										}
									}

									FlashIt ^= TRUE;
								}
							}
						}
					}

					CloseDevice(StatusTimeRequest);
				}

				DeleteIORequest(StatusTimeRequest);
			}

			DeleteMsgPort(StatusTimePort);
		}

		if(StatusDisplayTask)
		{
			Forbid();

			Signal(StatusDisplayTask,SIG_KILL);

			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);

			Permit();
		}

		if(BoxList)
			SZ_FreeBoxes(BoxList);

		if(CheckSignal(SIG_CLOSEWINDOW))
			Signal(ThisProcess,SIG_HANDSHAKE);
	}

		/* Signal the father process that we're done
		 * and quietly remove ourselves.
		 */

	Forbid();

	Signal(ThisProcess,SIG_HANDSHAKE);

	StatusProcess = NULL;
}
