/*************************************************************************

input.c 	-	User input function file.

Programmer      -       Edward James Pugh, FIAP.

Copyright       -       (C) Edward James Pugh, 12-Jan-1990.

*************************************************************************/

#include <conio.h>
#include <stdlib.h>
#include "ibmpc.h"

/* useful macros */

/* declare external variable support */
extern unsigned char SW_BUTTON; /* in ibmpc.c */
extern Event_t  EVENT; /* in screen.c */
extern Window_t *ACTWINDOW; /* in screen.c */
extern Window_t *CURWINDOW; /* in screen.c */
extern int MOUSE_SUPPORTED; /* in ibmpc.c */
extern Screen_t HOST_SCREEN; /* in ibmpc.c */

int     QUIT_PROGRAM = 0; /* default */

Thread_t *MAINTHREAD;

unsigned char CLOSE_WINDOW_KEY = F10;  /* default */
unsigned char ALTER_WINDOW_KEY = F9;  /* default */

unsigned char CHANGE_WINDOW_KEY = F6; /* default */
unsigned char HELP_KEY = F1; /* default */
void (*DO_HELP_FUNCTION)(void) = NULL; /* default */
unsigned HELP_STACK_SIZE = 1024; /* default */
BOOLEAN  HELP_ON = FALSE; /* default */

/* function prototypes */
static Event_t *DoMenuBar(Window_t *window);
static Event_t *DoButton(Window_t *window, unsigned int mode);
static Event_t *DoSwitch(Window_t *window, int line, unsigned int mode);
static Event_t *DoCheckBox(Window_t *window, int line, unsigned int mode);
static BOOLEAN SwitchBoxFL(void *ptr, int *first, int *last);
static int AlterWindow(void);
static void MoveWindow(Window_t *window, Screen_t *bounds);
static void SizeWindow(Window_t *window, Screen_t *bounds, int loc);
static void MoveWBounds(Window_t *window, int hmoves, int vmoves);

/**************************************************************************
GetEvent        -       Get Event

Description     -       Gets a system event for use by the calling routine.
			We set up a continuous loop that polls the keyboard
			and mouse (if one has been declared via the setting
			of MOUSE_SUPPORTED).

			ALT_EXT keys are interpreted as an Object's
			selection from within the current window or the
			Menu bar.  SHIFT_ALT_EXT keys request a change of
			window.

			The mode parameter determines the degree to which
			the event manager is responsible for automating
			the interface.  Bits have the following meanings:

			.......x  ALLOW_MENU_BAR          1
			......x.  ALLOW_ALTER_WINDOW      2
			.....x..  REPORT_EXT_KEY          4
			....x...  NO_FLUSH_KEYBD          8
			...x....  REPORT_NULL_EVENT       16

Parameters      -       GetEvent(mode)
			unsigned int mode;

Returns         -       A pointer to the Event_t structure.
**************************************************************************/
Event_t *GetEvent(unsigned int mode)
{
unsigned int status, row, col;

if(mode & NO_FLUSH_KEYBD)
	; /* do nothing */
else
	fflush(stdin);
for(;;) // continuous poll
	{
	/* check for EXIT_PROGRAM here */
	if(QUIT_PROGRAM)
		{
		EVENT.Event = EXIT_PROGRAM;
		EVENT.RetValue = QUIT_PROGRAM;
		return(&EVENT);
		}
	if(MOUSE_SUPPORTED)
		{
		if(GetMPress(0, &row, &col, &status))
			{
			/* find out where the EVENT occurred */
			if(GetMObj(row, col))
				{
				decision:
				switch(EVENT.Event)
					{
			case DO_MENU_BAR:
			{
			if(mode & ALLOW_MENU_BAR)
				{
				if(DoMenuBar(EVENT.Window))
					goto decision;
				else
					{
					if(mode & REPORT_NULL_EVENT)
						return(NULL);
					else
						break;
					}
				}
			else
				return(&EVENT);
			}
			break;
			case ALTER_WINDOW:
			{
			if(mode & ALLOW_ALTER_WINDOW)
				{
				EVENT.RetValue = AlterWindow();
				return(&EVENT);
				}
			else
				return(&EVENT);
			}
			break;
			case DO_DIALOGUE:
			case NEW_PROCESS:
			case CHANGE_WINDOW:
			case CLOSE_WINDOW:
			case DO_SWITCH:
			case DO_CHECK_BOX:
			case DO_BUTTON:
			case DO_LINE:
			case EXT_KEY_EVENT:
			case ASCII_KEY:
			case EXIT_PROGRAM:
				return(&EVENT);
			break;
					} // end switch
				} // end where EVENT
			} // end left button mouse press
		} // end if mouse supported
	if(kbhit())
		{
		/* read the key */
		status = KBStatus();
		if(PCKey(&EVENT.Key, &EVENT.Input))
			{
			/* extended character */
			EVENT.Class = ExtCharClass(&EVENT.Key);
			if(EVENT.Class == ALT_EXT)
				{
				/* possible object selection */
				if(GetObject(EVENT.Key))
					goto decision;
				} // end ALT_EXT
			else if(EVENT.Class == ALT_SHIFT_EXT)
				{
				/* possible window change */
				if(EVENT.Window = GetWindow(EVENT.Key))
					{
					EVENT.Event = CHANGE_WINDOW;
					goto decision;
					}
				} // end ALT_SHIFT_EXT
			else if( EVENT.Class == VIRGIN )
				{
				if(EVENT.Key == CLOSE_WINDOW_KEY)
					{
					EVENT.Event = CLOSE_WINDOW;
					goto decision;
					}
				else if(EVENT.Key == ALTER_WINDOW_KEY)
					{
					EVENT.Event = ALTER_WINDOW;
					goto decision;
					}
				else if(EVENT.Key == HELP_KEY)
					{
					if(HELP_ON && DO_HELP_FUNCTION)
						{
						EVENT.Event = NEW_PROCESS;
						EVENT.TgtProcess =
							DO_HELP_FUNCTION;
						EVENT.StackSize =
							HELP_STACK_SIZE;
						goto decision;
						}
					}
				else if(EVENT.Key == CHANGE_WINDOW_KEY)
					{
					if(EVENT.Window = BottomWindow());
						{
						EVENT.Event = CHANGE_WINDOW;
						goto decision;
						}
					}
				} // end VIRGIN class
			else if(mode & REPORT_EXT_KEY)
				{
				/* user wants unused keys */
				EVENT.Event = EXT_KEY_EVENT;
				return(&EVENT);
				} // end user wants unused keys
			} // end extended character
		else
			{
			/* process normal key */
			EVENT.Event = ASCII_KEY;
			return(&EVENT);
			} // end process normal key
		} // end if keybd hit
	} // end continuous poll
}

/*************************************************************************
DoMenuBar       -       Do Menu Bar

Description     -       Handles user interaction with the
			menu bar starting with the given entry point
			by highlighting the menu title and then
			passing its Pop Up pointer to DoPopUp().

Parameters      -       DoMenuBar(window)
			Window_t *window

Returns         -       A pointer to the Event_t structure containing
			the last event details.
***************************************************************************/
static Event_t *DoMenuBar(Window_t *window)
{
Event_t *event;

for(;;)
	{
	ShowLine(window, 0); // turn on
	if(event = DoPopUp(TRUE, (Window_t *)window->ObjList->first->item))
		{
		switch(event->Event)
		{
		case DO_MENU_BAR:
			{
			ShowLine(window, 0); // turn off
			window = event->Window;
			}
		break;
		case CLOSE_WINDOW:
			{
			ShowLine(window, 0); // turn off
			return(NULL);
			}
		break;
		case EXT_KEY_EVENT:
			{
			if(event->Class == GREY_KEY)
				{
				switch(event->Key)
					{
			case LEFT:
			case SHIFT_TAB:
			case CTRL_LEFT:
				{
				ShowLine(window, 0); // turn off
				window = (Window_t *)
					window->Posn->prev->item;
				}
			break;
			case RIGHT:
			case CTRL_RIGHT:
				{
				ShowLine(window, 0); // turn off
				window = (Window_t *)
					window->Posn->next->item;
				}
			break;
			default:
				ShowLine(window, 0); // turn off
			break;
					} // end switch
				} // end if
			else
				ShowLine(window, 0); // turn off
			}
		break;
		case ASCII_KEY:
			{
			switch(event->Input)
				{
			case '\t':
				{
				ShowLine(window, 0); // turn off
				window = (Window_t *)
					window->Posn->next->item;
				}
			break;
			case '\033':
				{
				ShowLine(window, 0); // turn off
				return(NULL);
				}
			break;
			case '\b':
				{
				ShowLine(window, 0); // turn off
				window = (Window_t *)
					window->Posn->prev->item;
				}
			break;
			default:
				ShowLine(window, 0); // turn off
			break;
				}
			}
		break;
		case CHANGE_WINDOW:
		case EXIT_PROGRAM:
		case NEW_PROCESS:
			{
			ShowLine(window, 0); /* turn off */
			return(event);
			}
		break;
		default:
			ShowLine(window, 0); // turn off
		break;
		} // end switch
		} // end if
	else
		return(NULL); /* all these options are off */
	} // end continuous loop
}

/**************************************************************************
DoPopUp         -       Do Pop Up

Description     -       Handles user interaction with the Pop Up Menu
			by opening the window and highlighting the first
			option.

			If the side_exit parameter is FALSE
			then horizontal movements are ignored.
			If side_exit is TRUE, then a horizontal
			movement key results in the closing of the
			PopUpMenu and the function returns with the
			given event.  side_exit == TRUE implies a return to
			DoMenuBar().

Parameters      -       DoPopUp(side_exit, window)
			BOOLEAN side_exit;
			Window_t *window;

Returns         -       A pointer to an Event_t structure
***************************************************************************/
Event_t *DoPopUp(BOOLEAN side_exit, Window_t *window)
{
Option_t *option = (Option_t *)window->Contents;
Event_t *event;
int line = 0, first = -1, last = -1;

while(option[line].Title)
	{
	if( (option[line].Title[0] != '.') &&
		(option[line].State > 0) )
		{
		first = line;
		break;
		}
	else
		++line;
	}
if(first > -1)
	{
	++line;
	while(option[line].Title)
		{
		if( (option[line].Title[0] != '.') &&
			(option[line].State > 0) )
			last = line;
		++ line;
		}
	if(last == -1)
		last = first;
	}
else
	{
	/* all options are off */
	return(NULL);
	}
line = first;
window->Changed = TRUE;
OpenWindow(window);
for(;;)
	{
	ShowLine(window, line); // turn on
	event = GetEvent(ALLOW_ALTER_WINDOW | REPORT_EXT_KEY);
	switch(event->Event)
		{
		case ALTER_WINDOW:
			{
			if(event->RetValue < 2)
				ShowLine(window, line);
			}
		break;
		case CLOSE_WINDOW:
		case DO_MENU_BAR:
		case CHANGE_WINDOW:
			{
			goto end;
			}
		break;
		case EXT_KEY_EVENT:
			{
			if(event->Class == GREY_KEY)
				{
				switch(event->Key)
					{
			case SHIFT_TAB:
			case CTRL_LEFT:
			case CTRL_RIGHT:
			case LEFT:
			case RIGHT:
				{
				if(side_exit)
					{
					goto end;
					}
				}
			break;
			case UP: // previous option or last
				{
				ShowLine(window, line); // turn off

				do
					line = line ? line - 1 : last;
				while( (option[line].Title[0] == '.') ||
				(option[line].State <= 0) );
				}
			break;
			case DOWN: // next option
				{
				ShowLine(window, line); // turn off

				do
					line = line < last ? line + 1 : first;
				while( (option[line].Title[0] == '.') ||
				(option[line].State <= 0) );
				}
			break;
			case HOME:
			case PGUP:
			case CTRL_HOME:
			case CTRL_PGUP: // first option
				{
				ShowLine(window, line); // turn off
				line = first;
				}
			break;
			case PGDN:
			case END:
			case CTRL_END:
			case CTRL_PGDN: // last option
				{
				ShowLine(window, line); // turn on
				line = last;
				}
			break;
			default:
				ShowLine(window, line); // turn off
			break;
					} // end switch
				}
			else
				ShowLine(window, line); // turn off
			}
		break;
		case DO_LINE:
			{
			if(event->LineNo != line)
				{
				ShowLine(window, line); // turn off
				line = event->LineNo;
				}
			else
				{
				event->Event = NEW_PROCESS;
				event->TgtProcess =
					option[line].Code;
				event->StackSize =
					option[line].StackSize;
				goto end;
				}
			}
		break;
		case ASCII_KEY:
			{
			switch(event->Input)
				{
				case '\t':
				case '\b':
					{
					if(side_exit)
						{
						goto end;
						}
					}
				break;
				case '\033':
					{
					goto end;
					}
				break;
				case '\r':
					{
					event->Event = NEW_PROCESS;
					event->TgtProcess =
						option[line].Code;
					event->StackSize =
						option[line].StackSize;
					goto end;
					}
				break;
				default:
					ShowLine(window, line); //turn off
				break;
				} // end switch
			}
		break;
		default:
			ShowLine(window, line); // turn off
		break;
		} // end switch
	}
end:
CloseWindow(window);
return(event);
}

/**************************************************************************
DoObjects       -       Do Objects

Description     -       Handles user interaction with an object window
			by.

			The function assumes at least two buttons being
			present and corresponding to the CONTINUEfunc and
			CANCELfunc supplied. If the user selects either
			of these buttons the procedure terminates.

Parameters      -       DoObjects(window, start, mode)
			Window_t *window;
			llnode_t *start; the object to start at.
				   NULL indicates "as they come"
			unsigned int mode; permission for
			ALLOW_MENU_BAR and ALLOW_ALTER_WINDOW

Returns         -       A pointer to the terminating event or NULL
			if start state was off or all objects were off.
**************************************************************************/
Event_t *DoObjects(Window_t *window, llnode_t *start, unsigned int mode)
{
Event_t *event;
Window_t *objwindow;
llnode_t *first = NULL, *last = NULL, *next;
int fline, lline, line;

window->Changed = TRUE;
MakeLinear(window->ObjList);
for(next = window->ObjList->first; next != NULL; next = next->next)
	{
	switch( ((Window_t *)next->item)->CType )
		{
		case CODE_BUTTON:
			{
			Button_t *item = ((Window_t *)next->item)->Contents;

			if(item->State > 0)
				{
				first = next;
				fline = 0;
				goto end_for_loop;
				}
			}
		break;
		case SWITCH:
		case CHECK_BOX:
			{
			if(SwitchBoxFL(((Window_t *)next->item)->Contents,
				&fline, NULL))
				{
				first = next;
				goto end_for_loop;
				}
			}
		break;
		case STRING_D:
		case INT_D:
		case NUMBER_D:
		case DATE_D:
			{
			first = next;
			fline = 0;
			goto end_for_loop;
			}
		break;
		} // end switch
	}
end_for_loop:
if(first)
	{
	while(next = next->next)
		{
		switch( ((Window_t *)next->item)->CType )
		{
		case CODE_BUTTON:
			{
			Button_t *item = ((Window_t *)next->item)->Contents;

			if(item->State > 0)
				{
				last = next;
				lline = 0;
				}
			}
		break;
		case SWITCH:
		case CHECK_BOX:
			{
			if(SwitchBoxFL(((Window_t *)next->item)->Contents,
				NULL, &lline))
				{
				last = next;
				break;
				}
			}
		break;
		case STRING_D:
		case INT_D:
		case NUMBER_D:
		case DATE_D:
			{
			last = next;
			lline = 0;
			break;
			}
		break;
		} // end switch
		} // end while
	if(!last)
		{
		last = first;
		lline = fline;
		}
	} // end if first
else
	/* no ON states */
	{
	MakeCirc(window->ObjList);
	return(NULL);
	}
if(!start)
	{
	next = first;
	line = fline;
	}
else
	{
	/* user has requested an object to start at */
	for(next = window->ObjList->first; next != NULL; next = next->next)
		if(next == start)
			break;
	if(next == NULL)
		return(NULL); /* non existant start node */
	switch( ((Window_t *)next->item)->CType )
		{
		case CODE_BUTTON:
			{
			Button_t *item = ((Window_t *)next->item)->Contents;

			if(item->State > 0)
				line = 0;
			else
				return(NULL);
			}
		break;
		case SWITCH:
		case CHECK_BOX:
			{
			if(SwitchBoxFL(((Window_t *)next->item)->Contents,
				&fline, NULL))
				line = 0;
			else
				return(NULL);
			}
		break;
		case STRING_D:
		case INT_D:
		case NUMBER_D:
		case DATE_D:
			line = 0;
		break;
		} // end switch
	} // end else
MakeCirc(window->ObjList);
for(;;)
	{
	objwindow = (Window_t *)next->item;
	if(objwindow->CType == CODE_BUTTON)
		{
		event = DoButton(objwindow, mode);
		}
	else if(objwindow->CType == SWITCH)
		{
		event = DoSwitch(objwindow, line, mode);
		}
	else if(objwindow->CType == CHECK_BOX)
		{
		event = DoCheckBox(objwindow, line, mode);
		}

	else if(objwindow->CType == STRING_D)
		{
		event = DoDialogue(objwindow, mode);
		}
	else if(objwindow->CType == INT_D)
		{
		event = DoDialogue(objwindow, mode);
		}
	else if(objwindow->CType == NUMBER_D)
		{
		event = DoDialogue(objwindow, mode);
		}
	else if(objwindow->CType == DATE_D)
		{
		event = DoDialogue(objwindow, mode);
		}

	if(event)
		switch(event->Event)
		{
		case DO_SWITCH:
		case DO_CHECK_BOX:
		case DO_BUTTON:
		case DO_DIALOGUE:
			{
			next = event->Window->Posn;
			line = event->LineNo;
			}
		break;
		case EXT_KEY_EVENT:
			{
			if(event->Class == GREY_KEY)
				{
				switch(event->Key)
				{
		case CTRL_HOME:
		case CTRL_PGUP:
		case HOME:
		case PGUP:
			{
			next = first;
			line = fline;
			}
		break;
		case UP:
			{
			llnode_t *target = next;
			BOOLEAN done = FALSE;
			do
				{
				target = target->prev;

				if(IsAbove(next, target))
					{
					done = TRUE;
					break;
					}
				}
			while( (target != next) && (!done) );
			if(target == next)
				goto left_entry;
			else
				{
				next = target->next;
				goto left_entry;
				}
			}
		break;
		case DOWN:
			{
			llnode_t *target = next;
			BOOLEAN done = FALSE;

			do
				{
				target = target->next;

				if(IsBelow(next, target))
					{
					done = TRUE;
					break;
					}
				}
			while( (target != next) && (!done) );
			if(target == next)
				goto right_entry;
			else
				{
				next = target->prev;
				goto right_entry;
				}

			}
		break;
		case SHIFT_TAB:
		case LEFT:
		case CTRL_LEFT:
		    {
		    left_entry:
		    line = -1;
		    do
			{
			next = next->prev;
			switch(((Window_t *)next->item)->CType)
			{
			case CODE_BUTTON:
			{
			Button_t *item =
				((Window_t*)next->item)->Contents;
			if(item->State > 0)
				line = 0;
			}
			break;
			case SWITCH:
			case CHECK_BOX:
			{
			SwitchBoxFL(
			((Window_t *)next->item)->Contents, &line, NULL);
			}
			break;
			case STRING_D:
			case INT_D:
			case NUMBER_D:
			case DATE_D:
				line = 0;
			break;
			} // end switch
			} // end do
		    while(line == -1);
		    }
		break;
		case CTRL_RIGHT:
		case RIGHT:
			{
			right_entry:
			line = -1;
			do
				{
				next = next->next;
				switch(((Window_t *)next->item)->CType)
				{
				case CODE_BUTTON:
				{
				Button_t *item =
					((Window_t*)next->item)->Contents;
				if(item->State > 0)
					line = 0;
				}
				break;
				case SWITCH:
				case CHECK_BOX:
				{
				SwitchBoxFL(
				((Window_t *)next->item)->Contents,
					&line, NULL);
				}
				break;
				case STRING_D:
				case INT_D:
				case NUMBER_D:
				case DATE_D:
					line = 0;
				break;
				} // end switch
				} // end do
			while(line == -1);
			}
		break;
		case END:
		case PGDN:
		case CTRL_END:
		case CTRL_PGDN:
			{
			next = last;
			line = lline;
			}
		break;
		default:
		break;
				} // end switch
				} // end GREY_KEY
			} // end EXT_KEY_EVENT
		break;
		case ASCII_KEY:
			{
			switch(event->Input)
				{
				case '\033':
					{
					event->Event = CLOSE_WINDOW;
					goto end;
					}
				break;
				case '\t': goto right_entry;
				break;
				case '\b': goto left_entry;
				break;
				default:
				break;
				}
			}
		break;
		case CHANGE_WINDOW:
			{
			if(event->Window != window)
				goto end;
			else
				break;
			}
		break;
		case NEW_PROCESS:
		case CLOSE_WINDOW:
		case DO_MENU_BAR:
		case EXIT_PROGRAM:
		case BUTTON_VALUE:
			{
			goto end;
			}
		break;
		default:
		break;
		} // end switch
	}
end:
window->Changed = TRUE;
event->Node = next;
return(event);
}

/**************************************************************************
DoButton        -       Do Button

Description     -       Highlights the button and interacts with the
			user

Parameters      -       DoButton(window, mode)
			Window_t *window;
			unsigned int mode;

Returns         -       NOTHING.
***************************************************************************/
static Event_t *DoButton(Window_t *window, unsigned int mode)
{
Event_t *event;
int value;

for(;;)
	{
	ShowLine(window, 0); /* turn on */
	event = GetEvent(mode | REPORT_EXT_KEY);
	switch(event->Event)
		{
		case DO_BUTTON:
			{
			if(event->Window == window)
				{
				do_button_entry:

				ShowLine(window, 0); /* turn off */
				event->RetValue = ((Button_t *)
					window->Contents)->Code();
				event->Event = BUTTON_VALUE;
				return(event);
				}
			else
				{
				ShowLine(window, 0); /* turn off */
				return(event);
				}
			}
		case EXT_KEY_EVENT:
			{
			if(event->Class == GREY_KEY)
				{
				switch(event->Key)
					{
			case SHIFT_TAB:
			case HOME:
			case UP:
			case PGUP:
			case LEFT:
			case RIGHT:
			case END:
			case DOWN:
			case PGDN:
			case CTRL_LEFT:
			case CTRL_RIGHT:
			case CTRL_END:
			case CTRL_PGDN:
			case CTRL_HOME:
			case CTRL_PGUP:
				{
				ShowLine(window, 0); // turn off
				return(event);
				}
			break;
			default:
				ShowLine(window, 0); // turn off
			break;
					} // end switch
				} // end if Class == GREY_KEY
			else
				ShowLine(window, 0); // turn off
			}
		break;
		case ASCII_KEY:
			{
			switch(event->Input)
				{
			case '\r': goto do_button_entry;
			break;
			case '\t':
			case '\b':
			case '\033':
				{
				ShowLine(window, 0); // turn off
				return(event);
				}
			break;
			default:
				ShowLine(window, 0); // turn off
			break;
				} // end switch
			}
		break;
		case ALTER_WINDOW:
			{
			if(event->RetValue < 2)
				ShowLine(window, 0);
			}
		break;
		case DO_DIALOGUE:
		case DO_CHECK_BOX:
		case DO_SWITCH:
		case CLOSE_WINDOW:
		case CHANGE_WINDOW:
		case DO_LINE:
		case NEW_PROCESS:
		case EXIT_PROGRAM:
			{
			ShowLine(window, 0); /* turn off */
			return(event);
			}
		break;
		default:
			ShowLine(window, 0); // turn off
		break;
		} // end switch
	} // end continuous loop
}

/**************************************************************************
DoSwitch        -       Do Switch

Description     -       Highlights the given switch and interacts
			with the user.

Parameters      -       DoSwitch(window, line, mode)
			Window_t *window;
			int line; the number of lines from the first entry
			unsigned int mode;

Returns         -       NOTHING.
**************************************************************************/
static Event_t *DoSwitch(Window_t *window, int line, unsigned int mode)
{
Event_t *event;
int curline = 0, first, last;
Switch_t *item = (Switch_t *)window->Contents;

/* get first and last elements */
SwitchBoxFL(window->Contents, &first, &last);

/* locate the present setting */
while(item[curline].Mask != *item[curline].TempVar)
	++curline;

for(;;)
	{
	ShowLine(window, line); /* turn on */
	event = GetEvent(mode | REPORT_EXT_KEY);
	switch(event->Event)
		{
		case DO_SWITCH:
			{
			if(event->Window == window)
				{
				if(event->LineNo == line)
					{
					do_switch_entry:
					/* blank the present setting */
					ShowLine(window, line); // turn off
					ShowSBChar(window, curline, ' ');
					*item[curline].TempVar = 0;
					/* mark new setting */
					ShowSBChar(window, line, SW_BUTTON);
					*item[line].TempVar =
						item[line].Mask;
					curline = line;
					}
				else
					{
					ShowLine(window, line); // turn off
					line = event->LineNo;
					}
				}
			else
				{
				ShowLine(window, line); // turn off
				return(event);
				}
			}
		break;
		case EXT_KEY_EVENT:
			{
			if(event->Class == GREY_KEY)
				{
				switch(event->Key)
					{
			case UP:
				{
				ShowLine(window, line); // turn off
				do
				    line = line == first ? last : line - 1;
				while(item[line].State <= 0);
				}
			break;
			case DOWN:
				{
				ShowLine(window, line); // turn off
				do
				    line = line == last ? first : line + 1;
				while(item[line].State <= 0);
				}
			break;
			case HOME:
			case PGUP:
				{
				ShowLine(window, line); // turn off
				line = first;
				}
			break;
			case END:
			case PGDN:
				{
				ShowLine(window, line); // turn off
				line = last;
				}
			break;
			case CTRL_END:
			case CTRL_PGDN:
			case CTRL_HOME:
			case CTRL_PGUP:
			case SHIFT_TAB:
			case CTRL_LEFT:
			case LEFT:
			case CTRL_RIGHT:
			case RIGHT:
				{
				ShowLine(window, line); // turn off
				return(event);
				}
			break;
			default:
				ShowLine(window, line); // turn off
			break;
					}
				}
			else
				ShowLine(window, line); // turn off
			}
		break;
		case ASCII_KEY:
			{
			switch(event->Input)
				{
			case '\r':
				goto do_switch_entry;
			break;
			case '\t':
			case '\b':
			case '\033':
				{
				ShowLine(window, line); // turn off
				return(event);
				}
			break;
			default:
				ShowLine(window, line); // turn off
			break;
				}
			}
		break;
		case ALTER_WINDOW:
			{
			if(event->RetValue < 2)
				ShowLine(window, line);
			}
		break;
		case DO_DIALOGUE:
		case DO_CHECK_BOX:
		case DO_BUTTON:
		case CLOSE_WINDOW:
		case CHANGE_WINDOW:
		case DO_LINE:
		case NEW_PROCESS:
		case EXIT_PROGRAM:
			{
			ShowLine(window, line); // turn off
			return(event);
			}
		break;
		default:
			ShowLine(window, line); // turn off
		break;
		} // end switch
	} // end continuous loop
}

/**************************************************************************
DoCheckBox      -       Do Check Box

Description     -       Highlights the given box and interacts with the
			user

Parameters      -       DoCheckBox(window, line, mode)
			Window_t *window;
			int line; the number of lines from the first entry
			unsigned int mode;

Returns         -       NOTHING

**************************************************************************/
static Event_t *DoCheckBox(Window_t *window, int line, unsigned int mode)
{
Event_t *event;
int first, last;
CheckBox_t *item = (CheckBox_t *)window->Contents;
unsigned char ch;

/* get first and last elements */
SwitchBoxFL(window->Contents, &first, &last);

for(;;)
	{
	ShowLine(window, line); // turn on
	event = GetEvent(mode | REPORT_EXT_KEY);
	switch(event->Event)
		{
		case DO_CHECK_BOX:
			{
			if(event->Window == window)
				{
				if(event->LineNo == line)
					{
					do_check_box_entry:
					ShowLine(window, line); /// turn off
					if( (*item[line].TempVar &
						item[line].Mask) ==
						item[line].Mask )
						ch = ' ';
					else
						ch = 'X';
					*item[line].TempVar ^=
						item[line].Mask;
					ShowSBChar(window, line, ch);
					}
				else
					{
					ShowLine(window, line); // turn off
					line = event->LineNo;
					}
				}
			else
				{
				ShowLine(window, line); // turn off
				return(event);
				}
			}
		break;
		case EXT_KEY_EVENT:
			{
			if(event->Class == GREY_KEY)
				{
				switch(event->Key)
					{
			case UP:
				{
				ShowLine(window, line); // turn off
				do
				    line = line == first ? last : line - 1;
				while(item[line].State <= 0);
				}
			break;
			case DOWN:
				{
				ShowLine(window, line); // turn off
				do
				    line = line == last ? first : line + 1;
				while(item[line].State <= 0);
				}
			break;
			case HOME:
			case PGUP:
				{
				ShowLine(window, line); // turn off
				line = first;
				}
			break;
			case END:
			case PGDN:
				{
				ShowLine(window, line); // turn off
				line = last;
				}
			break;
			case CTRL_END:
			case CTRL_PGDN:
			case CTRL_HOME:
			case CTRL_PGUP:
			case SHIFT_TAB:
			case CTRL_LEFT:
			case LEFT:
			case CTRL_RIGHT:
			case RIGHT:
				{
				ShowLine(window, line); // turn off
				return(event);
				}
			break;
			default:
				ShowLine(window, line); // turn off
			break;
					} // end switch
				} // end if
			else
				ShowLine(window, line);
			} // end case
		break;
		case ASCII_KEY:
			{
			switch(event->Input)
				{
			case '\r':
				goto do_check_box_entry;
			break;
			case '\t':
			case '\b':
			case '\033':
				{
				ShowLine(window, line); // turn off
				return(event);
				}
			break;
			default:
				ShowLine(window, line); // turn off
			break;
				}
			}
		break;
		case ALTER_WINDOW:
			{
			if(event->RetValue < 2)
				ShowLine(window, line);
			}
		break;
		case DO_DIALOGUE:
		case CLOSE_WINDOW:
		case CHANGE_WINDOW:
		case DO_LINE:
		case DO_SWITCH:
		case DO_BUTTON:
		case NEW_PROCESS:
		case EXIT_PROGRAM:
			{
			ShowLine(window, line); // turn off
			return(event);
			}
		break;
		default:
			ShowLine(window, line); // turn off
		break;
		} // end switch
	} // end continuous loop
}

/*************************************************************************
AlterWindow     -       Alter Window

Description     -       Alters the position, and possible size of the
			active window by interacting with the user

Parameters      -       AlterWindow()

Returns         -       An int corresponding to the event that took place.
			0 indicates nothing happened. Set bits indicate
			the following:-
			.......x  The window was moved   (1)
			......x.  The window was resized (2)
**************************************************************************/
static int AlterWindow(void)
{
int state = 0, retvalue = 0, event = 0; /* nothing has happened yet */
Screen_t bounds = ACTWINDOW->Bounds;
unsigned brow, row, bcol, col;
int loc = 0, status, vertmove = 0, horzmove = 0;
unsigned char key, input;

vertmove = horzmove = 0;

/* check for minimum boundaries */
if(ACTWINDOW->MinBounds.TRow == 255)
	{
	/* we need to set them up */
	ACTWINDOW->MinBounds = ACTWINDOW->Bounds;
	if(ACTWINDOW->Title)
		ACTWINDOW->MinBounds.BCol = (unsigned char)
			((int)ACTWINDOW->MinBounds.TCol + (int)
			ACTWINDOW->TLength + 3);
	else
		ACTWINDOW->MinBounds.BCol = (unsigned char)
			((int)ACTWINDOW->MinBounds.TCol + 3);
	ACTWINDOW->MinBounds.BRow = (unsigned char)
		((int)ACTWINDOW->MinBounds.TRow + 2);
	}

if(ACTWINDOW->Border)
	{
	IncBounds(&ACTWINDOW->MinBounds);
	IncBounds(&bounds);
	}

ShowGhostW(&bounds); // turn on

if(MOUSE_SUPPORTED)
	{
	brow = (unsigned)(bounds.TRow +
		( (bounds.BRow - bounds.TRow) / 2) );
	bcol = (unsigned)(bounds.TCol +
		( (bounds.BCol - bounds.TCol) / 2) );
	PutMouse(brow, bcol);
	}

for(;;)
	{
	if(MOUSE_SUPPORTED)
		{
		GetMPosn(&row, &col);
		if( (row == brow) && (col == bcol) )
			; // no movement
		else
			{
			vertmove = (int)(row - brow);
			horzmove = (int)(col - bcol);
			brow = row;
			bcol = col;
			}
		if(GetMPress(0, &row, &col, &status))
			{
			event = 1; // new position marked by user
			goto decision;
			}
		else
			event = 0;
		}
	if(kbhit())
		{
		int class;
		unsigned char key, input;

		/* read the key */
		status = KBStatus();
		if(PCKey(&key, &input))
			{
			/* extended character */
			class = ExtCharClass(&key);
			if(class == GREY_KEY)
				{
				switch(key)
					{
		case SHIFT_TAB:
			{
			horzmove = -4;
			}
		break;
		case LEFT:
			{
			horzmove = -1;
			}
		break;
		case RIGHT:
			{
			horzmove = 1;
			}
		break;
		case HOME:
		case CTRL_HOME:
			{
			switch(loc)
				{
				case 0:
				case 1:
					{
					// screen home posn
					vertmove = 1 - (int)bounds.TRow;
					horzmove = -(int)bounds.TCol;
					}
				break;
				case 2:
					{
					// MinBounds loc 2
					vertmove = 1 - (int)bounds.TRow;
					horzmove =
					(int)ACTWINDOW->MinBounds.BCol -
					(int)bounds.BCol;
					}
				break;
				case 3:
					{
					// MinBounds.BRow, screen TCol
					horzmove = -(int)bounds.TCol;
					vertmove =
					(int)ACTWINDOW->MinBounds.BRow -
					(int)bounds.BRow;
					}
				break;
				case 4:
					{
					// MinBounds.BCol & BRow
					horzmove =
					(int)ACTWINDOW->MinBounds.BCol -
					(int)bounds.BCol;
					vertmove =
					(int)ACTWINDOW->MinBounds.BRow -
					(int)bounds.BRow;
					}
				break;
				}
			}
		break;
		case CTRL_PGUP:
		case PGUP:
			{
			switch(loc)
				{
				case 0:
				case 1:
				case 2:
					vertmove = 1 - (int)bounds.TRow;
				break;
				case 3:
				case 4:
					vertmove =
					(int)ACTWINDOW->MinBounds.BRow -
					(int)bounds.BRow;
				break;
				}
			}
		break;
		case UP:
			{
			vertmove = -1;
			}
		break;
		case DOWN:
			{
			vertmove = 1;
			}
		break;
		case END:
		case CTRL_END:
			{
			switch(loc)
				{
				case 0:
				case 4:
				{
				// screen END
				horzmove = (int)HOST_SCREEN.BCol -
					   (int)bounds.BCol;
				vertmove = (int)HOST_SCREEN.BRow -
					   (int)bounds.BRow;
				}
				break;
				case 1:
				{
				// MinBounds loc 1
				horzmove = (int)ACTWINDOW->MinBounds.TCol -
					   (int)bounds.TCol;
				vertmove = (int)ACTWINDOW->MinBounds.TRow -
					   (int)bounds.TRow;
				}
				break;
				case 2:
				{
				// MinBounds.TRow, screen.BCol
				horzmove = (int)HOST_SCREEN.BCol -
					   (int)bounds.BCol;
				vertmove = (int)ACTWINDOW->MinBounds.TRow -
					   (int)bounds.TRow;
				}
				break;
				case 3:
				{
				// MinBounds.TCol, screen.BRow
				horzmove = (int)ACTWINDOW->MinBounds.TCol -
					   (int)bounds.TCol;
				vertmove = (int)HOST_SCREEN.BRow -
					   (int)bounds.BRow;
				}
				break;
				}
			}
		break;
		case CTRL_PGDN:
		case PGDN:
			{
			switch(loc)
				{
				case 0:
				case 3:
				case 4:
					vertmove = (int)HOST_SCREEN.BRow -
						   (int)bounds.BRow;
				break;
				case 1:
				case 2:
					vertmove = ACTWINDOW->MinBounds.TRow
						- bounds.TRow;
				break;
				}
			}
		break;
		case CTRL_LEFT:
			{
			switch(loc)
				{
				case 0:
				case 1:
				case 3:
					horzmove = -(int)bounds.TCol;
				break;
				case 2:
				case 4:
					horzmove =
					(int)ACTWINDOW->MinBounds.BCol -
					(int)bounds.BCol;
				break;
				}
			}
		break;
		case CTRL_RIGHT:
			{
			switch(loc)
				{
				case 0:
				case 2:
				case 4:
					horzmove = (int)HOST_SCREEN.BCol -
						   (int)bounds.BCol;
				break;
				case 1:
				case 3:
					horzmove =
					(int)ACTWINDOW->MinBounds.TCol -
					(int)bounds.TCol;
				break;
				}
			}
		break;
					} // end switch
				} // end GREY_KEY class
			} // end extended character
		else
			{
			/* process normal key */
			switch(input)
				{
		case '\033':
			goto escape;
		break;
		case '\t':
			horzmove = 4;
		break;
		case '\b':
			horzmove = -1;
		break;
		case '\r':
			event = 1;
		break;
				} // end switch
			} // end process normal key
		}
	decision:
	switch(state)
		{
		case 0: // window can be moved
			{
			if(horzmove || vertmove)
				{
				ShowGhostW(&bounds); // turn off
				AdjBounds(&bounds, horzmove, vertmove);
				horzmove = vertmove = 0;
				ShowGhostW(&bounds); // turn on
				}
			if(event == 1)
				{
				event = 0;
				ShowGhostW(&bounds); // turn off
				MoveWindow(ACTWINDOW, &bounds);
				retvalue |= 1;
				/* prepare for sizing */
				/* locate best position for user */
				loc = BestSizingPosn(ACTWINDOW, &brow, &bcol);
				if(MOUSE_SUPPORTED)
					PutMouse(brow, bcol);
				ShowGhostW(&bounds); // turn on
				state = 1; // move is complete
				}
			}
		break;
		case 1: // window can be sized
			{
			if(horzmove || vertmove)
				{
				ShowGhostW(&bounds); // turn off
				ExpBounds(&bounds, &ACTWINDOW->MinBounds,
					horzmove, vertmove, loc);
				horzmove = vertmove = 0;
				ShowGhostW(&bounds); // turn on
				}
			if(event == 1)
				{
				ShowGhostW(&bounds); // turn off
				SizeWindow(ACTWINDOW, &bounds, loc);
				retvalue |= 2;
				if(ACTWINDOW->Border)
					DecBounds(&ACTWINDOW->MinBounds);
				return(retvalue); // sizing is complete
				}
			}
		break;
		}
	} // end continuous loop
escape:
ShowGhostW(&bounds);
if(ACTWINDOW->Border)
	DecBounds(&ACTWINDOW->MinBounds);
return(retvalue);
}

/**************************************************************************
MoveWindow      -       Move Window

Description     -       Changes the parent and adjusts all child window
			boundaries to reflect the given boundaries passed

Parameters      -       MoveWindow(window, bounds)
			Window_t *window;
			Screen_t *bounds;

Returns         -       NOTHING
**************************************************************************/
static void MoveWindow(Window_t *window, Screen_t *bounds)
{
int hmove, vmove;

/* what movement has taken place ? */

hmove = (int)(bounds->TCol - window->Bounds.TCol); // we'll add differences
vmove = (int)(bounds->TRow - window->Bounds.TRow);
if(window->Border)
	{
	++hmove;
	++vmove;
	}
/* change the window's cursor position */
window->CursPosn.Row = (unsigned char)((int)window->CursPosn.Row + vmove);
window->CursPosn.Col = (unsigned char)((int)window->CursPosn.Col + hmove);
window->Changed = TRUE;
CloseWindow(window);
MoveWBounds(window, hmove, vmove);
OpenWindow(window);
}

/**************************************************************************
MoveWBounds     -       Move Window Boundaries

Description     -       Recursively adds the total horizontal and
			vertical movements to each of the appropriate
			window coordinates

Parameters      -       MoveWBounds(window, hmove, vmove)
			Window_t *window;
			int hmove, vmove;

Returns         -       NOTHING
***************************************************************************/
static void MoveWBounds(Window_t *window, int hmoves, int vmoves)
{
llnode_t *next = NULL;

if(window->ObjList)
	{
	MakeLinear(window->ObjList);
	next = window->ObjList->first;
	}

/* process the parent window */

window->Bounds.TRow = (unsigned char)((int)window->Bounds.TRow + vmoves);
window->Bounds.BRow = (unsigned char)((int)window->Bounds.BRow + vmoves);
window->Bounds.TCol = (unsigned char)((int)window->Bounds.TCol + hmoves);
window->Bounds.BCol = (unsigned char)((int)window->Bounds.BCol + hmoves);
window->MinBounds.TRow = (unsigned char)
	((int)window->MinBounds.TRow + vmoves);
window->MinBounds.BRow = (unsigned char)
	((int)window->MinBounds.BRow + vmoves);
window->MinBounds.TCol = (unsigned char)
	((int)window->MinBounds.TCol + hmoves);
window->MinBounds.BCol = (unsigned char)
	((int)(window->MinBounds.BCol + hmoves));

if(window->CType > CHECK_BOX)
	{
	/* we adjust the Dialogue box's cursor location */
	window->CursPosn.Row = (unsigned char)
		((int)window->CursPosn.Row + vmoves);
	window->CursPosn.Col = (unsigned char)
		((int)window->CursPosn.Col + hmoves);
	/* and the title's start column */
	window->TOffset = window->Bounds.TCol;
	}

/* now process the object list */
if(next)
	{
	while(next)
		{
		MoveWBounds((Window_t *)next->item, hmoves, vmoves);
		next = next->next;
		}
	MakeCirc(window->ObjList);
	}
}

/**************************************************************************
SizeWindow      -       Size Window

Description     -       Changes the parent and adjusts all child window
			boundaries to reflect the given boundaries passed

Parameters      -       SizeWindow(window, bounds, loc)
			Window_t *window;
			Screen_t *bounds;
			int loc; location at which sizing took place

Returns         -       NOTHING
**************************************************************************/
static void SizeWindow(Window_t *window, Screen_t *bounds, int loc)
{
int hmoves, vmoves;

if(window->Border)
	DecBounds(bounds);

CloseWindow(window);

switch(loc)
	{
	case 1: // top left
		{
		hmoves = (int)bounds->TCol - (int)window->Bounds.TCol;
		vmoves = (int)bounds->TRow - (int)window->Bounds.TRow;
		window->Bounds.TRow = (unsigned char)
			((int)window->Bounds.TRow + vmoves);
		window->Bounds.TCol = (unsigned char)
			((int)window->Bounds.TCol + hmoves);
		}
	break;
	case 2: // top right
		{
		hmoves = (int)bounds->BCol - (int)window->Bounds.BCol;
		vmoves = (int)bounds->TRow - (int)window->Bounds.TRow;
		window->Bounds.TRow = (unsigned char)
			((int)window->Bounds.TRow + vmoves);
		window->Bounds.BCol = (unsigned char)
			((int)window->Bounds.BCol + hmoves);
		}
	break;
	case 3: // bottom left
		{
		hmoves = (int)bounds->TCol - (int)window->Bounds.TCol;
		vmoves = (int)bounds->BRow - (int)window->Bounds.BRow;
		window->Bounds.BRow = (unsigned char)
			((int)window->Bounds.BRow + vmoves);
		window->Bounds.TCol = (unsigned char)
			((int)window->Bounds.TCol + hmoves);
		}
	break;
	case 4: // bottom right
		{
		hmoves = (int)bounds->BCol - (int)window->Bounds.BCol;
		vmoves = (int)bounds->BRow - (int)window->Bounds.BRow;
		window->Bounds.BRow = (unsigned char)
			((int)window->Bounds.BRow + vmoves);
		window->Bounds.BCol = (unsigned char)
			((int)window->Bounds.BCol + hmoves);
		}
	break;
	}
NameWindow(window, window->Title);
/* adjust cursor position */
window->CursPosn.Row = (unsigned char)((int)window->CursPosn.Row + vmoves);
window->CursPosn.Col = (unsigned char)((int)window->CursPosn.Col + hmoves);
if(!ClipCode(window->CursPosn.Row, window->CursPosn.Col, &window->Bounds))
	; /* do nothing */
else
	{
	/* reset to home posn */
	window->CursPosn.Row = window->Bounds.TRow;
	window->CursPosn.Col = window->Bounds.TCol;
	}
window->Changed = TRUE;
OpenWindow(window);
}

/**************************************************************************
CONTINUEfunc    -       Continue function

Description     -       Button routine which is responsible for
			updating program variables from their
			existing temporary settings

Parameters      -       CONTINUEfunc()

Returns         -       -1
***************************************************************************/
int CONTINUEfunc(void)
{
Window_t *window;
llnode_t *next = ACTWINDOW->ObjList->first;

MakeLinear(ACTWINDOW->ObjList);
while(next)
	{
	window = (Window_t *)next->item;
	switch(window->CType)
		{
		case SWITCH:
		case CHECK_BOX:
			{
			Switch_t *ptr = (Switch_t *)window->Contents;

			while(*ptr->Title)
				{
				*ptr->Var = *ptr->TempVar;
				++ ptr;
				}
			}
		break;
		}
	next = next->next;
	}
MakeCirc(window->ObjList);
return(-1);
}

/**************************************************************************
CANCELfunc      -       Cancel Function

Description     -       Button function responsible for re setting all
			temporary variables to their current values

Parameters      -       CANCELfunc()

Returns         -       0
**************************************************************************/
int CANCELfunc(void)
{
Window_t *window;
llnode_t *next = ACTWINDOW->ObjList->first;

MakeLinear(ACTWINDOW->ObjList);
while(next)
	{
	window = (Window_t *)next->item;
	switch(window->CType)
		{
		case SWITCH:
		case CHECK_BOX:
			{
			Switch_t *ptr = (Switch_t *)window->Contents;

			while(*ptr->Title)
				{
				*ptr->TempVar = *ptr->Var;
				++ ptr;
				}
			}
		break;
		}
	next = next->next;
	}
MakeCirc(window->ObjList);
return(0);
}

/*************************************************************************
SwitchBoxFL     -       Switch Box OK

Description     -       Checks the given array to ensure at least
			one element's state is ON and sets the
			two supplied parameters to the first and
			last element values as appropriate

Parameters      -       SwitchBoxFL(ptr, first, last)
			void *ptr; to array
			int *first, *last; for setting

Returns         -       TRUE or FALSE
**************************************************************************/
static BOOLEAN SwitchBoxFL(void *ptr, int *first, int *last)
{
int line = 0, fline, lline;
Switch_t *item = (Switch_t *)ptr;

fline = lline = -1;
while(item[line].Title)
	if(item[line].State > 0)
		{
		fline = line;
		break;
		}
	else
		++ line;
if(fline > -1)
	{
	++ line;
	while(item[line].Title)
		{
		if(item[line].State > 0)
			lline = line;
		++ line;
		}
	if(lline == -1)
		lline = fline;
	if(first)
		*first = fline;
	if(last)
		*last = lline;
	return(TRUE);
	}
else
	return(FALSE);
}
