/*
	This file is part of Warzone 2100.
	Copyright (C) 1999-2004  Eidos Interactive
	Copyright (C) 2005-2007  Warzone Resurrection Project

	Warzone 2100 is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	Warzone 2100 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Warzone 2100; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
 * Form.c
 *
 * Functionality for the form widget.
 */

#include "lib/framework/frame.h"
#include "widget.h"
#include "widgint.h"
#include "form.h"
#include "tip.h"
// FIXME Direct iVis implementation include!
#include "lib/ivis_common/rendmode.h"
#include "lib/ivis_common/piepalette.h"

/* The widget heaps */
OBJ_HEAP	*psFormHeap;
OBJ_HEAP	*psCFormHeap;
OBJ_HEAP	*psTFormHeap;

/* Control whether single tabs are displayed */
#define NO_DISPLAY_SINGLE_TABS 1

static void formFreeTips(W_TABFORM *psForm);

/* Store the position of a tab */
typedef struct _tab_pos
{
	SDWORD		index;
	SDWORD		x,y;
	UDWORD		width,height;
} TAB_POS;

/* Set default colours for a form */
static void formSetDefaultColours(W_FORM *psForm)
{
	static BOOL bDefaultsSet = FALSE;
	static UBYTE wcol_bkgrnd;
	static SDWORD wcol_text;
	static UBYTE wcol_light;
	static UBYTE wcol_dark;
	static UBYTE wcol_hilite;
	static UBYTE wcol_cursor;
	static UBYTE wcol_tipbkgrnd;
	static UBYTE wcol_disable;

	if (bDefaultsSet)
	{
		psForm->aColours[WCOL_BKGRND]    = wcol_bkgrnd;
		psForm->aColours[WCOL_TEXT]      = wcol_text;
		psForm->aColours[WCOL_LIGHT]     = wcol_light;
		psForm->aColours[WCOL_DARK]      = wcol_dark;
		psForm->aColours[WCOL_HILITE]    = wcol_hilite;
		psForm->aColours[WCOL_CURSOR]    = wcol_cursor;
		psForm->aColours[WCOL_TIPBKGRND] = wcol_tipbkgrnd;
		psForm->aColours[WCOL_DISABLE]   = wcol_disable;
	}
	else
	{
		wcol_bkgrnd    = (UBYTE)pal_GetNearestColour(0x7f,0x7f,0x7f);
		wcol_text      = -1;
		wcol_light     = (UBYTE)pal_GetNearestColour(0xff,0xff,0xff);
		wcol_dark      = (UBYTE)pal_GetNearestColour(0,0,0);
		wcol_hilite    = (UBYTE)pal_GetNearestColour(0x40,0x40,0x40);
		wcol_cursor    = (UBYTE)pal_GetNearestColour(0xff,0x00,0x00);
		wcol_tipbkgrnd = (UBYTE)pal_GetNearestColour(0x30,0x30,0x60);
		wcol_disable   = (UBYTE)pal_GetNearestColour(0xbf,0xbf,0xbf);

		bDefaultsSet   = TRUE;

		psForm->aColours[WCOL_BKGRND]    = wcol_bkgrnd;
		psForm->aColours[WCOL_TEXT]      = wcol_text;
		psForm->aColours[WCOL_LIGHT]     = wcol_light;
		psForm->aColours[WCOL_DARK]      = wcol_dark;
		psForm->aColours[WCOL_HILITE]    = wcol_hilite;
		psForm->aColours[WCOL_CURSOR]    = wcol_cursor;
		psForm->aColours[WCOL_TIPBKGRND] = wcol_tipbkgrnd;
		psForm->aColours[WCOL_DISABLE]   = wcol_disable;

	}
}

/* Create a plain form widget */
static BOOL formCreatePlain(W_FORM **ppsWidget, W_FORMINIT *psInit)
{
	/* Allocate the required memory */
#if W_USE_MALLOC
	*ppsWidget = (W_FORM *)MALLOC(sizeof(W_FORM));
	if (*ppsWidget == NULL)
#else
	if (!HEAP_ALLOC(psFormHeap, (void*) ppsWidget))
#endif
	{
		ASSERT( FALSE, "formCreatePlain: Out of memory" );
		return FALSE;
	}

	/* Initialise the structure */
	memset(*ppsWidget, 0, sizeof(W_FORM));
	(*ppsWidget)->type = WIDG_FORM;
	(*ppsWidget)->id = psInit->id;
	(*ppsWidget)->formID = psInit->formID;
	(*ppsWidget)->style = psInit->style;
	(*ppsWidget)->disableChildren = psInit->disableChildren;
	(*ppsWidget)->animCount = 0;
	(*ppsWidget)->x = psInit->x;
	(*ppsWidget)->y = psInit->y;
	(*ppsWidget)->width = psInit->width;
	(*ppsWidget)->height = psInit->height;

	if (psInit->pDisplay)
	{
		(*ppsWidget)->display = psInit->pDisplay;
	}
	else
	{
		(*ppsWidget)->display = formDisplay;
	}
	(*ppsWidget)->callback = psInit->pCallback;
	(*ppsWidget)->pUserData = psInit->pUserData;
	(*ppsWidget)->UserData = psInit->UserData;
	(*ppsWidget)->psWidgets = NULL;
	(*ppsWidget)->psLastHiLite = NULL;
	formSetDefaultColours(*ppsWidget);

	formInitialise(*ppsWidget);

	return TRUE;
}


/* Free a plain form widget */
static void formFreePlain(W_FORM *psWidget)
{
	ASSERT( PTRVALID(psWidget, sizeof(W_FORM)),
		"formFreePlain: Invalid form pointer" );

	widgReleaseWidgetList(psWidget->psWidgets);
#if W_USE_MALLOC
	FREE(psWidget);
#else
	HEAP_FREE(psFormHeap, psWidget);
#endif
}


/* Create a plain form widget */
static BOOL formCreateClickable(W_CLICKFORM **ppsWidget, W_FORMINIT *psInit)
{
	/* Allocate the required memory */
#if W_USE_MALLOC
	*ppsWidget = (W_CLICKFORM *)MALLOC(sizeof(W_CLICKFORM));
	if (*ppsWidget == NULL)
#else
	if (!HEAP_ALLOC(psCFormHeap, (void*) ppsWidget))
#endif
	{
		ASSERT( FALSE, "formCreateClickable: Out of memory" );
		return FALSE;
	}

	/* Initialise the structure */
	memset(*ppsWidget, 0, sizeof(W_CLICKFORM));
	(*ppsWidget)->type = WIDG_FORM;
	(*ppsWidget)->id = psInit->id;
	(*ppsWidget)->formID = psInit->formID;
	(*ppsWidget)->style = psInit->style;
	(*ppsWidget)->disableChildren = psInit->disableChildren;
	(*ppsWidget)->animCount = 0;
	(*ppsWidget)->x = psInit->x;
	(*ppsWidget)->y = psInit->y;
	(*ppsWidget)->width = psInit->width;
	(*ppsWidget)->height = psInit->height;
	(*ppsWidget)->callback = psInit->pCallback;
	(*ppsWidget)->pUserData = psInit->pUserData;
	(*ppsWidget)->UserData = psInit->UserData;

	(*ppsWidget)->AudioCallback = WidgGetAudioCallback();
	(*ppsWidget)->HilightAudioID = WidgGetHilightAudioID();
	(*ppsWidget)->ClickedAudioID = WidgGetClickedAudioID();

	if (psInit->pDisplay)
	{
		(*ppsWidget)->display = psInit->pDisplay;
	}
	else
	{
		(*ppsWidget)->display = formDisplayClickable;
	}
	(*ppsWidget)->psWidgets = NULL;
	(*ppsWidget)->psLastHiLite = NULL;
	if (psInit->pTip)
	{
#if W_USE_STRHEAP
		if (!widgAllocCopyString(&(*ppsWidget)->pTip, psInit->pTip))
		{
			ASSERT( FALSE, "formCreateClickable: Out of string memory" );
#if W_USE_MALLOC
			FREE(*ppsWidget);
#else
			HEAP_FREE(psCFormHeap, *ppsWidget);
#endif
			return FALSE;
		}
#endif
		(*ppsWidget)->pTip = psInit->pTip;
	}
	else
	{
		(*ppsWidget)->pTip = NULL;
	}
	formSetDefaultColours((W_FORM *)*ppsWidget);

	formInitialise((W_FORM *)*ppsWidget);

	return TRUE;
}


/* Free a plain form widget */
static void formFreeClickable(W_CLICKFORM *psWidget)
{
	ASSERT( PTRVALID(psWidget, sizeof(W_FORM)),
		"formFreePlain: Invalid form pointer" );

	widgReleaseWidgetList(psWidget->psWidgets);
#if W_USE_STRHEAP
	if (psWidget->pTip)
	{
		widgFreeString(psWidget->pTip);
	}
#endif

#if W_USE_MALLOC
	FREE(psWidget);
#else
	HEAP_FREE(psCFormHeap, psWidget);
#endif
}


/* Create a tabbed form widget */
static BOOL formCreateTabbed(W_TABFORM **ppsWidget, W_FORMINIT *psInit)
{
	UDWORD		major,minor;
	W_MAJORTAB	*psMajor;

	if (psInit->numMajor == 0)
	{
		ASSERT( FALSE, "formCreateTabbed: Must have at least one major tab on a tabbed form" );
		return FALSE;
	}
	if (psInit->majorPos != 0 && psInit->majorPos == psInit->minorPos)
	{
		ASSERT( FALSE, "formCreateTabbed: Cannot have major and minor tabs on same side" );
		return FALSE;
	}
	if (psInit->numMajor >= WFORM_MAXMAJOR)
	{
		ASSERT( FALSE, "formCreateTabbed: Too many Major tabs" );
		return FALSE;
	}
	for(major=0; major<psInit->numMajor; major++)
	{
		if (psInit->aNumMinors[major] >= WFORM_MAXMINOR)
		{
			ASSERT( FALSE, "formCreateTabbed: Too many Minor tabs for Major %d", major );
			return FALSE;
		}
		if (psInit->aNumMinors[major] == 0)
		{
			ASSERT( FALSE, "formCreateTabbed: Must have at least one Minor tab for each major" );
			return FALSE;
		}
	}

	/* Allocate the required memory */
#if W_USE_MALLOC
	*ppsWidget = (W_TABFORM *)MALLOC(sizeof(W_TABFORM));
	if (*ppsWidget == NULL)
#else
	if (!HEAP_ALLOC(psTFormHeap, (void*) ppsWidget))
#endif
	{
		ASSERT( FALSE, "formCreateTabbed: Out of memory" );
		return FALSE;
	}
	memset(*ppsWidget, 0, sizeof(W_TABFORM));

	/* Allocate the memory for tool tips and copy them in */
	psMajor = (*ppsWidget)->asMajor;
	for(major=0; major<psInit->numMajor; major++)
	{
		/* Check for a tip for the major tab */
		if (psInit->apMajorTips[major])
		{
#if W_USE_STRHEAP
			(void)widgAllocCopyString(&psMajor->pTip, psInit->apMajorTips[major]);
#else
			psMajor->pTip = psInit->apMajorTips[major];
#endif
		}
		/* Check for tips for the minor tab */
		for(minor=0; minor<psInit->aNumMinors[major]; minor++)
		{
			if (psInit->apMinorTips[major][minor])
			{
#if W_USE_STRHEAP
				(void)widgAllocCopyString(&(psMajor->asMinor[minor].pTip),
										  psInit->apMinorTips[major][minor]);
#else
				psMajor->asMinor[minor].pTip = psInit->apMinorTips[major][minor];
#endif
			}
		}
		psMajor++;
	}

	/* Initialise the structure */
	(*ppsWidget)->type = WIDG_FORM;
	(*ppsWidget)->id = psInit->id;
	(*ppsWidget)->formID = psInit->formID;
	(*ppsWidget)->style = psInit->style;
	(*ppsWidget)->disableChildren = psInit->disableChildren;
	(*ppsWidget)->animCount = 0;
	(*ppsWidget)->x = psInit->x;
	(*ppsWidget)->y = psInit->y;
	(*ppsWidget)->width = psInit->width;
	(*ppsWidget)->height = psInit->height;
	if (psInit->pDisplay)
	{
		(*ppsWidget)->display = psInit->pDisplay;
	}
	else
	{
		(*ppsWidget)->display = formDisplayTabbed;
	}
	(*ppsWidget)->callback = psInit->pCallback;
	(*ppsWidget)->pUserData = psInit->pUserData;
	(*ppsWidget)->UserData = psInit->UserData;
	(*ppsWidget)->psLastHiLite = NULL;
	(*ppsWidget)->majorSize = psInit->majorSize;
	(*ppsWidget)->minorSize = psInit->minorSize;
	(*ppsWidget)->tabMajorThickness = psInit->tabMajorThickness;
	(*ppsWidget)->tabMinorThickness = psInit->tabMinorThickness;
	(*ppsWidget)->tabMajorGap = psInit->tabMajorGap;
	(*ppsWidget)->tabMinorGap = psInit->tabMinorGap;
	(*ppsWidget)->tabVertOffset = psInit->tabVertOffset;
	(*ppsWidget)->tabHorzOffset = psInit->tabHorzOffset;
	(*ppsWidget)->majorOffset = psInit->majorOffset;
	(*ppsWidget)->minorOffset = psInit->minorOffset;
	(*ppsWidget)->majorPos = psInit->majorPos;
	(*ppsWidget)->minorPos = psInit->minorPos;
	(*ppsWidget)->pTabDisplay = psInit->pTabDisplay;
	(*ppsWidget)->pFormDisplay = psInit->pFormDisplay;

	formSetDefaultColours((W_FORM *)(*ppsWidget));

	/* Set up the tab data.
	 * All widget pointers have been zeroed by the memset above.
	 */
	(*ppsWidget)->numMajor = psInit->numMajor;
	for (major=0; major<psInit->numMajor; major++)
	{
		(*ppsWidget)->asMajor[major].numMinor = psInit->aNumMinors[major];
	}

	formInitialise((W_FORM *)*ppsWidget);

	return TRUE;
}

/* Free the tips strings for a tabbed form */
static void formFreeTips(W_TABFORM *psForm)
{
#if W_USE_STRHEAP
	UDWORD		minor,major;
	W_MAJORTAB	*psMajor;

	psMajor = psForm->asMajor;
	for(major = 0; major < psForm->numMajor; major++)
	{
		if (psMajor->pTip)
		{
			widgFreeString(psMajor->pTip);
		}
		for(minor = 0; minor < psMajor->numMinor; minor++)
		{
			if (psMajor->asMinor[minor].pTip)
			{
				widgFreeString(psMajor->asMinor[minor].pTip);
			}
		}
		psMajor++;
	}
#else
	psForm = psForm;
#endif
}

/* Free a tabbed form widget */
static void formFreeTabbed(W_TABFORM *psWidget)
{
	WIDGET			*psCurr;
	W_FORMGETALL	sGetAll;

	ASSERT( PTRVALID(psWidget, sizeof(W_TABFORM)),
		"formFreeTabbed: Invalid form pointer" );

	formFreeTips(psWidget);

	formInitGetAllWidgets((W_FORM *)psWidget,&sGetAll);
	psCurr = formGetAllWidgets(&sGetAll);
	while (psCurr)
	{
		widgReleaseWidgetList(psCurr);
		psCurr = formGetAllWidgets(&sGetAll);
	}
#if W_USE_MALLOC
	FREE(psWidget);
#else
	HEAP_FREE(psTFormHeap, psWidget);
#endif
}


/* Create a form widget data structure */
BOOL formCreate(W_FORM **ppsWidget, W_FORMINIT *psInit)
{
	/* Check the style bits are OK */
	if (psInit->style & ~(WFORM_TABBED | WFORM_INVISIBLE | WFORM_CLICKABLE |
						  WFORM_NOCLICKMOVE | WFORM_NOPRIMARY | WFORM_SECONDARY |
						  WIDG_HIDDEN))
	{
		ASSERT( FALSE, "formCreate: Unknown style bit" );
		return FALSE;
	}
	if ((psInit->style & WFORM_TABBED) &&
		(psInit->style & (WFORM_INVISIBLE | WFORM_CLICKABLE)))
	{
		ASSERT( FALSE, "formCreate: Tabbed form cannot be invisible or clickable" );
		return FALSE;
	}
	if ((psInit->style & WFORM_INVISIBLE) &&
		(psInit->style & WFORM_CLICKABLE))
	{
		ASSERT( FALSE, "formCreate: Cannot have an invisible clickable form" );
		return FALSE;
	}
	if (!(psInit->style & WFORM_CLICKABLE) &&
		((psInit->style & WFORM_NOPRIMARY) || (psInit->style & WFORM_SECONDARY)))
	{
		ASSERT( FALSE, "formCreate: Cannot set keys if the form isn't clickable" );
		return FALSE;
	}

	/* Create the correct type of form */
	if (psInit->style & WFORM_TABBED)
	{
		return formCreateTabbed((W_TABFORM **)ppsWidget, psInit);
	}
	else if (psInit->style & WFORM_CLICKABLE)
	{
		return formCreateClickable((W_CLICKFORM **)ppsWidget, psInit);
	}
	else
	{
		return formCreatePlain(ppsWidget, psInit);
	}

	return FALSE;
}


/* Free the memory used by a form */
void formFree(W_FORM *psWidget)
{
	if (psWidget->style & WFORM_TABBED)
	{
		formFreeTabbed((W_TABFORM *)psWidget);
	}
	else if (psWidget->style & WFORM_CLICKABLE)
	{
		formFreeClickable((W_CLICKFORM *)psWidget);
	}
	else
	{
		formFreePlain(psWidget);
	}
}


/* Add a widget to a form */
BOOL formAddWidget(W_FORM *psForm, WIDGET *psWidget, W_INIT *psInit)
{
	W_TABFORM	*psTabForm;
	WIDGET		**ppsList;
	W_MAJORTAB	*psMajor;

	ASSERT( PTRVALID(psWidget, sizeof(WIDGET)),
		"formAddWidget: Invalid widget pointer" );

	if (psForm->style & WFORM_TABBED)
	{
		ASSERT( PTRVALID(psForm, sizeof(W_TABFORM)),
			"formAddWidget: Invalid tab form pointer" );
		psTabForm = (W_TABFORM *)psForm;
		if (psInit->majorID >= psTabForm->numMajor)
		{
			ASSERT( FALSE, "formAddWidget: Major tab does not exist" );
			return FALSE;
		}
		psMajor = psTabForm->asMajor + psInit->majorID;
		if (psInit->minorID >= psMajor->numMinor)
		{
			ASSERT( FALSE, "formAddWidget: Minor tab does not exist" );
			return FALSE;
		}
		ppsList = &(psMajor->asMinor[psInit->minorID].psWidgets);
		psWidget->psNext = *ppsList;
		*ppsList = psWidget;
	}
	else
	{
		ASSERT( PTRVALID(psForm, sizeof(W_FORM)),
			"formAddWidget: Invalid form pointer" );
		psWidget->psNext = psForm->psWidgets;
		psForm->psWidgets = psWidget;
	}

	return TRUE;
}


/* Get the button state of a click form */
UDWORD formGetClickState(W_CLICKFORM *psForm)
{
	UDWORD State = 0;

	if (psForm->state & WCLICK_GREY)
	{
		State |= WBUT_DISABLE;
	}

	if (psForm->state & WCLICK_LOCKED)
	{
		State |= WBUT_LOCK;
	}

	if (psForm->state & WCLICK_CLICKLOCK)
	{
		State |= WBUT_CLICKLOCK;
	}

	return State;
}


/* Set the button state of a click form */
void formSetClickState(W_CLICKFORM *psForm, UDWORD state)
{
	ASSERT( !((state & WBUT_LOCK) && (state & WBUT_CLICKLOCK)),
		"widgSetButtonState: Cannot have WBUT_LOCK and WBUT_CLICKLOCK" );

	if (state & WBUT_DISABLE)
	{
		psForm->state |= WCLICK_GREY;
	}
	else
	{
		psForm->state &= ~WCLICK_GREY;
	}
	if (state & WBUT_LOCK)
	{
		psForm->state |= WCLICK_LOCKED;
	}
	else
	{
		psForm->state &= ~WCLICK_LOCKED;
	}
	if (state & WBUT_CLICKLOCK)
	{
		psForm->state |= WCLICK_CLICKLOCK;
	}
	else
	{
		psForm->state &= ~WCLICK_CLICKLOCK;
	}
}

/* Return the widgets currently displayed by a form */
WIDGET *formGetWidgets(W_FORM *psWidget)
{
	W_TABFORM	*psTabForm;
	W_MAJORTAB	*psMajor;

	if (psWidget->style & WFORM_TABBED)
	{
		psTabForm = (W_TABFORM *)psWidget;
		psMajor = psTabForm->asMajor + psTabForm->majorT;
		return psMajor->asMinor[psTabForm->minorT].psWidgets;
	}
	else
	{
		return psWidget->psWidgets;
	}
}


/* Initialise the formGetAllWidgets function */
void formInitGetAllWidgets(W_FORM *psWidget, W_FORMGETALL *psCtrl)
{
	if (psWidget->style & WFORM_TABBED)
	{
		psCtrl->psGAWList = NULL;
		psCtrl->psGAWForm = (W_TABFORM *)psWidget;
		psCtrl->psGAWMajor = psCtrl->psGAWForm->asMajor;
		psCtrl->GAWMajor = 0;
		psCtrl->GAWMinor = 0;
	}
	else
	{
		psCtrl->psGAWList = psWidget->psWidgets;
		psCtrl->psGAWForm = NULL;
	}
}


/* Repeated calls to this function will return widget lists
 * until all widgets in a form have been returned.
 * When a NULL list is returned, all widgets have been seen.
 */
WIDGET *formGetAllWidgets(W_FORMGETALL *psCtrl)
{
	WIDGET	*psRetList;

	if (psCtrl->psGAWForm == NULL)
	{
		/* Not a tabbed form, return the list */
		psRetList = psCtrl->psGAWList;
		psCtrl->psGAWList = NULL;
	}
	else
	{
		/* Working with a tabbed form - search for the first widget list */
		psRetList = NULL;
		while (psRetList == NULL && psCtrl->GAWMajor < psCtrl->psGAWForm->numMajor)
		{
			psRetList = psCtrl->psGAWMajor->asMinor[psCtrl->GAWMinor++].psWidgets;
			if (psCtrl->GAWMinor >= psCtrl->psGAWMajor->numMinor)
			{
				psCtrl->GAWMinor = 0;
				psCtrl->GAWMajor ++;
				psCtrl->psGAWMajor ++;
			}
		}
	}

	return psRetList;
}


/* Set the current tabs for a tab form */
void widgSetTabs(W_SCREEN *psScreen, UDWORD id, UWORD major, UWORD minor)
{
	W_TABFORM	*psForm;

	psForm = (W_TABFORM *)widgGetFromID(psScreen, id);
	if (psForm == NULL || !(psForm->style & WFORM_TABBED))
	{
		ASSERT( FALSE,"widgSetTabs: couldn't find tabbed form from id" );
		return;
	}
	ASSERT( PTRVALID(psForm, sizeof(W_TABFORM)),
		"widgSetTabs: Invalid tab form pointer" );

	if (major >= psForm->numMajor ||
		minor >= psForm->asMajor[major].numMinor)
	{
		ASSERT( FALSE, "widgSetTabs: invalid major or minor id" );
		return;
	}

	psForm->majorT = major;
	psForm->minorT = minor;
	psForm->asMajor[major].lastMinor = minor;
}


/* Get the current tabs for a tab form */
void widgGetTabs(W_SCREEN *psScreen, UDWORD id, UWORD *pMajor, UWORD *pMinor)
{
	W_TABFORM	*psForm;

	psForm = (W_TABFORM *)widgGetFromID(psScreen, id);
	if (psForm == NULL || psForm->type != WIDG_FORM ||
		!(psForm->style & WFORM_TABBED))
	{
		ASSERT( FALSE,"widgGetTabs: couldn't find tabbed form from id" );
		return;
	}
	ASSERT( PTRVALID(psForm, sizeof(W_TABFORM)),
		"widgGetTabs: Invalid tab form pointer" );

	*pMajor = psForm->majorT;
	*pMinor = psForm->minorT;
}


/* Set a colour on a form */
void widgSetColour(W_SCREEN *psScreen, UDWORD id, UDWORD colour,
				   UBYTE red, UBYTE green, UBYTE blue)
{
	W_TABFORM	*psForm;

	psForm = (W_TABFORM *)widgGetFromID(psScreen, id);
	if (psForm == NULL || psForm->type != WIDG_FORM)
	{
		ASSERT( FALSE,"widgSetColour: couldn't find form from id" );
		return;
	}
	ASSERT( PTRVALID(psForm, sizeof(W_FORM)),
		"widgSetColour: Invalid tab form pointer" );

	if (colour >= WCOL_MAX)
	{
		ASSERT( FALSE, "widgSetColour: Colour id out of range" );
		return;
	}
	psForm->aColours[colour] = (UBYTE)pal_GetNearestColour(red,green,blue);
}


/* Return the origin on the form from which button locations are calculated */
void formGetOrigin(W_FORM *psWidget, SDWORD *pXOrigin, SDWORD *pYOrigin)
{
	W_TABFORM	*psTabForm;

	ASSERT( PTRVALID(psWidget, sizeof(W_FORM)),
		"formGetOrigin: Invalid form pointer" );

	if (psWidget->style & WFORM_TABBED)
	{
		psTabForm = (W_TABFORM *)psWidget;
		if(psTabForm->majorPos == WFORM_TABTOP) {
			*pYOrigin = psTabForm->tabMajorThickness;
		} else if(psTabForm->minorPos == WFORM_TABTOP) {
			*pYOrigin = psTabForm->tabMinorThickness;
		} else {
			*pYOrigin = 0;
		}
		if(psTabForm->majorPos == WFORM_TABLEFT) {
			*pXOrigin = psTabForm->tabMajorThickness;
		} else if(psTabForm->minorPos == WFORM_TABLEFT) {
			*pXOrigin = psTabForm->tabMinorThickness;
		} else {
			*pXOrigin = 0;
		}
//		if ((psTabForm->majorPos == WFORM_TABTOP) ||
//			(psTabForm->minorPos == WFORM_TABTOP))
//		{
//			*pYOrigin = psTabForm->tabThickness;
//		}
//		else
//		{
//			*pYOrigin = 0;
//		}
//		if ((psTabForm->majorPos == WFORM_TABLEFT) ||
//			(psTabForm->minorPos == WFORM_TABLEFT))
//		{
//			*pXOrigin = psTabForm->tabThickness;
//		}
//		else
//		{
//			*pXOrigin = 0;
//		}
	}
	else
	{
		*pXOrigin = 0;
		*pYOrigin = 0;
	}
}


/* Initialise a form widget before running it */
void formInitialise(W_FORM *psWidget)
{
	W_TABFORM	*psTabForm;
	W_CLICKFORM	*psClickForm;
	UDWORD i;

	if (psWidget->style & WFORM_TABBED)
	{
		ASSERT( PTRVALID(psWidget, sizeof(W_TABFORM)),
			"formInitialise: invalid tab form pointer" );
		psTabForm = (W_TABFORM *)psWidget;
		psTabForm->majorT = 0;
		psTabForm->minorT = 0;
		psTabForm->tabHiLite = (UWORD)(-1);
		for (i=0; i<psTabForm->numMajor; i++)
		{
			psTabForm->asMajor[i].lastMinor = 0;
		}
	}
	else if (psWidget->style & WFORM_CLICKABLE)
	{
		ASSERT( PTRVALID(psWidget, sizeof(W_CLICKFORM)),
			"formInitialise: invalid clickable form pointer" );
		psClickForm = (W_CLICKFORM *)psWidget;
		psClickForm->state = WCLICK_NORMAL;
	}
	else
	{
		ASSERT( PTRVALID(psWidget, sizeof(W_FORM)),
			"formInitialise: invalid form pointer" );
	}

	psWidget->psLastHiLite = NULL;
}


/* Choose a horizontal tab from a coordinate */
static BOOL formPickHTab(TAB_POS *psTabPos,
						 SDWORD x0, SDWORD y0,
						 UDWORD width, UDWORD height, UDWORD gap,
						 UDWORD number, SDWORD fx, SDWORD fy)
{
	SDWORD	x, y1;
	UDWORD	i;

#if NO_DISPLAY_SINGLE_TABS
	if (number == 1)
	{
		/* Don't have single tabs */
		return FALSE;
	}
#endif

	x = x0;
	y1 = y0 + height;
	for (i=0; i < number; i++)
	{
//		if (fx >= x && fx <= x + (SDWORD)(width - gap) &&
		if (fx >= x && fx <= x + (SDWORD)(width) &&
			fy >= y0 && fy <= y1)
		{
			/* found a tab under the coordinate */
			psTabPos->index = i;
			psTabPos->x = x;
			psTabPos->y = y0;
			psTabPos->width = width;
			psTabPos->height = height;
			return TRUE;
		}

		x += width + gap;
	}

	/* Didn't find any  */
	return FALSE;
}


/* Choose a vertical tab from a coordinate */
static BOOL formPickVTab(TAB_POS *psTabPos,
						 SDWORD x0, SDWORD y0,
						 UDWORD width, UDWORD height, UDWORD gap,
						 UDWORD number, SDWORD fx, SDWORD fy)
{
	SDWORD	x1, y;
	UDWORD	i;

#if NO_DISPLAY_SINGLE_TABS
	if (number == 1)
	{
		/* Don't have single tabs */
		return FALSE;
	}
#endif

	x1 = x0 + width;
	y = y0;
	for (i=0; i < (SDWORD)number; i++)
	{
		if (fx >= x0 && fx <= x1 &&
			fy >= y && fy <= y + (SDWORD)(height))
//			fy >= y && fy <= y + (SDWORD)(height - gap))
		{
			/* found a tab under the coordinate */
			psTabPos->index = i;
			psTabPos->x = x0;
			psTabPos->y = y;
			psTabPos->width = width;
			psTabPos->height = height;
			return TRUE;
		}

		y += height + gap;
	}

	/* Didn't find any */
	return FALSE;
}


/* Find which tab is under a form coordinate */
static BOOL formPickTab(W_TABFORM *psForm, UDWORD fx, UDWORD fy,
						TAB_POS *psTabPos)
{
	SDWORD		x0,y0, x1,y1;
	W_MAJORTAB	*psMajor;
	SDWORD	xOffset,yOffset;
	SDWORD	xOffset2,yOffset2;

	/* Get the basic position of the form */
	x0 = 0;
	y0 = 0;
	x1 = psForm->width;
	y1 = psForm->height;

	/* Adjust for where the tabs are */
	if(psForm->majorPos == WFORM_TABLEFT) {
		x0 += psForm->tabMajorThickness;
	} else if(psForm->minorPos == WFORM_TABLEFT) {
		x0 += psForm->tabMinorThickness;
	}
	if(psForm->majorPos == WFORM_TABRIGHT) {
		x1 -= psForm->tabMajorThickness;
	} else if (psForm->minorPos == WFORM_TABRIGHT) {
		x1 -= psForm->tabMinorThickness;
	}
	if(psForm->majorPos == WFORM_TABTOP) {
		y0 += psForm->tabMajorThickness;
	} else if(psForm->minorPos == WFORM_TABTOP) {
		y0 += psForm->tabMinorThickness;
	}
	if(psForm->majorPos == WFORM_TABBOTTOM) {
		y1 -= psForm->tabMajorThickness;
	} else if(psForm->minorPos == WFORM_TABBOTTOM) {
		y1 -= psForm->tabMinorThickness;
	}

//		/* Adjust for where the tabs are */
//	if (psForm->majorPos == WFORM_TABLEFT || psForm->minorPos == WFORM_TABLEFT)
//	{
//		x0 += psForm->tabThickness;
//	}
//	if (psForm->majorPos == WFORM_TABRIGHT || psForm->minorPos == WFORM_TABRIGHT)
//	{
//		x1 -= psForm->tabThickness;
//	}
//	if (psForm->majorPos == WFORM_TABTOP || psForm->minorPos == WFORM_TABTOP)
//	{
//		y0 += psForm->tabThickness;
//	}
//	if (psForm->majorPos == WFORM_TABBOTTOM || psForm->minorPos == WFORM_TABBOTTOM)
//	{
//		y1 -= psForm->tabThickness;
//	}


	xOffset = yOffset = 0;
	switch (psForm->minorPos)
	{
		case WFORM_TABTOP:
			yOffset = psForm->tabVertOffset;
			break;

		case WFORM_TABLEFT:
			xOffset = psForm->tabHorzOffset;
			break;

		case WFORM_TABBOTTOM:
			yOffset = psForm->tabVertOffset;
			break;

		case WFORM_TABRIGHT:
			xOffset = psForm->tabHorzOffset;
			break;
	}

	xOffset2 = yOffset2 = 0;
	/* Check the major tabs */
	switch (psForm->majorPos)
	{
	case WFORM_TABTOP:
		if (formPickHTab(psTabPos, x0+psForm->majorOffset - xOffset, y0 - psForm->tabMajorThickness,
					 psForm->majorSize, psForm->tabMajorThickness, psForm->tabMajorGap,
					 psForm->numMajor, fx, fy))
		{
			return TRUE;
		}
		yOffset2 = -psForm->tabVertOffset;
		break;
	case WFORM_TABBOTTOM:
		if (formPickHTab(psTabPos, x0+psForm->majorOffset - xOffset, y1,
					 psForm->majorSize, psForm->tabMajorThickness, psForm->tabMajorGap,
					 psForm->numMajor, fx, fy))
		{
			return TRUE;
		}
		break;
	case WFORM_TABLEFT:
		if (formPickVTab(psTabPos, x0 - psForm->tabMajorThickness, y0+psForm->majorOffset - yOffset,
					 psForm->tabMajorThickness, psForm->majorSize, psForm->tabMajorGap,
					 psForm->numMajor, fx, fy))
		{
			return TRUE;
		}
		xOffset2 = psForm->tabHorzOffset;
		break;
	case WFORM_TABRIGHT:
		if (formPickVTab(psTabPos, x1, y0+psForm->majorOffset - yOffset,
					 psForm->tabMajorThickness, psForm->majorSize, psForm->tabMajorGap,
					 psForm->numMajor, fx, fy))
		{
			return TRUE;
		}
		break;
	case WFORM_TABNONE:
		ASSERT( FALSE, "formDisplayTabbed: Cannot have a tabbed form with no major tabs" );
		break;
	}

	/* Draw the minor tabs */
	psMajor = psForm->asMajor + psForm->majorT;
	switch (psForm->minorPos)
	{
	case WFORM_TABTOP:
		if (formPickHTab(psTabPos, x0+psForm->minorOffset - xOffset2, y0 - psForm->tabMinorThickness,
					 psForm->minorSize, psForm->tabMinorThickness, psForm->tabMinorGap,
					 psMajor->numMinor, fx, fy))
		{
			psTabPos->index += psForm->numMajor;
			return TRUE;
		}
		break;
	case WFORM_TABBOTTOM:
		if (formPickHTab(psTabPos, x0+psForm->minorOffset - xOffset2, y1,
					 psForm->minorSize, psForm->tabMinorThickness, psForm->tabMinorGap,
					 psMajor->numMinor, fx, fy))
		{
			psTabPos->index += psForm->numMajor;
			return TRUE;
		}
		break;
	case WFORM_TABLEFT:
		if (formPickVTab(psTabPos, x0+xOffset - psForm->tabMinorThickness, y0+psForm->minorOffset - yOffset2,
					 psForm->tabMinorThickness, psForm->minorSize, psForm->tabMinorGap,
					 psMajor->numMinor, fx, fy))
		{
			psTabPos->index += psForm->numMajor;
			return TRUE;
		}
		break;
	case WFORM_TABRIGHT:
		if (formPickVTab(psTabPos, x1+xOffset, y0+psForm->minorOffset - yOffset2,
					 psForm->tabMinorThickness, psForm->minorSize, psForm->tabMinorGap,
					 psMajor->numMinor, fx, fy))
		{
			psTabPos->index += psForm->numMajor;
			return TRUE;
		}
		break;
	/* case WFORM_TABNONE - no minor tabs so nothing to display */
	}

	return FALSE;
}

extern UDWORD gameTime2;

/* Run a form widget */
void formRun(W_FORM *psWidget, W_CONTEXT *psContext)
{
	SDWORD		mx,my;
	TAB_POS		sTabPos;
	STRING		*pTip;
	W_TABFORM	*psTabForm;

	if(psWidget->style & WFORM_CLICKABLE) {
		if(((W_CLICKFORM *)psWidget)->state & WCLICK_FLASH) {
			if (((gameTime2/250) % 2) == 0) {
				((W_CLICKFORM *)psWidget)->state &= ~WCLICK_FLASHON;
			} else {
				((W_CLICKFORM *)psWidget)->state |= WCLICK_FLASHON;
			}
		}
	}

	if (psWidget->style & WFORM_TABBED)
	{
		mx = psContext->mx;
		my = psContext->my;
		psTabForm = (W_TABFORM *)psWidget;

		/* If the mouse is over the form, see if any tabs need to be hilited */
		if (mx >= 0 && mx <= psTabForm->width &&
			my >= 0 && my <= psTabForm->height)
		{
			if (formPickTab(psTabForm, mx,my, &sTabPos))
			{
				if (psTabForm->tabHiLite != (UWORD)sTabPos.index)
				{
					/* Got a new tab - start the tool tip if there is one */
					psTabForm->tabHiLite = (UWORD)sTabPos.index;
					if (sTabPos.index >= psTabForm->numMajor)
					{
						pTip = psTabForm->asMajor[psTabForm->majorT].
									asMinor[sTabPos.index - psTabForm->numMajor].pTip;
					}
					else
					{
						pTip = psTabForm->asMajor[sTabPos.index].pTip;
					}
					if (pTip)
					{
						/* Got a tip - start it off */
						tipStart((WIDGET *)psTabForm, pTip,
								 psContext->psScreen->TipFontID, psTabForm->aColours,
								 sTabPos.x + psContext->xOffset,
								 sTabPos.y + psContext->yOffset,
								 sTabPos.width,sTabPos.height);
					}
					else
					{
						/* No tip - clear any old tip */
						tipStop((WIDGET *)psWidget);
					}
				}
			}
			else
			{
				/* No tab - clear the tool tip */
				tipStop((WIDGET *)psWidget);
				/* And clear the hilite */
				psTabForm->tabHiLite = (UWORD)(-1);
			}
		}
	}
}


void formSetFlash(W_FORM *psWidget)
{
	if(psWidget->style & WFORM_CLICKABLE) {
		((W_CLICKFORM *)psWidget)->state |= WCLICK_FLASH;
	}
}


void formClearFlash(W_FORM *psWidget)
{
	if(psWidget->style & WFORM_CLICKABLE) {
		((W_CLICKFORM *)psWidget)->state &= ~WCLICK_FLASH;
		((W_CLICKFORM *)psWidget)->state &= ~WCLICK_FLASHON;
	}
}


/* Respond to a mouse click */
void formClicked(W_FORM *psWidget, UDWORD key)
{
	W_CLICKFORM		*psClickForm;

	/* Stop the tip if there is one */
	tipStop((WIDGET *)psWidget);

	if (psWidget->style & WFORM_CLICKABLE)
	{
		/* Can't click a button if it is disabled or locked down */
		if (!(((W_CLICKFORM *)psWidget)->state & (WCLICK_GREY | WCLICK_LOCKED)))
		{
			// Check this is the correct key
			if ((!(psWidget->style & WFORM_NOPRIMARY) && key == WKEY_PRIMARY) ||
				((psWidget->style & WFORM_SECONDARY) && key == WKEY_SECONDARY))
			{
				((W_CLICKFORM *)psWidget)->state &= ~WCLICK_FLASH;	// Stop it flashing
				((W_CLICKFORM *)psWidget)->state &= ~WCLICK_FLASHON;
				((W_CLICKFORM *)psWidget)->state |= WCLICK_DOWN;

				psClickForm = (W_CLICKFORM *)psWidget;

				if(psClickForm->AudioCallback) {
					psClickForm->AudioCallback(psClickForm->ClickedAudioID);
				}
			}
		}
	}
}


/* Respond to a mouse form up */
void formReleased(W_FORM *psWidget, UDWORD key, W_CONTEXT *psContext)
{
	W_TABFORM	*psTabForm;
	W_CLICKFORM	*psClickForm;
	TAB_POS		sTabPos;

	if (psWidget->style & WFORM_TABBED)
	{
		psTabForm = (W_TABFORM *)psWidget;
		/* See if a tab has been clicked on */
		if (formPickTab(psTabForm, psContext->mx,psContext->my, &sTabPos))
		{
			if (sTabPos.index >= psTabForm->numMajor)
			{
				/* Clicked on a minor tab */
				psTabForm->minorT = (UWORD)(sTabPos.index - psTabForm->numMajor);
				psTabForm->asMajor[psTabForm->majorT].lastMinor = psTabForm->minorT;
				widgSetReturn((WIDGET *)psWidget);
			}
			else
			{
				/* Clicked on a major tab */
				psTabForm->majorT = (UWORD)sTabPos.index;
				psTabForm->minorT = psTabForm->asMajor[sTabPos.index].lastMinor;
				widgSetReturn((WIDGET *)psWidget);
			}
		}
	}
	else if (psWidget->style & WFORM_CLICKABLE)
	{
		psClickForm = (W_CLICKFORM *)psWidget;
		if (psClickForm->state & WCLICK_DOWN)
		{
			// Check this is the correct key
			if ((!(psWidget->style & WFORM_NOPRIMARY) && key == WKEY_PRIMARY) ||
				((psWidget->style & WFORM_SECONDARY) && key == WKEY_SECONDARY))
			{
				widgSetReturn((WIDGET *)psClickForm);
				psClickForm->state &= ~WCLICK_DOWN;
			}
		}
	}
}


/* Respond to a mouse moving over a form */
void formHiLite(W_FORM *psWidget, W_CONTEXT *psContext)
{
	W_CLICKFORM		*psClickForm;

	if (psWidget->style & WFORM_CLICKABLE)
	{
		psClickForm = (W_CLICKFORM *)psWidget;

		psClickForm->state |= WCLICK_HILITE;

		/* If there is a tip string start the tool tip */
		if (psClickForm->pTip)
		{
			tipStart((WIDGET *)psClickForm, psClickForm->pTip,
					 psContext->psScreen->TipFontID, psContext->psForm->aColours,
					 psWidget->x + psContext->xOffset, psWidget->y + psContext->yOffset,
					 psWidget->width, psWidget->height);
		}

		if(psClickForm->AudioCallback) {
			psClickForm->AudioCallback(psClickForm->HilightAudioID);
		}
	}
}


/* Respond to the mouse moving off a form */
void formHiLiteLost(W_FORM *psWidget, W_CONTEXT *psContext)
{
	/* If one of the widgets were hilited that has to loose it as well */
	if (psWidget->psLastHiLite != NULL)
	{
		widgHiLiteLost(psWidget->psLastHiLite, psContext);
	}
	if (psWidget->style & WFORM_TABBED)
	{
		((W_TABFORM *)psWidget)->tabHiLite = (UWORD)(-1);
	}
	if (psWidget->style & WFORM_CLICKABLE)
	{
		((W_CLICKFORM *)psWidget)->state &= ~(WCLICK_DOWN | WCLICK_HILITE);
	}
	/* Clear the tool tip if there is one */
	tipStop((WIDGET *)psWidget);
}

/* Display a form */
void formDisplay(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, UDWORD *pColours)
{
	UDWORD	x0,y0,x1,y1;

	if (!(psWidget->style & WFORM_INVISIBLE))
	{
		x0 = psWidget->x + xOffset;
		y0 = psWidget->y + yOffset;
		x1 = x0 + psWidget->width;
		y1 = y0 + psWidget->height;

		pie_BoxFillIndex(x0+1,y0+1, x1-1,y1-1,WCOL_BKGRND);
		iV_Line(x0,y1,x0,y0,*(pColours + WCOL_LIGHT));
		iV_Line(x0,y0,x1,y0,*(pColours + WCOL_LIGHT));
		iV_Line(x1,y0,x1,y1,*(pColours + WCOL_DARK));
		iV_Line(x1,y1,x0,y1,*(pColours + WCOL_DARK));
	}
}


/* Display a clickable form */
void formDisplayClickable(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, UDWORD *pColours)
{
	UDWORD			x0,y0,x1,y1;
	W_CLICKFORM		*psForm;

	psForm = (W_CLICKFORM *)psWidget;
	x0 = psWidget->x + xOffset;
	y0 = psWidget->y + yOffset;
	x1 = x0 + psWidget->width;
	y1 = y0 + psWidget->height;

	/* Fill the background */
	pie_BoxFillIndex(x0+1,y0+1, x1-1,y1-1,WCOL_BKGRND);

	/* Display the border */
	if (psForm->state & (WCLICK_DOWN | WCLICK_LOCKED | WCLICK_CLICKLOCK))
	{
		/* Form down */
		iV_Line(x0,y1,x0,y0,*(pColours + WCOL_DARK));
		iV_Line(x0,y0,x1,y0,*(pColours + WCOL_DARK));
		iV_Line(x1,y0,x1,y1,*(pColours + WCOL_LIGHT));
		iV_Line(x1,y1,x0,y1,*(pColours + WCOL_LIGHT));
	}
	else
	{
		/* Form up */
		iV_Line(x0,y1,x0,y0,*(pColours + WCOL_LIGHT));
		iV_Line(x0,y0,x1,y0,*(pColours + WCOL_LIGHT));
		iV_Line(x1,y0,x1,y1,*(pColours + WCOL_DARK));
		iV_Line(x1,y1,x0,y1,*(pColours + WCOL_DARK));
	}
}


/* Draw top tabs */
static void formDisplayTTabs(W_TABFORM *psForm,SDWORD x0, SDWORD y0,
							 UDWORD width, UDWORD height,
							 UDWORD number, UDWORD selected, UDWORD hilite,
							 UDWORD *pColours,UDWORD TabType,UDWORD TabGap)
{
	SDWORD	x,x1, y1;
	UDWORD	i;

#if NO_DISPLAY_SINGLE_TABS
	if (number == 1)
	{
		/* Don't display single tabs */
		return;
	}
#endif

	x = x0 + 2;
	x1 = x + width - 2;
	y1 = y0 + height;
	for (i=0; i < number; i++)
	{
		if(psForm->pTabDisplay) {
			psForm->pTabDisplay((WIDGET*)psForm,TabType,WFORM_TABTOP,i,i==selected,i==hilite,x,y0,width,height);
		} else {
			if (i == selected)
			{
				/* Fill in the tab */
				pie_BoxFillIndex(x+1,y0+1, x1-1,y1,WCOL_BKGRND);
				/* Draw the outline */
				iV_Line(x,y0+2, x,y1-1,*(pColours + WCOL_LIGHT));
				iV_Line(x,y0+2, x+2,y0,*(pColours + WCOL_LIGHT));
				iV_Line(x+2,y0, x1-1,y0,*(pColours + WCOL_LIGHT));
				iV_Line(x1,y0+1, x1,y1,*(pColours + WCOL_DARK));
			}
			else
			{
				/* Fill in the tab */
				pie_BoxFillIndex(x+1,y0+2, x1-1,y1-1,WCOL_BKGRND);
				/* Draw the outline */
				iV_Line(x,y0+3, x,y1-1,*(pColours + WCOL_LIGHT));
				iV_Line(x,y0+3, x+2,y0+1,*(pColours + WCOL_LIGHT));
				iV_Line(x+2,y0+1, x1-1,y0+1,*(pColours + WCOL_LIGHT));
				iV_Line(x1,y0+2, x1,y1-1,*(pColours + WCOL_DARK));
			}
			if (i == hilite)
			{
				/* Draw the hilite box */
				iV_Box(x+2,y0+4, x1-3, y1-3,*(pColours + WCOL_HILITE));
			}
		}
		x += width + TabGap;
		x1 += width + TabGap;
	}
}


/* Draw bottom tabs */
static void formDisplayBTabs(W_TABFORM *psForm,SDWORD x0, SDWORD y0,
							 UDWORD width, UDWORD height,
							 UDWORD number, UDWORD selected, UDWORD hilite,
							 UDWORD *pColours,UDWORD TabType,UDWORD TabGap)
{
	SDWORD	x,x1, y1;
	UDWORD	i;

#if NO_DISPLAY_SINGLE_TABS
	if (number == 1)
	{
		/* Don't display single tabs */
		return;
	}
#endif

	x = x0 + 2;
	x1 = x + width - 2;
	y1 = y0 + height;
	for (i=0; i < number; i++)
	{
		if(psForm->pTabDisplay) {
			psForm->pTabDisplay((WIDGET*)psForm,TabType,WFORM_TABBOTTOM,i,i==selected,i==hilite,x,y0,width,height);
		} else {
			if (i == selected)
			{
				/* Fill in the tab */
				pie_BoxFillIndex(x+1,y0, x1-1,y1-1,WCOL_BKGRND);
				/* Draw the outline */
				iV_Line(x,y0, x,y1-1,*(pColours + WCOL_LIGHT));
				iV_Line(x,y1, x1-3,y1,*(pColours + WCOL_DARK));
				iV_Line(x1-2,y1, x1,y1-2,*(pColours + WCOL_DARK));
				iV_Line(x1,y1-3, x1,y0+1,*(pColours + WCOL_DARK));
			}
			else
			{
				/* Fill in the tab */
				pie_BoxFillIndex(x+1,y0+1, x1-1,y1-2,WCOL_BKGRND);
				/* Draw the outline */
				iV_Line(x,y0+1, x,y1-1,*(pColours + WCOL_LIGHT));
				iV_Line(x+1,y1-1, x1-3,y1-1,*(pColours + WCOL_DARK));
				iV_Line(x1-2,y1-1, x1,y1-3,*(pColours + WCOL_DARK));
				iV_Line(x1,y1-4, x1,y0+1,*(pColours + WCOL_DARK));
			}
			if (i == hilite)
			{
				/* Draw the hilite box */
				iV_Box(x+2,y0+3, x1-3, y1-4,*(pColours + WCOL_HILITE));
			}
		}
		x += width + TabGap;
		x1 += width + TabGap;
	}
}


/* Draw left tabs */
static void formDisplayLTabs(W_TABFORM *psForm,SDWORD x0, SDWORD y0,
							 UDWORD width, UDWORD height,
							 UDWORD number, UDWORD selected, UDWORD hilite,
							 UDWORD *pColours,UDWORD TabType,UDWORD TabGap)
{
	SDWORD	x1, y,y1;
	UDWORD	i;

#if NO_DISPLAY_SINGLE_TABS
	if (number == 1)
	{
		/* Don't display single tabs */
		return;
	}
#endif

	x1 = x0 + width;
	y = y0+2;
	y1 = y + height - 2;
	for (i=0; i < (SDWORD)number; i++)
	{
		if(psForm->pTabDisplay) {
			psForm->pTabDisplay((WIDGET*)psForm,TabType,WFORM_TABLEFT,i,i==selected,i==hilite,x0,y,width,height);
		} else {
			if (i == selected)
			{
				/* Fill in the tab */
				pie_BoxFillIndex(x0+1,y+1, x1,y1-1,WCOL_BKGRND);
				/* Draw the outline */
				iV_Line(x0,y, x1-1,y,*(pColours + WCOL_LIGHT));
				iV_Line(x0,y+1, x0,y1-2,*(pColours + WCOL_LIGHT));
				iV_Line(x0+1,y1-1, x0+2,y1,*(pColours + WCOL_DARK));
				iV_Line(x0+3,y1, x1,y1,*(pColours + WCOL_DARK));
			}
			else
			{
				/* Fill in the tab */
				pie_BoxFillIndex(x0+2,y+1, x1-1,y1-1,WCOL_BKGRND);
				/* Draw the outline */
				iV_Line(x0+1,y, x1-1,y,*(pColours + WCOL_LIGHT));
				iV_Line(x0+1,y+1, x0+1,y1-2,*(pColours + WCOL_LIGHT));
				iV_Line(x0+2,y1-1, x0+3,y1,*(pColours + WCOL_DARK));
				iV_Line(x0+4,y1, x1-1,y1,*(pColours + WCOL_DARK));
			}
			if (i == hilite)
			{
				iV_Box(x0+4,y+2, x1-2, y1-3,*(pColours + WCOL_HILITE));
			}
		}
		y += height + TabGap;
		y1 += height + TabGap;
	}
}


/* Draw right tabs */
static void formDisplayRTabs(W_TABFORM *psForm,SDWORD x0, SDWORD y0,
							 UDWORD width, UDWORD height,
							 UDWORD number, UDWORD selected, UDWORD hilite,
							 UDWORD *pColours,UDWORD TabType,UDWORD TabGap)
{
	SDWORD	x1, y,y1;
	UDWORD	i;

#if NO_DISPLAY_SINGLE_TABS
	if (number == 1)
	{
		/* Don't display single tabs */
		return;
	}
#endif

	x1 = x0 + width;
	y = y0+2;
	y1 = y + height - 2;
	for (i=0; i < (SDWORD)number; i++)
	{
		if(psForm->pTabDisplay) {
			psForm->pTabDisplay((WIDGET*)psForm,TabType,WFORM_TABRIGHT,i,i==selected,i==hilite,x0,y,width,height);
		} else {
			if (i == selected)
			{
				/* Fill in the tab */
				pie_BoxFillIndex(x0,y+1, x1-1,y1-1,(UBYTE)*(pColours + WCOL_BKGRND));
				/* Draw the outline */
				iV_Line(x0,y, x1-1,y,*(pColours + WCOL_LIGHT));
				iV_Line(x1,y, x1,y1-2,*(pColours + WCOL_DARK));
				iV_Line(x1-1,y1-1, x1-2,y1,*(pColours + WCOL_DARK));
				iV_Line(x1-3,y1, x0,y1,*(pColours + WCOL_DARK));
			}
			else
			{
				/* Fill in the tab */
				pie_BoxFillIndex(x0+1,y+1, x1-2,y1-1,(UBYTE)*(pColours + WCOL_BKGRND));
				/* Draw the outline */
				iV_Line(x0+1,y, x1-1,y,*(pColours + WCOL_LIGHT));
				iV_Line(x1-1,y, x1-1,y1-2,*(pColours + WCOL_DARK));
				iV_Line(x1-2,y1-1, x1-3,y1,*(pColours + WCOL_DARK));
				iV_Line(x1-4,y1, x0+1,y1,*(pColours + WCOL_DARK));
			}
			if (i == hilite)
			{
				iV_Box(x0+2,y+2, x1-4, y1-3,*(pColours + WCOL_HILITE));
			}
		}
		y += height + TabGap;
		y1 += height + TabGap;
	}
}


/* Display a tabbed form */
void formDisplayTabbed(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, UDWORD *pColours)
{
	UDWORD		x0,y0,x1,y1;
	W_TABFORM	*psForm;
	W_MAJORTAB	*psMajor;

	psForm = (W_TABFORM *)psWidget;

	/* Get the basic position of the form */
	x0 = psForm->x + xOffset;
	y0 = psForm->y + yOffset;
	x1 = x0 + psForm->width;
	y1 = y0 + psForm->height;

	/* Adjust for where the tabs are */
	if(psForm->majorPos == WFORM_TABLEFT) {
		x0 += psForm->tabMajorThickness - psForm->tabHorzOffset;
	} else if(psForm->minorPos == WFORM_TABLEFT) {
		x0 += psForm->tabMinorThickness - psForm->tabHorzOffset;
	}
	if(psForm->majorPos == WFORM_TABRIGHT) {
		x1 -= psForm->tabMajorThickness - psForm->tabHorzOffset;
	} else if(psForm->minorPos == WFORM_TABRIGHT) {
		x1 -= psForm->tabMinorThickness - psForm->tabHorzOffset;
	}
	if(psForm->majorPos == WFORM_TABTOP) {
		y0 += psForm->tabMajorThickness - psForm->tabVertOffset;
	} else if(psForm->minorPos == WFORM_TABTOP) {
		y0 += psForm->tabMinorThickness - psForm->tabVertOffset;
	}
	if(psForm->majorPos == WFORM_TABBOTTOM) {
		y1 -= psForm->tabMajorThickness - psForm->tabVertOffset;
	} else if(psForm->minorPos == WFORM_TABBOTTOM) {
		y1 -= psForm->tabMinorThickness - psForm->tabVertOffset;
	}

	/* Adjust for where the tabs are */
//	if (psForm->majorPos == WFORM_TABLEFT || psForm->minorPos == WFORM_TABLEFT)
//	{
//		x0 += psForm->tabThickness - psForm->tabHorzOffset;
//	}
//	if (psForm->majorPos == WFORM_TABRIGHT || psForm->minorPos == WFORM_TABRIGHT)
//	{
//		x1 -= psForm->tabThickness - psForm->tabHorzOffset;
//	}
//	if (psForm->majorPos == WFORM_TABTOP || psForm->minorPos == WFORM_TABTOP)
//	{
//		y0 += psForm->tabThickness - psForm->tabVertOffset;
//	}
//	if (psForm->majorPos == WFORM_TABBOTTOM || psForm->minorPos == WFORM_TABBOTTOM)
//	{
//		y1 -= psForm->tabThickness - psForm->tabVertOffset;
//	}

	if(psForm->pFormDisplay) {
		psForm->pFormDisplay((WIDGET *)psForm, xOffset, yOffset, psForm->aColours);
	} else {
		/* Draw the form outline */
		if (!(psForm->style & WFORM_INVISIBLE))
		{
			pie_BoxFillIndex(x0,y0,x1,y1,WCOL_BKGRND);
			iV_Line(x0,y1,x0,y0,*(pColours + WCOL_LIGHT));
			iV_Line(x0,y0,x1,y0,*(pColours + WCOL_LIGHT));
			iV_Line(x1,y0,x1,y1,*(pColours + WCOL_DARK));
			iV_Line(x1,y1,x0,y1,*(pColours + WCOL_DARK));
		}
	}

	/* Draw the major tabs */
	switch (psForm->majorPos)
	{
	case WFORM_TABTOP:
		formDisplayTTabs(psForm,x0+psForm->majorOffset, y0 - psForm->tabMajorThickness + psForm->tabVertOffset,
						 psForm->majorSize, psForm->tabMajorThickness,
						 psForm->numMajor, psForm->majorT, psForm->tabHiLite,
						 pColours,TAB_MAJOR,psForm->tabMajorGap);
		break;
	case WFORM_TABBOTTOM:
		formDisplayBTabs(psForm,x0+psForm->majorOffset, y1 + psForm->tabVertOffset,
						 psForm->majorSize, psForm->tabMajorThickness,
						 psForm->numMajor, psForm->majorT, psForm->tabHiLite,
						 pColours,TAB_MAJOR,psForm->tabMajorGap);
		break;
	case WFORM_TABLEFT:
		formDisplayLTabs(psForm,x0 - psForm->tabMajorThickness + psForm->tabHorzOffset, y0+psForm->majorOffset,
						 psForm->tabMajorThickness, psForm->majorSize,
						 psForm->numMajor, psForm->majorT, psForm->tabHiLite,
						 pColours,TAB_MAJOR,psForm->tabMajorGap);
		break;
	case WFORM_TABRIGHT:
		formDisplayRTabs(psForm,x1 - psForm->tabHorzOffset, y0+psForm->majorOffset,
						 psForm->tabMajorThickness, psForm->majorSize,
						 psForm->numMajor, psForm->majorT, psForm->tabHiLite,
						 pColours,TAB_MAJOR,psForm->tabMajorGap);
		break;
	case WFORM_TABNONE:
		ASSERT( FALSE, "formDisplayTabbed: Cannot have a tabbed form with no major tabs" );
		break;
	}

	/* Draw the minor tabs */
	psMajor = psForm->asMajor + psForm->majorT;
	switch (psForm->minorPos)
	{
	case WFORM_TABTOP:
		formDisplayTTabs(psForm,x0 + psForm->minorOffset, y0 - psForm->tabMinorThickness + psForm->tabVertOffset,
						 psForm->minorSize, psForm->tabMinorThickness,
						 psMajor->numMinor, psForm->minorT,
						 psForm->tabHiLite - psForm->numMajor, pColours,TAB_MINOR,psForm->tabMinorGap);
		break;
	case WFORM_TABBOTTOM:
		formDisplayBTabs(psForm,x0 + psForm->minorOffset, y1 + psForm->tabVertOffset,
						 psForm->minorSize, psForm->tabMinorThickness,
						 psMajor->numMinor, psForm->minorT,
						 psForm->tabHiLite - psForm->numMajor, pColours,TAB_MINOR,psForm->tabMinorGap);
		break;
	case WFORM_TABLEFT:
		formDisplayLTabs(psForm,x0 - psForm->tabMinorThickness + psForm->tabHorzOffset + psForm->minorOffset,
						 y0+psForm->minorOffset,
						 psForm->tabMinorThickness, psForm->minorSize,
						 psMajor->numMinor, psForm->minorT,
						 psForm->tabHiLite - psForm->numMajor, pColours,TAB_MINOR,psForm->tabMinorGap);
		break;
	case WFORM_TABRIGHT:
		formDisplayRTabs(psForm,x1 + psForm->tabHorzOffset, y0+psForm->minorOffset,
						 psForm->tabMinorThickness, psForm->minorSize,
						 psMajor->numMinor, psForm->minorT,
						 psForm->tabHiLite - psForm->numMajor, pColours,TAB_MINOR,psForm->tabMinorGap);
		break;
	/* case WFORM_TABNONE - no minor tabs so nothing to display */
	}

}
