/**************************************************************************
screen.c	-	Collection of generic routines for driving the
			VDU screen in text mode.

Programmer      -       Edward James Pugh, FIAP.

Copyright	-	(C) Edward James Pugh, 12-Jan-1990.
***************************************************************************/
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "ibmpc.h"

/* declare public globals */

Window_t                *CURWINDOW = NULL;
Window_t                *ACTWINDOW = NULL;
Event_t                 EVENT;
char                    DATE_SEPARATOR = '-'; /* default */

int			TABSTOPS = 4; /* default */

extern int              MOUSE_SUPPORTED; /* in ibmpc.c */
extern Screen_t         HOST_SCREEN; /* in ibmpc.c */
extern unsigned char    SW_BUTTON; /* in ibmpc.c */
extern unsigned char    CW_ICON; /* in ibmpc.c */

static llstack_t		SCREENSTACK = { 0, NULL};
static llstack_t        MENUSTACK = { 0, NULL};
static llstack_t        TEMPSTACK = { 0, NULL};

/* internal for use with clipping routines */
static int code1, code2, code3, code4;

/* useful macros */
#define MALLOC(x) ((x*)malloc(sizeof(x)))
#define BIT1 1 /* for clipping routines */
#define BIT2 2 /* for clipping routines */
#define BIT3 4 /* for clipping routines */
#define BIT4 8 /* for clipping routines */
#define NOTWITHIN(code1, code2) (code1 & code2)
#define BOTHIN(code1, code2) (!(code1 | code2))
#define ROW ((unsigned char)row)
#define COL ((unsigned char)col)

/* structure declarations */
extern Border_t S_BORDER, D_BORDER; /* in ibmpc.c */

/* function prototypes */
static void KillWObjects(Window_t *window);
static void SetMinBounds(Screen_t *master, Screen_t *child);
static char PutOString(WObj_s type, char *string, BOOLEAN active);
static void ShowWObj(Window_t *window);
static Window_t *ShowWindow(Window_t *window, BOOLEAN active);
static void DoWBorder(void);
static char POString(char *string, BOOLEAN active, unsigned char attrib);
static llnode_t *AddWObj(Window_t *twindow, Window_t *object);
static int PointsInside(void);
static void SetDCodes(Screen_t *bounds1, Screen_t *bounds2);
static int WindowClash(Window_t *window1, Window_t *window2);
static BOOLEAN CheckStack(llstack_t *stack, Window_t *window);
static Window_t *WNotVisible(Window_t *window);
static void CloseW(Window_t *window);
static Window_t *PopW(Window_t *target);
static BOOLEAN FindWObj(Window_t *window, char hkey);
static unsigned int FindMWObj(Window_t *window, unsigned char row,
		unsigned char col);
static Window_t* AddPList(Window_t *window, Option_t *options,
		unsigned char TRow, unsigned char TCol);
static unsigned char AddMTitle(Window_t *window, Menu_t *menu,
		unsigned char TRow, unsigned char TCol);
static unsigned *SaveWArea(Window_t *window);
static Window_t *PushWindow(Window_t *window);
static unsigned *PutWArea(Window_t *window, unsigned *area);
static void FlipBorder(Window_t *window);

/**************************************************************************
ResCurs 	-	Restore Cursor.

Description	-	Restores the previously saved cursor position.

Parameters	-	ResCurs(cursposn)
			const Curs_t *cursposn; pointer to structure
			containing details for restoring.

Returns 	-	NOTHING.
***************************************************************************/
void	ResCurs(const Curs_t *cursposn)
{
PosnCurs(cursposn->Row, cursposn->Col);
}

/**************************************************************************
SaveCurs	-	Save Cursor.

Description	-	Saves a cursors position.

Parameters	-	SaveCurs(cursposn)
			Curs_t *cursposn; pointer to structure in which
				positions will be saved.

Returns 	-	NOTHING.
**************************************************************************/
void	SaveCurs(Curs_t *cursposn)
{
unsigned char row, col;

GetCurs(&row, &col);
cursposn->Row = row;
cursposn->Col = col;
}

/**************************************************************************
NewWindow	-	New Window

Description	-	Creates a new Window_t structure and assigns
			text screen co-ordinates.  Also calculates and
			assigns values to Width and Height of window
			parameter.

Parameters      -       NewWindow(Type,Title,TL,TC,BL,BC,Attrib,Border)
			Wind_s Type; of window
				SCREEN_W, MENU_BAR_W, OBJECT_W, POP_UP_W
			char *Title;
			unsigned char TL, TC, BL, BC; line/col coord
			unsigned char Attrib; window attribute
			Border_t *Border;

Returns 	-	A pointer to the new Window_t structure or NULL
			if an error.
**************************************************************************/
Window_t *NewWindow(Wind_s Type, char *Title, unsigned char TLine,
		unsigned char TCol, unsigned char BLine, unsigned char BCol,
		unsigned char Attrib, Border_t *Border)
{
Window_t *window;

if(window = (Window_t *)malloc(sizeof(Window_t)))
	{
	window->Border = Border;
	window->Area = NULL;
	window->Pane = NULL;
	window->Contents = NULL;
	window->Bounds.TRow = TLine;
	window->Bounds.TCol = TCol;
	window->Bounds.BRow = BLine;
	window->Bounds.BCol = BCol;
	window->MinBounds.TRow = 255;
	window->MinBounds.TCol = 255;
	window->MinBounds.BRow = 0;
	window->MinBounds.BCol = 0;
	window->Attrib = ValidAttrib(Attrib);
	window->ObjList = NULL;
	window->Type = Type;
	window->SNode = NULL;
	window->Posn = NULL;
	window->Parent = NULL;
	window->CType = EMPTY;
	window->Process = NULL;
	if(window->Border)
		{
		DecBounds(&window->Bounds);
		NameWindow(window, Title);
		}
	else
		{
		window->TOffset = 0;
		window->TLength = 0;
		window->HKey = '\0';
		}
	window->Changed = TRUE;
	window->CursPosn.Row = window->Bounds.TRow;
	window->CursPosn.Col = window->Bounds.TCol;
	}
return(window);
}

/**************************************************************************
NameWindow      -       Name Window

Description     -       Calculates the position that the title will
			occupy within the window's boundaries and
			assigns these values to the window's fields

			If NULL is passed as a pointer to the title
			string then the fields are just initialized.

Parameters      -       NameWindow(window, title)
			Window_t *window;
			char *title; pointer to title string

Returns         -       The title length, not including any @ character
**************************************************************************/
size_t NameWindow(Window_t *window, char *title)
{
window->Title = title;
if(title)
	{
	window->TLength = TStringWidth(title, &window->HKey);
	window->TOffset = (unsigned char)((window->Bounds.BCol -
		window->Bounds.TCol + 1 -
			window->TLength) / 2 + 1);
	}
else
	{
	window->TOffset = 0;
	window->TLength = 0;
	window->HKey = '\0';
	}
return(window->TLength);
}



/**************************************************************************
LabelWindow     -       Label Window

Description     -       Calculates the length of the title string and
			sets its offset, length and hkey fields as
			required for a Dialogue object (_D in WObj_s)

Parameters      -       LabelWindow(window, title)
			Window_t *window;
			char *title

Returns         -       The length of the title string, not including any
			'@' character
**************************************************************************/
size_t LabelWindow(Window_t *window, char *title)
{
window->Title = title;
if(title)
	{
	window->TLength = TStringWidth(title, &window->HKey);
	window->TOffset = window->Bounds.TCol;
	}
else
	{
	window->TOffset = 0;
	window->TLength = 0;
	window->HKey = '\0';
	}
return(window->TLength);
}

/*************************************************************************
IncDBounds      -       Increment Dialogue Boundaries

Description     -       Increments the supplied dialogue boundaries
			to include the label and border areas

Parameters      -       IncDBounds(window)
			Window_t *window;

Returns         -       NOTHING
**************************************************************************/
void IncDBounds(Window_t *window)
{
-- window->Bounds.TRow;
++ window->Bounds.BRow;
++ window->Bounds.BCol;
window->Bounds.TCol = window->TOffset;
}

/**************************************************************************
DecDBounds      -       Decrement Dialogue Boundaries

Description     -       Decrements the given dialogue boundaries
			in preparation for input

Parameters      -       DecDBounds(window)
			Window_t *window;

Returns         -       NOTHING
***************************************************************************/
void DecDBounds(Window_t *window)
{
++ window->Bounds.TRow;
-- window->Bounds.BRow;
-- window->Bounds.BCol;
window->Bounds.TCol += window->TLength;
++ window->Bounds.TCol;
}


/**************************************************************************
CursUp		-	Cursor Up.

Description	-	Moves cursor up one line.

Parameters	-	CursUp(void)

Returns 	-	TRUE if successful, else FALSE.
**************************************************************************/
BOOLEAN CursUp(void)
{
unsigned char row, col;

GetCurs(&row, &col);
if(row > CURWINDOW->Bounds.TRow)
	{
	PosnCurs((unsigned char)(row-1), col);
	return(TRUE);
	}
else
	return(FALSE);
}

/**************************************************************************
CursDn		-	Cursor Down.

Description	-	Moves cursor down one line.

Parameters	-	CursDn(void)

Returns 	-	TRUE is successful, else FALSE.
**************************************************************************/
BOOLEAN CursDn(void)
{
unsigned char row, col;

GetCurs(&row, &col);
if(row < CURWINDOW->Bounds.BRow)
	{
	PosnCurs((unsigned char)(row+1), col);
	return(TRUE);
	}
else
	return(FALSE);
}

/**************************************************************************
CursL		-	Cursor Left.

Description	-	Moves cursor left one column.

Parameters	-	CursL(void)

Returns 	-	TRUE if successful, else FALSE.
**************************************************************************/
BOOLEAN CursL(void)
{
unsigned char row, col;

GetCurs(&row, &col);
if(col > CURWINDOW->Bounds.TCol)
	{
	PosnCurs(row, (unsigned char)(col-1));
	return(TRUE);
	}
else
	return(FALSE);
}

/**************************************************************************
CursR		-	Cursor Right.

Description	-	Moves cursor right by one column.

Parameters	-	CursR(void)

Returns 	-	TRUE if successful, else FALSE.
**************************************************************************/
BOOLEAN CursR(void)
{
unsigned char row, col;

GetCurs(&row, &col);
if(col < CURWINDOW->Bounds.BCol)
	{
	PosnCurs(row, (unsigned char)(col+1));
	return(TRUE);
	}
else
	return(FALSE);
}

/**************************************************************************
CursRet 	-	Cursor Return

Description	-	Puts the cursor at the beginning of the next line

Parameters	-	CursRet(void)

Returns 	-	TRUE if successful else FALSE
**************************************************************************/
BOOLEAN CursRet(void)
{
unsigned char row, col;

GetCurs(&row, &col);
if(row < CURWINDOW->Bounds.BRow)
	{
	PosnCurs((unsigned char)(row+1), CURWINDOW->Bounds.TCol);
	return(TRUE);
	}
else
	return(FALSE);
}
/*************************************************************************
CursHome        -       Cursor Home

Description     -       Positions the cursor in the top left corner
			of the CURWINDOW

Parameters      -       CursHome()

Returns         -       TRUE or FALSE - if no CURWINDOW
*************************************************************************/
BOOLEAN CursHome(void)
{
if(CURWINDOW)
	{
	PosnCurs(CURWINDOW->Bounds.TRow, CURWINDOW->Bounds.TCol);
	return(TRUE);
	}
return(FALSE);
}

/*************************************************************************
ClearLine	-	Clear Line

Description	-	Clears a screen line

Parameters	-	ClearLine(void)

Returns 	-	NOTHING
**************************************************************************/
void ClearLine(void)
{
BegLine();
do
    PutChar(' ', CURWINDOW->Attrib);
while(CursR());
BegLine();
}

/**************************************************************************
BegLine 	-	Begin Line

Description	-	Moves cursor to beginning of line

Parameters	-	BegLine(void)

Returns 	-	NOTHING
***************************************************************************/
void BegLine(void)
{
while(CursL());
}

/**************************************************************************
EndLine 	-	End Line

Description	-	Moves Cursor to the End of the line

Parameters	-	EndLine(void)

Returns 	-	NOTHING
**************************************************************************/
void EndLine(void)
{
while(CursR());
}

/**************************************************************************
ClearWindow	-	Clear Window.

Description     -       Clears the CURWINDOW and positions the cursor at
			the top left position. Saves the CursPosn field
			and sets Changed to TRUE.

Parameters	-	ClearWindow()

Returns 	-	NOTHING.
**************************************************************************/
void ClearWindow(void)
{
ClearSection(CURWINDOW->Bounds.TRow, CURWINDOW->Bounds.TCol,
	CURWINDOW->Bounds.BRow, CURWINDOW->Bounds.BCol, CURWINDOW->Attrib);
PosnCurs(CURWINDOW->Bounds.TRow, CURWINDOW->Bounds.TCol);
SaveCurs(&CURWINDOW->CursPosn);
CURWINDOW->Changed = TRUE;
}

/**************************************************************************
DoWBorder	-	Do Window Border

Description     -       Puts a border around the window area and
			draws the title.

Parameters	-	DoWBorder(window)
			Window_t *window; pointer to window structure.

Returns 	-	NOTHING.
**************************************************************************/
static void DoWBorder(void)
{
IncBounds(&CURWINDOW->Bounds);
PosnCurs(CURWINDOW->Bounds.TRow, CURWINDOW->Bounds.TCol);
while(CursDn())
	PutChBright(CURWINDOW->Border->VT, CURWINDOW->Border->Attrib);
PutChBright(CURWINDOW->Border->BL, CURWINDOW->Border->Attrib);
while(CursR())
	PutChBright(CURWINDOW->Border->HZ, CURWINDOW->Border->Attrib);
PutChBright(CURWINDOW->Border->BR, CURWINDOW->Border->Attrib);
while(CursUp())
	PutChBright(CURWINDOW->Border->VT, CURWINDOW->Border->Attrib);
PutChBright(CURWINDOW->Border->TR, CURWINDOW->Border->Attrib);
while(CursL())
	PutChBright(CURWINDOW->Border->HZ, CURWINDOW->Border->Attrib);
PutChBright(CURWINDOW->Border->TL, CURWINDOW->Border->Attrib);
/* show close window icon */
PosnCurs(CURWINDOW->Bounds.TRow, CURWINDOW->Bounds.TCol);
if(MOUSE_SUPPORTED)
	{
	CursR();
	CursR();
	PutChBright(CW_ICON, CURWINDOW->Border->Attrib);
	}
if(CURWINDOW->Title)
	{
	char *string = CURWINDOW->Title;

	PosnCurs(CURWINDOW->Bounds.TRow, (unsigned char)(
		CURWINDOW->Bounds.TCol + CURWINDOW->TOffset));
	while(*string)
		{
		if(*string == '@')
			{
			string ++;
			PutChDull(*string, CURWINDOW->Border->Attrib);
			}
		else
			PutChBright(*string, CURWINDOW->Border->Attrib);
		string ++;
		CursR();
		}
	}
DecBounds(&CURWINDOW->Bounds);
}

/**************************************************************************
PutOString      -       Put Object String

Description     -       Writes the object string starting at the current
			cursor location.

Parameters      -       PutOString(type, string, active)
			WObj_s type; the object type
			char *string; for output
			BOOLEAN active; if TRUE then the marked '@' hot
				key is output with "bright ink".

Returns         -       The string's hot key as a char or '\0'.
**************************************************************************/
static char PutOString(WObj_s type, char *string, BOOLEAN active)
{
char *ptr = string, ch = '\0';

switch(type)
	{
	case MENU_TITLE:
		ch = POString(ptr, active, CURWINDOW->Attrib);
		break;
	case POP_UP_LIST:
		if(*ptr == '.')
			{
			IncBounds(&CURWINDOW->Bounds);
			BegLine();
			PutChBright(CURWINDOW->Border->LB,
				CURWINDOW->Border->Attrib);
			while(CursR())
				PutChBright(CURWINDOW->Border->HZ,
					CURWINDOW->Border->Attrib);
			PutChBright(CURWINDOW->Border->RB,
				CURWINDOW->Border->Attrib);
			DecBounds(&CURWINDOW->Bounds);
			}
		else
			ch = POString(ptr, active, CURWINDOW->Attrib);
		break;
	case CODE_BUTTON:
		{
		PutChar('<', CURWINDOW->Attrib);
		CursR();
		ch = POString(ptr, active, CURWINDOW->Attrib);
		PutChar('>', CURWINDOW->Attrib);
		}
		break;
	case SWITCH:
		{
		char *str  = "( )";

		ch = POString(ptr, active, CURWINDOW->Attrib);
		CursL(); CursL();
		POString(str, active, CURWINDOW->Attrib);
		}
		break;
	case CHECK_BOX:
		{
		char *str = "[ ]";

		ch = POString(ptr, active, CURWINDOW->Attrib);
		CursL(); CursL();
		POString(str, active, CURWINDOW->Attrib);
		}
		break;
	}
return(ch);
}

/**************************************************************************
POString        -       Low Level Put Output String

Description     -       Puts the object string at the current cursor
			location, using either the default or
			active attribute depending upon active parameter
			passed.

Parameters      -       POString(ptr, active, attrib)
			char *ptr; to the object string
			BOOLEAN active; TRUE or FALSE
			unsigned char attrib; the attribute

Returns         -       The hot key as a char or '\0' if none in string
**************************************************************************/
static char POString(char *string, BOOLEAN active, unsigned char attrib)
{
char ch = '\0';
BOOLEAN room = FALSE;

while(*string)
	{
	if(*string == '@')
		{
		string ++;
		if(active)
			{
			ch = (char)toupper(*string);
			/* with the "ink" highlighted */
			PutChBright(*string, attrib);
			}
		else
			/* with the "ink" dull */
			PutChDull(*string, attrib);
		}
	else
		/* with the "ink" dull */
		PutChDull(*string, attrib);
	string ++;
	room = CursR();
	}
while(room)
	{
	PutChDull(' ', attrib);
	room = CursR();
	}
return(ch);
}

/**************************************************************************
OpenWindow	-	Open Window

Description     -       Opens a user window in text mode using the Window_t
			structure.

			Draws the window border, clears the text area and
			makes it active by assigning struct to CURWINDOW.

			SCREEN_W type is pushed onto the SCREENSTACK,
			MENU_BAR_W type to the MENUSTACK

Parameters      -       OpenWindow(window)
			Window_t *window; pointer to structure containing
					    the window's details.

Returns 	-	The window pointer if successful, NULL indicates
			insufficient RAM.
**************************************************************************/
Window_t *OpenWindow(Window_t *window)
{
/* push the new window, deactivate ACTWINDOW if exists and reset
to this window if it is pushed to the SCREENSTACK */
if(!PushWindow(window))
	return(NULL);
/* save the area beneath the proposed window */
if(!(window->Area = SaveWArea(window)))
	return(NULL);
/* show the new window */
if(window->Pane && (!window->Changed))
	PutWArea(window, window->Pane);
else
	{
	ShowWindow(window, TRUE);
	if(window->Pane)
		free(window->Pane);
	window->Pane = SaveWArea(window);
	window->Changed = FALSE;
	}

/* now make it current */
CURWINDOW = window;
return(window);
}

/**************************************************************************
SaveWArea       -       Save Window Area

Description     -       Saves the scrren area beneath the window
			and assigns the contents to the window's
			Area field

Parameters      -       SaveWArea(window)
			Window_t *window;

Returns         -       The window pointer if successful, else NULL
**************************************************************************/
static unsigned *SaveWArea(Window_t *window)
{
unsigned *area;

/* save the screen area */
if(window->Border)
	IncBounds(&window->Bounds);
area = SaveScreen(window->Bounds.TRow,
window->Bounds.TCol, window->Bounds.BRow, window->Bounds.BCol);
if(window->Border)
	DecBounds(&window->Bounds);
return(area);
}

/**************************************************************************
PushWindow      -       Push Window

Description     -       Pushes the window onto the required stack,
			deactivates the current ACTWINDOW if required
			and resets ACTWINDOW to the new SCREEN_W
			pushed.

Parameters      -       PushWindow(window)
			Window_t *window

Returns         -       The supplied window pointer if successful,
			else NULL
**************************************************************************/
static Window_t *PushWindow(Window_t *window)
{
switch(window->Type)
	{
	case SCREEN_W:
	case OBJECT_W:
	case POP_UP_W:
		{
		if(ACTWINDOW && ACTWINDOW->SNode)
			/* de-activate it */
			/* we show the window title using the window's
			Border->Attrib and turn on the window title's hot key.

			Active hot keys can only relate to an item on
			top of the MENUSTACK, the ACTWINDOW, or be a window
			title hot key to select a previous thread. */

			FlipBorder(ACTWINDOW);

		/* now push the new window */
		if(!(window->SNode = PushStack(&SCREENSTACK, (LLDATA *)window)))
			return(NULL);
		/* now make it active */
		ACTWINDOW = window;
		}
		break;
	case MENU_BAR_W:
		{
		/* there is no menu title for this object and
		the new menu overwrites the old - so there is
		nothing to do except :- */
		/* push the new menu */
		if(!(window->SNode = PushStack(&MENUSTACK, (LLDATA *)window)))
			return(NULL);
		}
		break;
	default: /* unknown window type */
		return(NULL);
	}
return(window);
}

/**************************************************************************
ShowWindow      -       Show Window

Description     -       Displays the given window on the screen


Parameters      -       ShowWindow(window, active)
			Window_t *window; the window required
			BOOLEAN active; TRUE if ALT combinations apply

Returns         -       A pointer to the window shown if successful, else
			NULL
**************************************************************************/
static Window_t *ShowWindow(Window_t *window, BOOLEAN active)
{
llnode_t *nnode;

if(window->ObjList)
	{
	nnode = window->ObjList->first;
	MakeLinear(window->ObjList);
	}
else
	nnode = NULL;

CURWINDOW = window; /* all output routines use CURWINDOW, so assign here */
CursOff();

if(window->Border)
	DoWBorder();

if((CURWINDOW == ACTWINDOW) || (window->Type == MENU_BAR_W))
	ClearWindow();

ShowWObj(window);

/* process object list here - with the exception of a POP_UP_LIST */

while(nnode)
	{
	if(((Window_t *)nnode->item)->CType != POP_UP_LIST)
		ShowWindow((Window_t *)nnode->item, active);
	nnode = nnode->next;
	}
if(window->ObjList)
	MakeCirc(window->ObjList);
return(window);
}

/**************************************************************************
ShowWObj        -       Show Window Object

Description     -       Draws the object contained in the given
			window

Parameters      -       ShowWObj(window)
			Window_t *window

Returns         -       NOTHING
**************************************************************************/
static void ShowWObj(Window_t *window)
{
CursHome();
switch(window->CType)
	{
	case MENU_TITLE:
		{
		((Menu_t *)window->Contents)->HKey = PutOString(
		MENU_TITLE, ((Menu_t *)window->Contents)->Title, TRUE);
		}
		break;
	case POP_UP_LIST:
		{
		Option_t *next = ((Option_t *)window->Contents);

		while(next->Title)
			{
			next->HKey = PutOString(POP_UP_LIST, next->Title,
			next->State);
			CursRet();
			++ next;
			}
		}
		break;
	case CODE_BUTTON:
		{
		Button_t *next = (Button_t *)window->Contents;

		next->HKey = PutOString(CODE_BUTTON, next->Title,
			next->State);
		}
		break;
	case SWITCH:
		{
		Switch_t *next = ((Switch_t *)window->Contents);

		while(next->Title)
			{
			next->HKey = PutOString(SWITCH, next->Title,
				next->State);
			if(*(next->TempVar) == next->Mask)
				{
				CursL();
				PutChar(SW_BUTTON, CURWINDOW->Attrib);
				}
			CursRet();
			++ next;
			}
		}
		break;
	case CHECK_BOX:
		{
		CheckBox_t *next = ((CheckBox_t *)window->Contents);

		while(next->Title)
			{
			next->HKey = PutOString(CHECK_BOX, next->Title,
				next->State);
			if(*(next->TempVar) & next->Mask)
				{
				CursL();
				PutChar('X', CURWINDOW->Attrib);
				}
			CursRet();
			++ next;
			}
		}
		break;
		case STRING_D:
		case INT_D:
		case NUMBER_D:
		case DATE_D:
			{
			Screen_t bounds = window->Bounds;
			Dialogue_t *data = (Dialogue_t *)window->Contents;

			PosnCurs((unsigned char)(bounds.TRow + 1),
				window->TOffset);
			POString(window->Title, TRUE, window->Attrib);
			window->Bounds.TCol += window->TLength;
			PosnCurs((unsigned char)(window->Bounds.TRow + 1),
				window->Bounds.TCol);
			PutChar(S_BORDER.VT, window->Attrib);
			CursDn();
			PutChar(S_BORDER.BL, window->Attrib);
			while(CursR())
				PutChar(S_BORDER.HZ, window->Attrib);
			PutChar(S_BORDER.BR, window->Attrib);
			CursUp();
			PutChar(S_BORDER.VT, window->Attrib);
			CursUp();
			PutChar(S_BORDER.TR, window->Attrib);
			while(CursL())
				PutChar(S_BORDER.HZ, window->Attrib);
			PutChar(S_BORDER.TL, window->Attrib);
			window->Bounds = bounds;
			DecDBounds(window);
			PosnCurs(window->Bounds.TRow, window->Bounds.TCol);
			if(data->State == -1)
				{
				switch(window->CType)
					{
					case INT_D:
					case NUMBER_D:
						ShowDNumber();
					break;
					case STRING_D:
					case DATE_D:
					{
					PutString(CURWINDOW,
					data->Buffer, TRUE,
					(window->CType == DATE_D));
					}
					break;
					}
				}
			else if(data->State == 0)
				{
				ClearWindow();
				PutString(CURWINDOW,
				(char *)data->LPosn->item, TRUE,
				(window->CType == DATE_D));
				}
			else /* any other state */
				{
				do_buffer:
				PutString(CURWINDOW,
				&data->Buffer[data->Offset],
				TRUE, (window->CType == DATE_D));
				}
			IncDBounds(window);
			}
		break;
	default: /* unknown object type */
		break;
	}
}

/**************************************************************************
CloseWindow	-	Close Window

Description     -       First ensures that the target window is on top
			of the relevant stack by a call to PopWindow().
			Pops the target window from the relevant stack, then
			updates the physical screen by a call to CloseW().

Parameters      -       CloseWindow(target)
			Window_t *target; window for closing

Returns         -       A pointer to the closed window.
**************************************************************************/
Window_t *CloseWindow(Window_t *target)
{
llstack_t *stack = target->Type == MENU_BAR_W ? &MENUSTACK : &SCREENSTACK;
Window_t *temp = ACTWINDOW == target ? NULL : ACTWINDOW;

PopWindow(target);
PopStack(stack);
target->SNode = NULL;
CloseW(target);
ACTWINDOW = NULL;
CURWINDOW = NULL;
if(target->Type != MENU_BAR_W)
	{
	if(temp)
		PopWindow(temp); // restores old active window
	else
		/* need to restore the top item if it exists */
		if(SCREENSTACK.length)
			PopWindow((Window_t *)SCREENSTACK.top->item);
	}
return(target);
}

/**************************************************************************
CloseW          -       Close W - low level counterpart of Close Window

Description     -       Restores the previously saved screen area.

Parameters      -       CloseW(window)
			Window_t *window;

Returns         -       NOTHING
**************************************************************************/
static void CloseW(Window_t *window)
{
/* first save the work area for SCREEN_W and OBJECT_W types */
if(window->Changed)
	{
	if(window->Pane)
		free(window->Pane);
	if(window == ACTWINDOW)
		; /* do nothing */
	else
		FlipBorder(window); /* save hkey state correctly */
	window->Pane = SaveWArea(window);
	window->Changed = FALSE;
	}
free(PutWArea(window, window->Area));
window->Area = NULL;
}

/*************************************************************************
PutWArea        -       Put Window Area

Description     -       Puts the saved area to screen

Parameters      -       PutWArea(window, area)
			Window_t *window;
			unsigned *area;

Returns         -       The supplied area pointer
*************************************************************************/
static unsigned *PutWArea(Window_t *window, unsigned *area)
{
if(window->Border)
	IncBounds(&window->Bounds);
PutArea(window->Bounds.TRow, window->Bounds.TCol,
	window->Bounds.BRow, window->Bounds.BCol, area);
if(window->Border)
	DecBounds(&window->Bounds);
return(area);
}
/**************************************************************************
ScrollUp	-	Scroll Up

Description	-	Scrolls the window up by 1 line

Parameters	-	ScrollUp()

Returns 	-	NOTHING
**************************************************************************/
void	ScrollUp(void)
{
ScrUp(CURWINDOW->Bounds.TRow, CURWINDOW->Bounds.TCol,
      CURWINDOW->Bounds.BRow, CURWINDOW->Bounds.BCol,
      CURWINDOW->Attrib);
}

/**************************************************************************
ScrollDn	-	Scroll Down

Description	-	Scrolls the window down by 1 line

Parameters	-	ScrollDn()

Returns 	-	NOTHING
**************************************************************************/
void	ScrollDn(void)
{
ScrDn(CURWINDOW->Bounds.TRow, CURWINDOW->Bounds.TCol,
      CURWINDOW->Bounds.BRow, CURWINDOW->Bounds.BCol,
      CURWINDOW->Attrib);
}

/**************************************************************************
KillAllWindows  -       Kill All Windows

Description     -       Frees all memory used by window structures.

Parameters      -       KillAllWindows(void)

Returns 	-	NOTHING
**************************************************************************/
void    KillAllWindows(void)
{
while(SCREENSTACK.length)
	free(KillWindow((Window_t *)SCREENSTACK.top->item));
while(MENUSTACK.length)
	free(KillWindow((Window_t *)MENUSTACK.top->item));
}

/*************************************************************************
KillWindow      -       Kill Window

Description     -       Frees the memory used by a Window structure

Parameters      -       KillWindow(window)
			Window_t *window;

Returns         -       A Window_t pointer to the empty structure.
*************************************************************************/
Window_t *KillWindow(Window_t *window)
{
CloseWindow(window);
free(window->Pane);
KillWObjects(window);
window->Changed = TRUE;
return(window);
}

/*************************************************************************
IncBounds       -       Increment Bounds

Description     -       Increases the circumference by a value of one

Parameters      -       IncBounds(bounds)
			Screen_t *bounds; pointer to structure

Returns         -       NOTHING
**************************************************************************/
void IncBounds(Screen_t *Bounds)
{
Bounds->TRow --;
Bounds->BRow ++;
Bounds->TCol --;
Bounds->BCol ++;
}

/**************************************************************************
DecBounds       -       Decrement Bounds

Description     -       Reduces the circumference by a value of one

Parameters      -       DecBounds(bounds)
			Screen_t *bounds; pointer to structure

Returns         -       NOTHING
**************************************************************************/
void DecBounds(Screen_t *Bounds)
{
Bounds->TRow++;
Bounds->TCol++;
Bounds->BRow--;
Bounds->BCol--;
}

/**************************************************************************
AddWObj         -       Add Window Object

Description     -       Adds an object to the given window's ObjList.
			Valid objects, of WObj_s type are:-

Parameters      -       AddWObj(window, object)
			Window_t *twindow; target to add to
			Object_t *object; for addition

Returns         -       A pointer to the llnode_t structure appended
**************************************************************************/
static llnode_t *AddWObj(Window_t *twindow, Window_t *object)
{
if(!twindow->ObjList) /* need to create one */
	if(twindow->ObjList = NewList())
		; /* do nothing */
	else
		return(NULL);
/* now append the new object */
if(AppendItem(twindow->ObjList, (LLDATA *)object))
	{
	/* unless the object added is a POP_UP_LIST, we need to increment
	the target window's MinBounds as appropriate */
	if(object->CType != POP_UP_LIST)
		{
		SetMinBounds(&twindow->MinBounds, &object->Bounds);
		}
	if(twindow->Type == SCREEN_W)
		twindow->Type = OBJECT_W;
	return(twindow->ObjList->last);
	}
return(NULL);
}

/**************************************************************************
NewMenuBar      -       New Menu Bar

Description     -       Creates a new Window_t structure and fills it
			with the required menu bar titles.

Parameters      -       NewMenuBar(menu, attrib)
			Menu_t *menu; pointer to data array
			unsigned char attrib; required

Returns         -       A pointer to the new Window_t structure if
			successful, else NULL
**************************************************************************/
Window_t *NewMenuBar(Menu_t *menu, unsigned char attrib)
{
Window_t *window;
unsigned char ColNo, MaxCol;

MaxCol = HOST_SCREEN.BCol;

if(!(window = NewWindow(MENU_BAR_W, NULL, 0, 0, 0, MaxCol, attrib, NULL)))
		return(NULL);

/* initialise the ColNo */
ColNo = 1;

/* now add each menu title, incrementing colno and checking that it does not
exceed the screen col boundaries.  If it does then we must kill all objects
added and return NULL to the calling routine */
while(menu->Title)
	{
	ColNo += (AddMTitle(window, menu, 0, ColNo) + 1);
	menu++;
	}
if(ColNo > MaxCol)
	{
	KillWObjects(window);
	free(window);
	return(NULL);
	}
return(window);
}

/**************************************************************************
AddMTitle       -       Add Menu Title

Description     -       Adds a MENU_TITLE object to the given window

Parameters      -       AddMTitle(window, menu, TRow, TCol)
			Window_t *window; to add to
			Menu_t *menu; to add
			unsigned char TRow, TCol; object posn

Returns         -       The width of the title string if successful, else 0
**************************************************************************/
static unsigned char AddMTitle(Window_t *window, Menu_t *menu,
			unsigned char TRow, unsigned char TCol)
{
Window_t *NewObject;
unsigned char width;
char ch;

width = TStringWidth(menu->Title, &ch);
/* create a new object window structure */
if(NewObject = NewWindow(OBJECT_W, NULL, TRow, TCol, TRow,
	(unsigned char)(TCol + width), window->Attrib, NULL))
		{
		NewObject->CType = MENU_TITLE;
		NewObject->Contents = (void *)menu;
		NewObject->Parent = window;
		if(AddPList(NewObject, menu->Options, (unsigned char)(TRow + 1), TCol))
			if(NewObject->Posn = AddWObj(window, NewObject))
				return(width);
			else
				KillWObjects(NewObject);
		}
return(0);
}

/**************************************************************************
AddPList        -       Add Pop Up List

Description     -       Adds a list of code labels (for a pop up menu window)
			to the given windows ObjList

Parameters      -       AddPList(window, options, TRow, TCol)
			Window_t *window; the target
			Option_t *options; array pointer
			unsigned char TRow, TCol; initial coords

Returns         -       A Window_t pointer to the new structure if
			successful, else NULL
**************************************************************************/
static Window_t* AddPList(Window_t *window, Option_t *options,
		unsigned char TRow, unsigned char TCol)
{
Window_t *NewObject;

if(NewObject = NewPopUp(NULL, options, TRow, TCol, window->Attrib, &D_BORDER))
	{
	NewObject->Parent = window;
	if(NewObject->Posn = AddWObj(window, NewObject))
		return(NewObject);
	else
		free(NewObject);
	}
return(NULL);
}

/*************************************************************************
NewPopUp        -       New Pop Up Window

Description     -       Creates a new Pop Up Window

Parameters      -       NewPopUp(Title, Options, TRow, TCol, Attrib, Border)
			char *title; window title
			Option_t *options; array pointer
			unsigned char trow, tcol; initial coords
			unsigned char attrib; required
			Border_t *border;

Returns         -       A Window_t pointer to the new window if successful
			else NULL
***************************************************************************/
Window_t *NewPopUp(char *Title, Option_t *Options, unsigned char TRow,
	unsigned char TCol, unsigned char Attrib, Border_t *Border)
{
Window_t *NewObject;
unsigned char height = 0, width = 0, maxwidth = 0, BRow, BCol;
Option_t *next = Options;

/* calculate info on options data */
while(next->Title)
	{
	if((width = TStringWidth(next->Title, &next->HKey)) > maxwidth)
			maxwidth = width;
	height ++;
	next ++;
	}
/* adjust for border */
height += 2;
maxwidth += 2;
/* now calculate the boundaries */
BRow = (unsigned char)(TRow + height - 1);
BCol = (unsigned char)(TCol + maxwidth - 1);

/* and ensure that it is on the screen */
height = HOST_SCREEN.BRow;
width = HOST_SCREEN.BCol;
while(BCol > width)
	{
	TCol --;
	BCol --;
	}
while(BRow > height)
	{
	TRow --;
	BRow --;
	}
if( (TRow < 1) || (TCol < 0) )
	return(NULL); /* no can do */
if(NewObject = NewWindow(POP_UP_W, Title, TRow, TCol, BRow, BCol,
	Attrib, Border))
		{
		NewObject->CType = POP_UP_LIST;
		NewObject->Contents = (void *)Options;
		NewObject->MinBounds = NewObject->Bounds;
		return(NewObject);
		}
return(NULL);
}

/*************************************************************************
SetMinBounds    -       Set Minimum Boundaries

Description     -       Compares the two sets of Screen_t passed and
			enlarges the first parameter to include the second
			as required.

Parameters      -       SetMinBounds(master, child)
			Screen_t *master, child; boundary data

Returns         -       NOTHING
**************************************************************************/
static void SetMinBounds(Screen_t *master, Screen_t *child)
{
if(child->TRow < master->TRow)
	master->TRow = child->TRow;
if(child->TCol < master->TCol)
	master->TCol = child->TCol;
if(child->BRow > master->BRow)
	master->BRow = child->BRow;
if(child->BCol > master->BCol)
	master->BCol = child->BCol;
}

/**************************************************************************
TStringWidth    -       Command String Width

Description     -       Calculates the char length of a clean command
			string and sets the supplied char parameter
			to the found hot key

Parameters      -       TStringWidth(string, hkey)
			char *string; the command string
			char *ch; pointer to storage location

Returns         -       The clean length of the supplied string as an
			unsigned char
***************************************************************************/
unsigned char TStringWidth(char *string, char *hkey)
{
unsigned char w = 0;

while(*string)
	{
	if(*string == '@')
		{
		string++;
		*hkey = (char)toupper(*string);
		continue;
		}
	w++;
	string ++;
	}
return(w);
}


/**************************************************************************
AddButton       -       Add Button

Description     -       Adds a Button to the given window at the supplied
			starting coordinate

Parameters      -       AddButton(window, data, TRow, TCol)
			Window_t *window; for adding to
			Option_t *data; of the button
			unsigned char TRow, TCol; basic coords

Returns         -       The width of the resulting button if successful,
			else 0
**************************************************************************/
unsigned char AddButton(Window_t *window, Button_t *data,
		unsigned char TRow, unsigned char TCol)
{
Window_t *NewObject;
unsigned char width;

width = (unsigned char)(TStringWidth(data->Title, &data->HKey) + 2);
if(NewObject = NewWindow(OBJECT_W, NULL, TRow, TCol, TRow,
	(unsigned char)(TCol + width - 1), window->Attrib, NULL))
		{
		NewObject->CType = CODE_BUTTON;
		NewObject->Contents = (void *)data;
		NewObject->Parent = window;
		if(NewObject->Posn = AddWObj(window, NewObject))
			return(width);
		else
			free(NewObject);
		}
return(0);
}



/**************************************************************************
AddDBox         -       Add Dialogue Box

Description     -       Adds a dialogue box to the given window as a
			further object to be managed

Parameters      -       AddDBox(window, label, type, data, trow, tcol, width)
			Window_t *window; the target window to add to
			char *label; the box's label string
			WObj_s type; the type of dialogue box ..._D
			void *data; to the dialogue description structure
			unsigned char trow, tcol; top left location
			unsigned char width;

Returns         -       The width of the box and title if successful as
			an unsigned char, else 0
***************************************************************************/
unsigned char AddDBox(Window_t *window, char *label, WObj_s type, void *data,
	unsigned char trow, unsigned char tcol, unsigned char width)
{
Window_t *NewObject;
unsigned char brow, bcol;
unsigned char w;
char ch;

w = TStringWidth(label, &ch);
brow = (unsigned char)(trow + 2);
bcol = (unsigned char)(tcol + width + w + 2);
if(NewObject = NewWindow(OBJECT_W, NULL, trow, tcol, brow, bcol,
	window->Attrib, NULL))
		{
		LabelWindow(NewObject, label);
		NewObject->CType = type;
		NewObject->Contents = data;
		NewObject->Parent = window;
		if(NewObject->Posn = AddWObj(window, NewObject))
			{
			DecDBounds(NewObject);
			NewObject->CursPosn.Row = NewObject->Bounds.TRow;
			NewObject->CursPosn.Col = NewObject->Bounds.TCol;
			IncDBounds(NewObject);
			return((unsigned char)width);
			}
		else
			free(NewObject);
		}
return(0);
}


/**************************************************************************
AddSBox         -       Add Switches or Check Boxes

Description     -       Adds a number of switches or check boxes
			to the given window depending upon supplied
			type parameter.

Parameters      -       AddSBox(type, window, switches, TRow, TCol, title)
			WObj_s type; either SWITCH or CHECK_BOX
			Window_t *window; to add to
			Switch_t *switches; array pointer
			unsigned char TRow, TCol; basic coordinate

Returns         -       TRUE if successful else FALSE
**************************************************************************/
BOOLEAN AddSBox(WObj_s type, Window_t *window, Switch_t *switches,
		unsigned char TRow, unsigned char TCol)
{
Window_t *NewObject;
Switch_t *next = switches;
unsigned char height = 0, width = 0, maxwidth = 0;
int count = 0;

while(next->Title)
	{
	if((width = TStringWidth(next->Title, &next->HKey))>maxwidth)
			maxwidth = width;
	height ++;
	next ++;
	}
maxwidth += 3; /* adjust for " ( )" or " [ ]" */
if(NewObject = NewWindow(OBJECT_W, NULL, TRow, TCol,
	(unsigned char)(TRow +height - 1), (unsigned char)(TCol + maxwidth),
	window->Attrib, NULL))
		{
		NewObject->CType = type;
		NewObject->Contents = switches;
		NewObject->Parent = window;
		if(NewObject->Posn = AddWObj(window, NewObject))
			return(TRUE);
		free(NewObject);
		}
return(FALSE);
}

/**************************************************************************
KillWObjects    -       Kill Window Objects

Description     -       Removes all a window's sub windows and related
			objects recursively

Parameters      -       KillWObjects(window)
			Window_t *window;

Returns         -       NOTHING
**************************************************************************/
static void KillWObjects(Window_t *window)
{
if(!window->ObjList)
	return;
MakeLinear(window->ObjList);
while(window->ObjList->first)
	{
	KillWObjects((Window_t *)window->ObjList->first->item);
	free((Window_t *)window->ObjList->first->item);
	DeleteNode(window->ObjList, window->ObjList->first, TRUE);
	}
free(window->ObjList);
window->ObjList = NULL;
window->CType = EMPTY;
}

/**************************************************************************
GetWindow       -       Get Window

Description     -       Locates the window whose title hot key corresponds
			to the char passed.  Searching takes place on the
			SCREENSTACK.

Parameters      -       GetWindow(key)
			char key;

Returns         -       A Window_t pointer to the located window if
			successful, else NULL
***************************************************************************/
Window_t *GetWindow(char key)
{
char ch = (char)toupper(key);
llnode_t *next;

if(SCREENSTACK.top)
	{
	next = SCREENSTACK.top; /* not interested in active window */
	while(next)
		{
		Window_t *ptr = (Window_t *)next->item;
		if((ptr->HKey == ch) &&
			(ptr != ACTWINDOW))
			return(ptr);
		next = next->prev;
		}
	}
return(NULL);
}

/**************************************************************************
PopW            -       Pop W - low level counterpart of PopWindow

Description     -       Closes any obscuring windows, pulling them from
			the window stack and pushing them to TEMPSTACK.

Parameters      -       PopW(target)
			Window_t *target; for popping

Returns         -       A pointer to the window popped if successful
			else NULL and windows removed temporarily on the
			TEMPSTACK (if any).
***************************************************************************/
static Window_t *PopW(Window_t *target)
{
llstack_t *stack = target->Type == MENU_BAR_W ? &MENUSTACK : &SCREENSTACK;

if(!WNotVisible(target))
	{
	PullStack(stack, target->SNode);
	target->SNode = NULL;
	return(target);
	}
else
	{
	Window_t *window;

	while(window = WNotVisible(target))
		{
		PushStack(&TEMPSTACK, (LLDATA *)PopW(window)); /* recursive
								call */
		CloseW(window);
		}
	/* now target is visible */
	PullStack(stack, target->SNode);
	target->SNode = NULL;
	return(target);
	}
}

/**************************************************************************
PopWindow       -       Pop Window

Description     -       High level version of PopW().  Pulls the given
			target off of the stack by a call to PopW() and
			then checks the status of TEMPSTACK.

			If TEMPSTACK contains any items removed from the
			window stack, the target is closed, the contents
			of TEMPSTACK popped and re-opened, the target then
			opened.

			If TEMPSTACK is empty, then the target is simply
			pushed to the appropriate window stack.

			Finally both ACTWINDOW and CURWINDOW are set to
			target and the target shown as active.

Parameters      -       PopWindow(target)
			Window_t *target;

Returns         -       The length of TEMPSTACK as size_t
***************************************************************************/
size_t PopWindow(Window_t *target)
{
if(target->Type == MENU_BAR_W)
	/* show the top menu bar window with hot keys off */
	FlipBorder((Window_t *)MENUSTACK.top->item);
else
	if(ACTWINDOW && ACTWINDOW->SNode)
		/* show this window with hot keys off */
		{
		FlipBorder(ACTWINDOW);
		ACTWINDOW = NULL;
		}
PopW(target);
if(TEMPSTACK.length)
	{
	CloseW(target);
	while(TEMPSTACK.length)
		OpenWindow((Window_t *)PopStack(&TEMPSTACK));
	OpenWindow(target);
	}
else
	{
	llstack_t *stack = target->Type == MENU_BAR_W ?
					   &MENUSTACK : &SCREENSTACK ;

	target->SNode = PushStack(stack, (LLDATA *)target);
	FlipBorder(target);
	if(target->Type != MENU_BAR_W)
		ACTWINDOW = CURWINDOW = target;
	}
return(TEMPSTACK.length);
}

/*************************************************************************
ClipCode        -       ClipCode

Description     -       Assigns a clip code based on the position of the
			given point in relation to the passed boundaries

				1001 | 1000 | 1010
				------------------
				0001 | 0000 | 0010
				------------------
				0101 | 0100 | 0110

Parameters      -       ClipCode(row, col, bounds)
			unsigned char row, col; point description
			Screen_t *bounds; the boundaries

Returns         -       The assigned clip code as an int
**************************************************************************/
int ClipCode(unsigned char row, unsigned char col, Screen_t *bounds)
{
int code = 0;

if(col < bounds->TCol)
	code += BIT1;
else if(col > bounds->BCol)
	code += BIT2;
if(row < bounds->TRow)
	code += BIT4;
else if(row > bounds->BRow)
	code += BIT3;
return(code);
}

/*************************************************************************
SetDCodes       -       Set Diagonal Codes

Descriptions    -       Sets the two clip codes required, corresponding
			to a diagonal line drawn from the top left corner
			of a window to its bottom right corner

Parameters      -       SetCodes(bounds1, bounds2)
			Screen_t *bounds1; for codes required
			Screen_t *bounds2; boundaries for comparing

Returns         -       NOTHING
**************************************************************************/
static void SetDCodes(Screen_t *bounds1, Screen_t *bounds2)
{
code1 = ClipCode(bounds1->TRow, bounds1->TCol, bounds2);
code2 = ClipCode(bounds1->BRow, bounds1->BCol, bounds2);
code3 = ClipCode(bounds1->BRow, bounds1->TCol, bounds2);
code4 = ClipCode(bounds1->TRow, bounds1->BCol, bounds2);
}

/**************************************************************************
IsAbove         -       Is Above

Description     -       Checks to see if the window contained in the
			target node is above that contained in the
			location node.

Parameters      -       IsAbove(location, target)
			llnode_t *location, *target;

Returns         -       TRUE if target window is above location window,
			else FALSE
***************************************************************************/
BOOLEAN IsAbove(llnode_t *location, llnode_t *target)
{
Screen_t *tbounds, *lbounds;

tbounds = &((Window_t *)target->item)->Bounds;
lbounds = &((Window_t *)location->item)->Bounds;
SetDCodes(lbounds, tbounds);
if( (code1 & 4) && (code4 & 4) ) // location' top line is beneath target
	if( (code1 == 4) || (code4 == 4) ) // one end is directly beneath
		return(TRUE);
return(FALSE);
}

/***************************************************************************
IsBelow         -       Is Below

Description     -       Checks to see if the window contained in the
			target node is below that contained in the
			location node.

Parameters      -       IsBelow(location, target)
			llnode_t *location, *target;

Returns         -       TRUE if target window is below location window,
			else FALSE
***************************************************************************/
BOOLEAN IsBelow(llnode_t *location, llnode_t *target)
{
Screen_t *tbounds, *lbounds;

tbounds = &((Window_t *)target->item)->Bounds;
lbounds = &((Window_t *)location->item)->Bounds;
SetDCodes(lbounds, tbounds);
if( (code3 & 8) && (code2 & 8) ) // location' bottom line is above target
	if( (code3 == 8) || (code2 == 8) ) // one end is directly above
		return(TRUE);
return(FALSE);
}

/*************************************************************************
B1inB2          -       Bounds1 contained in Bounds2

Description     -       Checks to see that the diagonal lines
			contained in bounds1 are all contained in bounds2

Parameters      -       B1inB2(bounds1, bounds2)
			Screen_t *bounds1, *bounds2;

Returns         -       TRUE if all points lie inside bounds 2, else FALSE
**************************************************************************/
BOOLEAN B1inB2(Screen_t *bounds1, Screen_t *bounds2)
{
SetDCodes(bounds1, bounds2);
if(PointsInside() == 4)
	return(TRUE);
return(FALSE);
}

/**************************************************************************
WindowClash     -       Window Clash

Description     -       Discovers if the two windows overlap by finding
			out if either of the first window's diagonals are
			located anywhere in the second window's boundaries.

Parameters      -       WindowClash(window1, window2)
			Window_t *window1, window2; for comparison

Returns         -       0 if both windows are in separate areas else the
			number of diagonal endpoints that are located
			within it - up to a maximum of 4.
***************************************************************************/
static int WindowClash(Window_t *window1, Window_t *window2)
{
if(window1->Border)
	IncBounds(&window1->Bounds);
if(window2->Border)
	IncBounds(&window2->Bounds);
SetDCodes(&window1->Bounds, &window2->Bounds);
if(window1->Border)
	DecBounds(&window1->Bounds);
if(window2->Border)
	DecBounds(&window2->Bounds);
return(PointsInside());
}

/**************************************************************************
PointsInside    -       Points Inside

Description     -       Tests the set clipcodes to determine the result
			of a two line clip.

Parameters      -       PointsInside()

Returns         -       The number of bound1 line end points that lie
			within bound2. 0 indicates no overlap occurs.
***************************************************************************/
static int PointsInside(void)
{
int result;

if(NOTWITHIN(code1, code2)) /* no clash */
	result = 0;
else if(BOTHIN(code1, code2))
	result = 2;
/* then one point is inside and the other is outside */
else result = 1;
if(NOTWITHIN(code3, code4)) /* no clash */
	; /* do nothing */
else if(BOTHIN(code3, code4))
	result += 2;
else ++result;
return(result);
}

/***************************************************************************
WNotVisible     -       Window Not Visible

Description     -       Walks up the relevant window stack to find
			the first window that is obscuring the target.

Parameters      -       WNotVisible(window)
			Window_t *window;

Returns         -       A pointer to the intruding window, or NULL if
			the given window is fully visible.
***************************************************************************/
static Window_t *WNotVisible(Window_t *window)
{
llnode_t *next = window->SNode->next;

while(next)
	{
	if(WindowClash((Window_t *)next->item, window))
		return((Window_t *)next->item);
	next = next->next;
	}
return(NULL);
}

/***************************************************************************
CheckStack      -       Check Stack

Description     -       Checks the given stack for any window that
			is overlapped by the given window

Parameters      -       CheckStack(stack, window)
			llstack_t *stack; for checking
			Window_t *window; for comparison

Returns         -       TRUE if the given window overlaps any stack member,
			else FALSE
***************************************************************************/
static BOOLEAN CheckStack(llstack_t *stack, Window_t *window)
{
if(stack->top)
	{
	llnode_t *next = stack->top;

	while(next)
		{
		if(WindowClash((Window_t *)next->item, window))
			return(TRUE);
		next = next->prev;
		}
	}
return(FALSE);
}

/**************************************************************************
GetObject       -       Get Object

Description     -       Searches the ACTWINDOW and, if necessary, the
			MENUSTACK.top window to locate the object
			associated with the given hot key.

Parameters      -       GetObject(hkey)
			char hkey;

Returns         -       A pointer to the Event_t structure containing the
			type and a void pointer to the data structure
			concerned.
**************************************************************************/
Event_t *GetObject(char hkey)
{
char ch = (char)toupper(hkey);

if(ACTWINDOW)
	if(FindWObj(ACTWINDOW, ch))
		return(&EVENT);
if(MENUSTACK.top)
	if(FindWObj((Window_t *)MENUSTACK.top->item, ch))
		return(&EVENT);
return(NULL);
}

/*************************************************************************
FindWObj        -       Find Window Object

Description     -       Looks recursively through a given window's
			object list to find the item with the given
			hot key character

Parameters      -       FindWObj(window, hkey)
			Window_t *window;
			char hkey;

Returns         -       TRUE if the hot key is located, else FALSE
***************************************************************************/
static BOOLEAN FindWObj(Window_t *window, char hkey)
{
llnode_t *nnode = window->ObjList ? window->ObjList->first : NULL;

switch(window->CType)
	{
	case MENU_TITLE:
		{
		if(((Menu_t *)window->Contents)->HKey == hkey)
			{
			EVENT.Event = DO_MENU_BAR;
			EVENT.Window = window;
			return(TRUE);
			}
		}
		break;
	case POP_UP_LIST:
		{
		Option_t *next = ((Option_t *)window->Contents);
		int line = 0;

		while(next[line].Title)
			{
			if( (next[line].Title[0] != '.') &&
				(next[line].State > 0) &&
				(next[line].HKey == hkey) )
				{
				EVENT.Event = DO_LINE;
				EVENT.Window = window;
				EVENT.LineNo = line;
				return(TRUE);
				}
			++ line;
			}
		}
		break;
	case CODE_BUTTON:
		{
		if( (((Button_t *)window->Contents)->State > 0) &&
		(((Button_t *)window->Contents)->HKey == hkey) )
			{
			EVENT.Event = DO_BUTTON;
			EVENT.Window = window;
			EVENT.LineNo = 0;
			return(TRUE);
			}
		}
		break;
	case SWITCH:
		{
		Switch_t *next = ((Switch_t *)window->Contents);
		int line = 0;

		while(next[line].Title)
			{
			if( (next[line].State > 0) &&
				(next[line].HKey == hkey) )
				{
				EVENT.Event = DO_SWITCH;
				EVENT.Window = window;
				EVENT.LineNo = line;
				return(TRUE);
				}
			++ line;
			}
		}
		break;
	case CHECK_BOX:
		{
		CheckBox_t *next = ((CheckBox_t *)window->Contents);
		int line = 0;

		while(next[line].Title)
			{
			if( (next[line].State > 0) &&
				(next[line].HKey == hkey) )
				{
				EVENT.Event = DO_CHECK_BOX;
				EVENT.Window = window;
				EVENT.LineNo = line;
				return(TRUE);
				}
			++ line;
			}
		}
		break;
		case STRING_D:
		case INT_D:
		case NUMBER_D:
		case DATE_D:
			{
			if(hkey == window->HKey)
				{
				EVENT.Event = DO_DIALOGUE;
				EVENT.Window = window;
				EVENT.LineNo = 0;
				return(TRUE);
				}
			}
		break;
	default: /* unknown object type */
		break;
	}
/* process object list here - with the exception of a POP_UP_LIST */
if(window->ObjList)
	MakeLinear(window->ObjList);
while(nnode)
	{
	if(((Window_t *)nnode->item)->CType != POP_UP_LIST)
		if(FindWObj((Window_t *)nnode->item, hkey))
			{
			MakeCirc(window->ObjList);
			return(TRUE);
			}
	nnode = nnode->next;
	}
if(window->ObjList)
	MakeCirc(window->ObjList);
return(FALSE);
}

/**************************************************************************
GetMObj         -       Get Mouse Object

Description     -       Compares the given co-ordinates with the menu
			bar boundaries to determine if this was selected.
			If it was then we examine each menu title to see
			if it was chosen, returning NULL if no match.
			If not then we walk down the screen stack to
			determine if a window was chosen.
			If the active window was selected we try to
			match the coordinates with an active object,
			returning NULL if no match.
			If a non active window was selected the user is
			indicating a pop request.

Parameters      -       GetMObj(row, col)
			unsigned int row, col;

Returns         -       A pointer to the Event_t containing the appropriate
			details, or NULL if the click took place on the
			desktop.
***************************************************************************/
Event_t  *GetMObj(unsigned int row, unsigned int col)
{
llnode_t *next;
Window_t *window;

EVENT.Event = 0;

/* check the active window first */

if(ACTWINDOW->Border)
	{
	/* is it on the close window icon */
	if( (row == (unsigned)((int)ACTWINDOW->Bounds.TRow - 1)) &&
	    (col == (unsigned)((int)ACTWINDOW->Bounds.TCol + 1)) )
		{
		EVENT.Event = CLOSE_WINDOW;
		return(&EVENT);
		}
	/*  so is it in the window's border - possible move */
	if(ClipCode(ROW, COL, &ACTWINDOW->Bounds))
		{
		IncBounds(&ACTWINDOW->Bounds);
		if(!ClipCode(ROW, COL, &ACTWINDOW->Bounds))
			{
			EVENT.Event = ALTER_WINDOW;
			DecBounds(&ACTWINDOW->Bounds);
			return(&EVENT);
			}
		DecBounds(&ACTWINDOW->Bounds);
		}
	}
/* so is it within the active window */
if( FindMWObj(ACTWINDOW, ROW, COL) )
	if(EVENT.Event)
		return(&EVENT);
	else
		return(NULL);
/* so check the menu bar */
if(MENUSTACK.top)
	if( FindMWObj((Window_t *)MENUSTACK.top->item, ROW, COL) )
		return(&EVENT);
/* so walk down the screen stack for a possible change window */
for(next = SCREENSTACK.top; next != NULL ; next = next->prev)
	{
	window = (Window_t *)next->item;

	if( window->Border )
		IncBounds( &window->Bounds);
	if(!ClipCode(ROW, COL, &window->Bounds))
		{
		EVENT.Event = CHANGE_WINDOW;
		EVENT.Window = window;
		if( window->Border )
			DecBounds(&window->Bounds);
		return(&EVENT);
		}
	if( window->Border )
		DecBounds( &window->Bounds);
	}
/* so must be a non event */
return(NULL);
}

/*************************************************************************
FindMWObj       -       Find Mouse Window Object

Description     -       Looks recursively through a given window's
			object list to find the item clicked on setting
			global EVENT.Event to a value greater than 0 if
			found.

Parameters      -       FindMWObj(window, row, col)
			Window_t *window;
			unsigned char row, col; co-ords;

Returns         -       1 as an unsigned int if successful
			with EVENT.Event == 0 or set.
***************************************************************************/
static unsigned int FindMWObj(Window_t *window, unsigned char row,
	unsigned char col)
{
llnode_t *nnode = window->ObjList ? window->ObjList->first : NULL;

if(window->Border)
	IncBounds(&window->Bounds);
if(!ClipCode(row, col, &window->Bounds))
	{
	switch(window->CType)
		{
		case MENU_TITLE:
			{
			EVENT.Event = DO_MENU_BAR;
			EVENT.Window = window;
			goto end;
			}
			break;
		case POP_UP_LIST:
			{
			Option_t *item = ((Option_t *)window->Contents);
			int line = window->Border ?
				row - window->Bounds.TRow - 1:
				row - window->Bounds.TRow;

			if( (item[line].Title[0] != '.') &&
			(item[line].State > 0) )
				{
				EVENT.Event = DO_LINE;
				EVENT.Window = window;
				EVENT.LineNo = line;
				goto end;
				}
			goto stop;
			}
			break;
		case CODE_BUTTON:
			{
			if( ((Button_t *)window->Contents)->State > 0 )
				{
				EVENT.Event = DO_BUTTON;
				EVENT.Window = window;
				EVENT.LineNo = 0;
				goto end;
				}
			goto stop;
			}
			break;
		case SWITCH:
			{
			Switch_t *item = ((Switch_t *)window->Contents);
			int line = window->Border ?
				row - window->Bounds.TRow - 1:
				row - window->Bounds.TRow;

			if( item[line].State > 0 )
				{
				EVENT.Event = DO_SWITCH;
				EVENT.Window = window;
				EVENT.LineNo = line;
				goto end;
				}
			goto stop;
			}
			break;
		case CHECK_BOX:
			{
			CheckBox_t *item = ((CheckBox_t *)window->Contents);
			int line = window->Border ?
				row - window->Bounds.TRow - 1:
				row - window->Bounds.TRow;

			if( item[line].State > 0 )
				{
				EVENT.Event = DO_CHECK_BOX;
				EVENT.Window = window;
				EVENT.LineNo = line;
				goto end;
				}
			goto stop;
			}
			break;
		case STRING_D:
		case INT_D:
		case NUMBER_D:
		case DATE_D:
		       {
		       EVENT.Event = DO_DIALOGUE;
		       EVENT.Window = window;
		       EVENT.LineNo = 0;
		       goto end;
		       }
		break;
		case EMPTY:
		default: /* unknown object type */
			break;
		} // end switch
	/* process object list here - with the exception of a POP_UP_LIST */
	if(nnode)
		MakeLinear(window->ObjList);
	while(nnode)
		{
		if(((Window_t *)nnode->item)->CType != POP_UP_LIST)
			if(FindMWObj((Window_t *)nnode->item, row, col))
				{
				MakeCirc(window->ObjList);
				goto stop;
				}
		nnode = nnode->next;
		}
	if(window->ObjList)
		MakeCirc(window->ObjList);
	} // end if clip code
end:
if(window->Border)
	DecBounds(&window->Bounds);
return(EVENT.Event);
stop:
if(window->Border)
	DecBounds(&window->Bounds);
return(1);
}

/**************************************************************************
FlipBorder      -       Flip Border

Description     -       Flips the attributes of the window border making
			it appear active if inactive and vice versa

Parameters      -       FlipBorder(window)
			Window_t *window;

Returns         -       NOTHING
**************************************************************************/
static void FlipBorder(Window_t *window)
{
Screen_t area;

if(!window->Border)
	return;
IncBounds(&window->Bounds);
area = window->Bounds;
++area.TCol;
--area.BCol;
area.BRow = area.TRow;
FlipArea(&area);
area.TRow = area.BRow = window->Bounds.BRow;
FlipArea(&area);
area = window->Bounds;
area.BCol = area.TCol;
FlipArea(&area);
area.TCol = area.BCol = window->Bounds.BCol;
FlipArea(&area);
DecBounds(&window->Bounds);
}

/**************************************************************************
wprintf         -       Window printf

Description     -       Window counterpart of standard library printf
			function with the exception that '\t' is not
			handled

Parameters      -       wprintf(window, restore, format, ...)
			Window_t *window;
			BOOLEAN restore; TRUE if cursor posn to be
				left unchanged, else FALSE
			char *format;
			...  format arguments as per printf;

Returns         -       The number of characters printed or -1 to
			indicate an error
***************************************************************************/
int wprintf(Window_t *window, BOOLEAN restore, char *fmt, ...)
{
int ret, size;
va_list marker;
char *buffer;

/* It's probably safe to use a buffer length of 512 bytes or five times
the length of the format string. */
size = strlen(fmt);
size = (size > 512) ? 512 : size * 5;
if( (buffer = (char *)malloc( size )) == NULL )
	return(-1);

    /* Write text to a string */
va_start(marker, fmt);
ret = vsprintf(buffer, fmt, marker);
va_end(marker);
ResCurs(&window->CursPosn);
/* output the string to the window */
ret = PutString(window, buffer, restore, FALSE);
free(buffer);
return(ret);
}

/**************************************************************************
PutString       -       Put String

Description     -       Puts the given string to the given window
			starting at the window's present cursor
			location.  If restore is TRUE then the
			the cursor is repositioned at its original
			location, otherwise the window's CursPosn
			is updated.

Parameters      -       PutString(window, buffer, restore, date)
			Window_t *window;
			char *buffer;
			BOOLEAN restore;
			BOOLEAN date; TRUE if : conversion required

Returns         -       NOTHING
**************************************************************************/
int PutString(Window_t *window, char *buffer, BOOLEAN restore, BOOLEAN date)
{
int ret = 0;
Curs_t cposn;

CURWINDOW = window;
if(restore)
	SaveCurs(&cposn);

restart:

if(CursR())
	CursL(); /* do nothing */
else
	while(*buffer && (*buffer != '\n'))
		buffer ++;
	entrypoint:
while(*buffer)
	{
	if(*buffer == '\n')
		{
		if(CursDn())
			BegLine();
		else
			{
			ScrollUp();
			BegLine();
			}
		buffer ++;
		continue;
		}
	if(*buffer == '\t')
		{
		unsigned char row, col;
		int moves;
		buffer ++;
		GetCurs(&row, &col);
		moves = (int)window->Bounds.TCol - (int)col + 1;
		do
			{
			if(CursR())
				moves ++;
			else
				goto restart;
			}
		while(moves % TABSTOPS);
		goto entrypoint;
		}
	if(date && (*buffer == ':'))
		PutChar(DATE_SEPARATOR, window->Attrib);
	else
		PutChar(*buffer, window->Attrib);
	ret ++;
	buffer ++;
	if(CursR())
		; /* do nothing */
	else
		while(*buffer && (*buffer != '\n'))
			buffer ++;
	}
/* save the current cursor position */
if(!restore)
	SaveCurs(&window->CursPosn);
else
	ResCurs(&cposn);
window->Changed = TRUE;
return(ret);
}

/*************************************************************************
BottomWindow    -       Bottom Window

Description     -       Locates the window on the bottom of the
			SCREENSTACK

Parameters      -       BottomWindow()

Returns         -       A Window_t pointer to the window at the
			bottom of the stack or NULL if there is not
			two or more windows
*************************************************************************/
Window_t *BottomWindow(void)
{
if(SCREENSTACK.length <= 1)
	return(NULL);
else /* OK */
	{
	llnode_t *prev = SCREENSTACK.top;

	while(prev->prev)
		prev = prev->prev;
	return((Window_t *)prev->item);
	}
}
