/*
**	termGadTools.c
**
**	GadTools object management support routines
**
**	Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#ifdef DEBUG
#include "termDebug.h"
#endif	/* DEBUG */

	/* Some includes for starters. */

#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <intuition/sghooks.h>
#include <utility/tagitem.h>
#include <exec/libraries.h>
#include <exec/memory.h>

#include <clib/intuition_protos.h>
/*#include <clib/gadtools_protos.h>*/
#include <clib/diskfont_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/exec_protos.h>

#include <string.h>
#include <stdarg.h>

	/* Some useful minterms. */

#define MINTERM_COPY		ABC | ABNC | NABC | NABNC
#define MINTERM_NOT_C		ABNC | ANBNC | NABNC | NANBNC
#define MINTERM_NOT_B_AND_C	ANBC | NANBC
#define MINTERM_B_OR_C		ABC | ABNC | NABC | NABNC | ANBC | NANBC

	/* User interface definitions. */

#define SZLV_Display	1		/* Listview includes display */
#define SZLV_String	2		/* Listview includes string */

#define FOLDER_KIND	42		/* Folder selection button. */
#define FILE_KIND	43		/* File selection button. */
#define PASSWORD_KIND	44		/* Password entry string gadget. */
#define SCREEN_KIND	45		/* Screen selection button. */

#define SZ_Adjust	TAG_USER+1	/* Adjust object */
#define SZ_AutoWidth	TAG_USER+2	/* Use default width? */
#define SZ_AlignExtra	TAG_USER+3	/* Add extra space to top edge */
#define SZ_NewColumn	TAG_USER+4	/* Start new column */
#define SZ_Lines	TAG_USER+5	/* Number of lines in object */
#define SZ_Chars	TAG_USER+6	/* Number of chars (width) in object */
#define SZ_AlignLeft	TAG_USER+7	/* Align to left border */
#define SZ_AlignRight	TAG_USER+8	/* Align to right border */
#define SZ_AlignCentre	TAG_USER+9	/* Centre it */
#define SZ_AlignBottom	TAG_USER+10	/* Align to window bottom */
#define SZ_GroupCount	TAG_USER+11	/* Create a group with N objects */
#define SZ_GroupNext	TAG_USER+12	/* Skip to next position */
#define SZ_SaveX	TAG_USER+13	/* Place left to current object, don't touch the left edge position, though. */
#define SZ_SaveY	TAG_USER+14	/* Place below current object, don't touch the top edge position, though. */
#define SZ_UseLeft	TAG_USER+15	/* Use this coordinate as left edge. */
#define SZ_UseTop	TAG_USER+16	/* Use this coordinate as top edge. */
#define SZ_UseWidth	TAG_USER+17	/* Use this gadget width. */
#define SZ_UseHeight	TAG_USER+18	/* Use this gadget height. */
#define SZ_FullWidth	TAG_USER+19	/* Use full window width for gadget. */
#define SZ_FullHeight	TAG_USER+20	/* Use full window height for gadget. */
#define SZ_SmallSpace	TAG_USER+21	/* Provide small alignment space. */
#define SZ_LeaveFolder	TAG_USER+22	/* Leave space for folder icon. */
#define SZ_PlaceLeft	TAG_USER+23	/* Place left to current object, don't modify next object position. */
#define SZ_PlaceBelow	TAG_USER+24	/* Place below current object, don't modify next object position. */
#define SZ_InterHeight	TAG_USER+25	/* Use this inter-object space. */

	/* Box information types. */

enum	{	BOX_LEFT,BOX_TOP,BOX_WIDTH,BOX_HEIGHT };

	/* Use these macros if the corresponding functions are not available. */

extern ULONG			(* __regargs ReadAPen)(struct RastPort *RPort);
extern ULONG			(* __regargs ReadBPen)(struct RastPort *RPort);
extern ULONG			(* __regargs ReadDrMd)(struct RastPort *RPort);

	/* Image types */

enum	{	BUTTONIMAGE_FOLDER, BUTTONIMAGE_FILE, BUTTONIMAGE_SCREEN };

	/* External data. */

extern struct Library		*GadToolsBase,
				*DiskfontBase;
extern struct Gadget		*ActiveGadget;
extern UBYTE			 SharedBuffer[];
extern struct TextAttr		 UserFont;
extern APTR			 VisualInfo;
extern LONG			 InterWidth,
				 InterHeight;
extern struct Hook		 CommandHook,
				 PasswordHook;
extern struct TextFont		*UserTextFont;

	/* This structure will help us to remember the current state of
	 * a GadTools object.
	 */

struct GadgetSupportInfo
{
	STRPTR				 Original,	/* Password string entry, original contents (for undo). */
					 Buffer;	/* Password string entry, current contents. */

	struct GadgetSupportInfo	*PointBack;	/* Points back to itself. */
	struct Gadget			*Gadget;	/* Points back to the host. */

	struct Image			*Image;		/* Folder images. */

	STRPTR				 Title;		/* New gadget title. */

	WORD				 Kind;		/* Item type (BUTTON_KIND, STRING_KIND, etc.) */
	UBYTE				 Key;		/* The key to press. */
	BYTE				 ReadOnly;	/* LISTVIEW_KIND only: is this a read-only object? */
	LONG				 Current,	/* Current state or counter. */
					 Min,		/* Minimum value for `Current'. */
					 Max;		/* Maximum value for `Current'. */
	UWORD				 Flags;		/* Miscellaneous flag values. */
};

	/* A text box for several lines of text. */

struct TextBox
{
	struct TextBox	*NextBox;		/* Next box in chain. */

	LONG		 Left,Top,		/* Position and size. */
			 Width,Height;

	LONG		 LineWidth,		/* Line width in pixels. */
			 LineHeight;		/* Line height in pixels. */

	LONG		 NumChars,		/* Number of chars per line. */
			 NumLines;		/* Number of lines. */

	STRPTR		*Title,			/* Line titles. */
			*Text;			/* Line texts. */

	LONG		 TitleFgPen,
			 TitleBgPen,
			 TextPen;
};

	/* Our home-made tag items. */

STATIC Tag PrivateTags[] =
{
	SZ_Adjust,
	SZ_AutoWidth,
	SZ_AlignExtra,
	SZ_NewColumn,
	SZ_Lines,
	SZ_Chars,
	SZ_AlignLeft,
	SZ_AlignRight,
	SZ_AlignCentre,
	SZ_AlignBottom,
	SZ_GroupCount,
	SZ_GroupNext,
	SZ_SaveX,
	SZ_SaveY,
	SZ_UseLeft,
	SZ_UseTop,
	SZ_UseWidth,
	SZ_UseHeight,
	SZ_FullWidth,
	SZ_FullHeight,
	SZ_SmallSpace,
	SZ_LeaveFolder,
	SZ_PlaceLeft,
	SZ_PlaceBelow,
	SZ_InterHeight,

	TAG_DONE
};

	/* Sizing data. */

STATIC struct RastPort	*SZ_RPort;
STATIC struct TextFont	*SZ_TextFont;
STATIC struct Screen	*SZ_Screen;

STATIC LONG		 SZ_Left,
			 SZ_Top,
			 SZ_WindowWidth,
			 SZ_WindowHeight;
			 SZ_CurrentLeft,
			 SZ_CurrentTop,
			 SZ_CurrentWidth,
			 SZ_MaxWidth,
			 SZ_GroupDelta,
			 SZ_GroupX,
			 SZ_WidthMax,
			 SZ_TextPen,

			 SZ_LastLeft,
			 SZ_LastTop,
			 SZ_LastWidth,
			 SZ_LastHeight;

	/* Dimensions of the font glyphs. */

STATIC ULONG		 SZ_AverageGlyphWidth,	/* Average width. */
			 SZ_MaxGlyphWidth,	/* Maximum width. */
			 SZ_MaxNumericWidth;	/* Maximum numeral width. */

	/* AmigaGuide help. */

extern VOID			GuideSetup(VOID);

	/* Local routines. */

struct Gadget *			CreateGadget(ULONG Kind,struct Gadget *Previous,struct NewGadget *New,...);
VOID				FreeGadgets(struct Gadget *GadgetList);
VOID				GT_SetGadgetAttrs(struct Gadget *Gadget,struct Window *Window,struct Requester *Requester,...);
VOID				GT_ReplyIMsg(struct IntuiMessage *Message);

VOID				KeySelect(struct Gadget **GadgetList,WORD Last,UBYTE Key,struct Window *Window,struct Gadget **Gadget,ULONG *IClass,LONG *Code);

	/* Sizing routines. */

LONG				SZ_GetWindowWidth(VOID);
LONG				SZ_GetWindowHeight(VOID);
LONG				SZ_GetLeftEdge(VOID);
LONG				SZ_GetTopEdge(VOID);
VOID __regargs			SZ_SetTopEdge(LONG Top);
VOID __regargs			SZ_SetLeftEdge(LONG Left);
VOID __regargs			SZ_SetAbsoluteTop(LONG Top);
VOID __regargs			SZ_SetAbsoluteLeft(LONG Left);
VOID __regargs			SZ_SetWidth(LONG Width);
VOID				SZ_InitWidth(VOID);
LONG				SZ_ResetMaxWidth(VOID);
VOID __regargs			SZ_UpdateMaxWidth(UBYTE Type,STRPTR Title,LONG Chars,STRPTR *Choices);

VOID __regargs			SZ_GetDimension(LONG *Width,LONG *Height);
LONG __regargs			SZ_TextWidth(STRPTR String);
VOID __regargs			SZ_AddLeftOffset(LONG Offset);
LONG __stdargs			SZ_LeftOffsetN(LONG DataArray,...);
LONG __regargs			SZ_LeftOffsetDelta(LONG From,LONG To);
VOID				SZ_SizeCleanup(VOID);
BYTE __regargs			SZ_SizeSetup(struct Screen *Screen,struct TextAttr *TextAttr,BYTE Title);
ULONG __regargs			SZ_GetNumericLen(STRPTR String);
ULONG __regargs			SZ_GetGlyphLen(STRPTR String);
ULONG __regargs			SZ_GetLen(STRPTR String);
LONG __regargs			SZ_Height(UBYTE Type,LONG Lines,BYTE Display);
LONG __regargs			SZ_Width(UBYTE Type,STRPTR Title,LONG Chars,STRPTR *Choices);

	/* Text box routines. */

VOID __regargs			SZ_FreeBox(struct TextBox *Box);
VOID __regargs			SZ_FreeBoxes(struct TextBox *FirstBox);
LONG __regargs			SZ_BoxWidth(LONG Chars);
LONG __regargs			SZ_BoxHeight(LONG Lines);
VOID __regargs			SZ_SetTitlePen(struct TextBox *Box,LONG FgPen,LONG BgPen);
struct TextBox * __stdargs	SZ_CreateTextBox(struct TextBox **FirstBox,...);
VOID __stdargs			SZ_SetBoxTitles(struct TextBox *Box,STRPTR Array,...);
VOID __regargs			SZ_SetLine(struct RastPort *RPort,struct TextBox *Box,LONG Line,STRPTR String);
VOID __stdargs			SZ_PrintLine(struct RastPort *RPort,struct TextBox *Box,LONG Line,STRPTR String,...);
VOID __regargs			SZ_DrawBox(struct RastPort *RPort,struct TextBox *Box);
VOID __regargs			SZ_DrawBoxes(struct RastPort *RPort,struct TextBox *FirstBox);
VOID __regargs			SZ_MoveBox(struct TextBox *Box,LONG Left,LONG Top);
VOID __regargs			SZ_MoveBoxes(struct TextBox *FirstBox,LONG Left,LONG Top);
VOID __regargs			SZ_SetBox(struct TextBox *Box,LONG Left,LONG Top);
VOID __regargs			SZ_SetBoxes(struct TextBox *FirstBox,LONG Left,LONG Top);
LONG __regargs			SZ_GetBoxInfo(struct TextBox *Box,LONG Type);

	/* The original gadtools.library routines. */

struct Gadget *			OldCreateGadgetA(ULONG,struct Gadget *,struct NewGadget *,struct TagItem *);
VOID				OldFreeGadgets(struct Gadget *);
VOID				OldGT_SetGadgetAttrsA(struct Gadget *,struct Window *,struct Requester *,struct TagItem *);
VOID				OldGT_ReplyIMsg(struct IntuiMessage *);
VOID				DrawBevelBox(struct RastPort *,LONG,LONG,LONG,LONG,...);

	/* Pragma calls: the easy way. */

#pragma libcall GadToolsBase OldCreateGadgetA 1E A98004
#pragma libcall GadToolsBase OldFreeGadgets 24 801
#pragma libcall GadToolsBase OldGT_SetGadgetAttrsA 2A BA9804
#pragma libcall GadToolsBase OldGT_ReplyIMsg 4E 901

	/* CreateButtonImages():
	 *
	 *	Create images for the folder and file buttons.
	 */

STATIC struct Image *
CreateButtonImages(BYTE Type,UWORD Width,UWORD Height,struct Screen *Screen,APTR VisualInfo)
{
	struct Image *Image = NULL;

		/* Minimum size required. */

	if(Width >= 16 && Height >= 12)
	{
		struct RastPort	*RPort;

			/* Allocate local dummy rastport. */

		if(RPort = (struct RastPort *)AllocVec(sizeof(struct RastPort),MEMF_ANY))
		{
			struct BitMap	*BitMap[2] = { NULL, NULL };
			BYTE		 Success;
			WORD		 Depth,
					 i,j;

				/* Determine screen depth. */

			Depth = Screen -> RastPort . BitMap -> Depth;

				/* Set up rastport. */

			InitRastPort(RPort);

				/* Allocate bitmaps and bitplane data. */

			Success = TRUE;

			for(i = 0 ; Success && i < 2 ; i++)
			{
				if(BitMap[i] = (struct BitMap *)AllocVec(sizeof(struct BitMap),MEMF_ANY))
				{
					InitBitMap(BitMap[i],Depth,Width,Height);

					if(BitMap[i] -> Planes[0] = (PLANEPTR)AllocVec(BitMap[i] -> BytesPerRow * BitMap[i] -> Rows * BitMap[i] -> Depth,MEMF_CHIP | MEMF_CLEAR))
					{
						for(j = 1 ; j < Depth ; j++)
							BitMap[i] -> Planes[j] = BitMap[i] -> Planes[j - 1] + BitMap[i] -> BytesPerRow * BitMap[i] -> Rows;
					}
					else
						Success = FALSE;
				}
				else
					Success = FALSE;
			}

				/* Did we get what we wanted? */

			if(Success)
			{
				struct DrawInfo *DrawInfo;

					/* Get drawing pen information. */

				if(DrawInfo = GetScreenDrawInfo(Screen))
				{
						/* Run-dump of the folder image. */

					STATIC UWORD __chip FolderData[10] =
					{
						0x03C0,
						0x0420,
						0xF810,
						0xFC10,
						0xC3F0,
						0xC010,
						0xC010,
						0xC010,
						0xC010,
						0xFFF0
					};

						/* Run-dump of the file image. */

					STATIC UWORD __chip FileData[10] =
					{
						0xFF00,
						0x8180,
						0x8140,
						0x8120,
						0x81F0,
						0x8010,
						0x8010,
						0x8010,
						0x8010,
						0xFFF0
					};

						/* Run-dump of the screen image. */

					STATIC UWORD __chip ScreenData[10] =
					{
						0xFFF0,
						0x8050,
						0xFFF0,
						0x8010,
						0x8010,
						0x8010,
						0x8010,
						0x8010,
						0x8010,
						0xFFF0
					};

					struct BitMap __aligned	FolderBitMap;
					WORD			Left,
								Top;

						/* Determine image offsets. */

					Left	= (Width - 12) / 2;
					Top	= (Height - 10) / 2;

						/* Set up the folder bitmap. */

					InitBitMap(&FolderBitMap,Depth,12,10);

						/* Put the mask into all bitplanes. */

					switch(Type)
					{
						case BUTTONIMAGE_FOLDER:

							for(i = 0 ; i < Depth ; i++)
								FolderBitMap . Planes[i] = (PLANEPTR)FolderData;

							break;

						case BUTTONIMAGE_FILE:

							for(i = 0 ; i < Depth ; i++)
								FolderBitMap . Planes[i] = (PLANEPTR)FileData;

							break;

						case BUTTONIMAGE_SCREEN:

							for(i = 0 ; i < Depth ; i++)
								FolderBitMap . Planes[i] = (PLANEPTR)ScreenData;

							break;
					}

						/* Manipulate the first bitmap. */

					RPort -> BitMap = BitMap[0];

						/* Clear the bitmap. */

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

						/* Draw the button box. */

					DrawBevelBox(RPort,0,0,Width,Height,
						GT_VisualInfo,	VisualInfo,
					TAG_DONE);

						/* Clear the folder mask. */

					BltBitMap(&FolderBitMap,0,0,BitMap[0],Left,Top,12,10,MINTERM_NOT_B_AND_C,(1 << Depth) - 1,NULL);

						/* Draw the folder mask. */

					BltBitMap(&FolderBitMap,0,0,BitMap[0],Left,Top,12,10,MINTERM_B_OR_C,DrawInfo -> dri_Pens[TEXTPEN],NULL);

						/* Are we to provide a selecte
						 * image in the new look?
						 */

					if((DrawInfo -> dri_Flags & DRIF_NEWLOOK) && Depth > 1)
					{
							/* Manipulate the second bitmap. */

						RPort -> BitMap = BitMap[1];

							/* Set the bitmap to the
							 * selected button colour.
							 */

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

							/* Draw the selected button box. */

						DrawBevelBox(RPort,0,0,Width,Height,
							GTBB_Recessed,	TRUE,
							GT_VisualInfo,	VisualInfo,
						TAG_DONE);

							/* Clear the folder mask. */

						BltBitMap(&FolderBitMap,0,0,BitMap[1],Left,Top,12,10,MINTERM_NOT_B_AND_C,(1 << Depth) - 1,NULL);

							/* Draw the folder mask. */

						BltBitMap(&FolderBitMap,0,0,BitMap[1],Left,Top,12,10,MINTERM_B_OR_C,DrawInfo -> dri_Pens[FILLTEXTPEN],NULL);
					}
					else
					{
							/* Copy the image. */

						BltBitMap(BitMap[0],0,0,BitMap[1],0,0,Width,Height,MINTERM_COPY,(1 << Depth) - 1,NULL);

							/* Invert the inner area. */

						BltBitMap(BitMap[0],0,0,BitMap[1],2,1,Width - 4,Height - 2,MINTERM_NOT_C,(1 << Depth) - 1,NULL);
					}

						/* Allocate space for standard image and selected image. */

					if(Image = (struct Image *)AllocVec(2 * sizeof(struct Image),MEMF_ANY | MEMF_CLEAR))
					{
							/* Fill in the standard data. */

						for(i = 0 ; i < 2 ; i++)
						{
							Image[i] . Width	= Width;
							Image[i] . Height	= Height;
							Image[i] . Depth	= Depth;
							Image[i] . ImageData	= (UWORD *)BitMap[i] -> Planes[0];
							Image[i] . PlanePick	= (1 << Depth) - 1;
						}
					}

						/* Free the screen drawing pen information. */

					FreeScreenDrawInfo(Screen,DrawInfo);
				}
			}

				/* Free the bitmaps, but not necessarily the
				 * bitplane data.
				 */

			for(i = 0 ; i < 2 ; i++)
			{
				if(BitMap[i])
				{
					if(!Success)
						FreeVec(BitMap[i] -> Planes[0]);

					FreeVec(BitMap[i]);
				}
			}

				/* Free the rastport. */

			FreeVec(RPort);
		}
	}

		/* Return the result. */

	return(Image);
}

	/* NewTitle(STRPTR Original,LONG Len,LONG *MaxPixelLen,BYTE Create):
	 *
	 *	Prepare a new slider title, padding the label with spaces.
	 */

STATIC STRPTR __regargs
NewTitle(STRPTR Original,LONG Len,LONG *MaxPixelLen,BYTE Create)
{
	register LONG i,TotalLen,NumSpaces = 0,Last;

		/* Determine length of title text. */

	TotalLen = Last = strlen(Original);

		/* Count the trailing spaces, look for the
		 * last character which is not a space.
		 */

	for(i = TotalLen - 1 ; i >= 0 ; i--)
	{
			/* Is this a space? */

		if(Original[i] == ' ')
		{
				/* Increment number of spaces. */

			NumSpaces++;

				/* Remember last space position. */

			Last = i;
		}
		else
			break;
	}

		/* No spaces? Return the defaults. */

	if(!Len && !NumSpaces)
	{
			/* Reset the information. */

		if(MaxPixelLen)
			*MaxPixelLen = 0;

			/* Return original buffer. */

		return(Original);
	}
	else
	{
			/* If no length is given, use the
			 * values we have determined. */

		if(!Len)
			Len = NumSpaces + 1;

/*		if(MaxPixelLen)
			*MaxPixelLen = Len * SZ_MaxNumericWidth;*/

		NumSpaces = ((Len - 1) * SZ_MaxNumericWidth) / TextLength(SZ_RPort," ",1) + 1;

			/* Copy the body. */

		memcpy(SharedBuffer,Original,Last);

			/* Supply the spaces if any. */

		if(NumSpaces)
			memset(SharedBuffer + Last,' ',NumSpaces);

			/* Provide null-termination. */

		SharedBuffer[Last + NumSpaces] = 0;

			/* Are we to allocate a buffer to
			 * hold the new label?
			 */

		if(Create)
		{
			STRPTR Buffer;

				/* Allocate and copy the buffer. */

			if(Buffer = (STRPTR)AllocVec(Last + NumSpaces + 1,MEMF_ANY))
				strcpy(Buffer,SharedBuffer);

				/* Return the result. */

			return(Buffer);
		}
		else
			return(SharedBuffer);
	}
}

	/* CreateGadget():
	 *
	 *	A frontend to the original routine.
	 */

struct Gadget *
CreateGadget(ULONG Kind,struct Gadget *Previous,struct NewGadget *New,...)
{
	STATIC struct NewGadget		 Backup;

	va_list				 VarArgs;
	struct TagItem			*TagList,
					*ThisTag,
					 MoreTags[3];
	struct GadgetSupportInfo	*Info = NULL;
	WORD				 i;
	struct Gadget			*Result;
	UWORD				 Flags = NULL;
	STRPTR				 NewGadgetTitle = NULL;
	LONG				 MaxPixelLen = 0;

		/* Get the tag item array. */

	va_start(VarArgs,New);

		/* Make a backup copy of the original NewGadget structure,
		 * we may want to modify it.
		 */

	CopyMem(New,&Backup,sizeof(struct NewGadget));

	New = &Backup;

		/* Fill in the defaults if none are present. */

	if(!New -> ng_VisualInfo)
		New -> ng_VisualInfo = VisualInfo;

	if(!New -> ng_TextAttr)
		New -> ng_TextAttr = &UserFont;

		/* Get the tag item list. */

	TagList = (struct TagItem *)VarArgs;

		/* Is this a slider object? */

	if(Kind == SLIDER_KIND && New -> ng_GadgetText)
	{
			/* Is a special label width required? */

		if(ThisTag = FindTagItem(GTSL_MaxLevelLen,TagList))
			NewGadgetTitle = NewTitle(New -> ng_GadgetText,ThisTag -> ti_Data + 1,&MaxPixelLen,TRUE);
		else
			NewGadgetTitle = NewTitle(New -> ng_GadgetText,0,&MaxPixelLen,TRUE);
	}

		/* Try to find the SZ_Adjust tag item. If found, we are to
		 * apply our formatting functions to it.
		 */

	if(ThisTag = FindTagItem(SZ_Adjust,TagList))
	{
		if(ThisTag -> ti_Data)
		{
			LONG	 Width,
				 Height,
				 AddHeight	= InterHeight,
				 Left		= SZ_CurrentLeft,
				 Top		= SZ_CurrentTop;
			BYTE	 AutoWidth	= FALSE,
				 MoveDown	= TRUE,
				 SaveWidth	= TRUE,
				 SetLeft	= FALSE,
				 SetBelow	= FALSE,
				 DropLeft	= FALSE,
				 DropTop	= FALSE;

				/* Are we to leave the left edge untouched? */

			if(ThisTag = FindTagItem(SZ_SaveX,TagList))
				SetLeft = ThisTag -> ti_Data;

				/* Are we to leave the top edge untouched? */

			if(ThisTag = FindTagItem(SZ_SaveY,TagList))
				SetBelow = ThisTag -> ti_Data;

				/* Are we to use a special inter-object alignment size? */

			if(ThisTag = FindTagItem(SZ_InterHeight,TagList))
				AddHeight = ThisTag -> ti_Data;

				/* Are we to place the new object to the left
				 * of the previous object?
				 */

			if(ThisTag = FindTagItem(SZ_PlaceLeft,TagList))
			{
				if(ThisTag -> ti_Data)
				{
					Top		= SZ_LastTop;
					Left		= SZ_LastLeft + SZ_LastWidth + 2;

					DropLeft	= TRUE;
					DropTop		= TRUE;
				}
			}

				/* Are we to place the new object below
				 * the previous object?
				 */

			if(ThisTag = FindTagItem(SZ_PlaceBelow,TagList))
			{
				if(ThisTag -> ti_Data)
				{
					Top = SZ_LastTop + SZ_LastHeight + 1;

					DropLeft = TRUE;
				}
			}

				/* Are we to align the object to the
				 * window bottom?
				 */

			if(ThisTag = FindTagItem(SZ_AlignBottom,TagList))
			{
				if(ThisTag -> ti_Data)
					Top = SZ_WindowHeight;
			}

				/* Are we to add extra space to the
				 * object top edge?
				 */

			if(ThisTag = FindTagItem(SZ_AlignExtra,TagList))
			{
				if(ThisTag -> ti_Data)
					Top += AddHeight;
			}

				/* Are we to start a new column? */

			if(ThisTag = FindTagItem(SZ_NewColumn,TagList))
			{
				if(ThisTag -> ti_Data)
				{
					Top		= SZ_Top;
					Left		= Left + SZ_MaxWidth;
					SZ_MaxWidth	= 0;

					if(ThisTag = FindTagItem(SZ_SmallSpace,TagList))
					{
						if(ThisTag -> ti_Data)
							Left += 2;
						else
							Left += InterWidth;
					}
					else
						Left += InterWidth;
				}
			}

				/* Is the object to contain a certain
				 * number of lines?
				 */

			if(ThisTag = FindTagItem(SZ_Lines,TagList))
			{
				LONG Lines = (LONG)ThisTag -> ti_Data;

					/* Is it a listview object? */

				if(Kind == LISTVIEW_KIND)
				{
						/* Determine height of object. */

					if(ThisTag = FindTagItem(GTLV_ShowSelected,TagList))
					{
						if(ThisTag -> ti_Data)
							Height = SZ_Height(Kind,Lines,SZLV_String);
						else
							Height = SZ_Height(Kind,Lines,SZLV_Display);
					}
					else
						Height = SZ_Height(Kind,Lines,NULL);
				}
				else
					Height = SZ_Height(Kind,Lines,NULL);
			}
			else
			{
					/* If this is a MX kind object,
					 * determine the number of lines
					 * by looking at the labels.
					 */

				if(ThisTag = FindTagItem(GTMX_Labels,TagList))
				{
					STRPTR	*Choices;
					LONG	 Lines = 0;

					Choices = (STRPTR *)ThisTag -> ti_Data;

					while(*Choices++)
						Lines++;

					Height = SZ_Height(Kind,Lines,NULL);
				}
				else
					Height = SZ_Height(Kind,0,NULL);
			}

				/* Is the object width derived from
				 * certain magical calculations or
				 * is it as wide as the default width?
				 */

			if(ThisTag = FindTagItem(SZ_AutoWidth,TagList))
				AutoWidth = ThisTag -> ti_Data;

				/* Determine the width of the object. */

			if(!AutoWidth)
			{
				STRPTR	*Choices	= NULL,
					 Title		= NULL;
				LONG	 Chars		= 0;

					/* Determine number of characters in
					 * this object.
					 */

				if(ThisTag = FindTagItem(SZ_Chars,TagList))
					Chars = ThisTag -> ti_Data;

					/* If this is a button object,
					 * calculate the width by looking
					 * at the title.
					 */

				if(Kind == BUTTON_KIND && New -> ng_GadgetText)
					Title = New -> ng_GadgetText;

					/* If this is a cycle object,
					 * take a look at the labels.
					 */

				if(ThisTag = FindTagItem(GTCY_Labels,TagList))
					Choices = (STRPTR *)ThisTag -> ti_Data;

					/* If this is a listview object,
					 * take a look at the list.
					 */

				if(ThisTag = FindTagItem(GTLV_Labels,TagList))
				{
					if(ThisTag -> ti_Data != ~0)
					{
						struct List	*List = (struct List *)ThisTag -> ti_Data;
						struct Node	*Node;
						LONG		 Len;

						Node = List -> lh_Head;

						while(Node -> ln_Succ)
						{
							if((Len = strlen(Node -> ln_Name)) > Chars)
								Chars = Len;

							Node = Node -> ln_Succ;
						}
					}
				}

					/* Determine object width. */

				Width = SZ_Width(Kind,Title,Chars,Choices);
			}
			else
			{
					/* Use the default width. */

				Width = SZ_CurrentWidth;

					/* A checkbox object has a
					 * fixed width. We don't want
					 * any other objects to share
					 * it.
					 */

				if(Kind == CHECKBOX_KIND || Kind == FOLDER_KIND || Kind == FILE_KIND || Kind == SCREEN_KIND)
				{
					Width = SZ_Width(Kind,NULL,0,NULL);

					SaveWidth = FALSE;
				}
			}

				/* Are we to align this object to
				 * the left window edge?
				 */

			if(ThisTag = FindTagItem(SZ_AlignLeft,TagList))
			{
				if(ThisTag -> ti_Data)
				{
					Left = SZ_Left;

					MoveDown = FALSE;
				}
			}

				/* Are we to use a specific object width? */

			if(ThisTag = FindTagItem(SZ_UseWidth,TagList))
				Width = ThisTag -> ti_Data;

				/* Are we to use a specific object height? */

			if(ThisTag = FindTagItem(SZ_UseHeight,TagList))
				Height = ThisTag -> ti_Data;

				/* Are we to align this object to the
				 * right window edge?
				 */

			if(ThisTag = FindTagItem(SZ_AlignRight,TagList))
			{
				if(ThisTag -> ti_Data)
				{
					Left = SZ_WindowWidth - INTERWIDTH - Width;

					MoveDown = FALSE;
				}
			}

				/* Are we to centre the object in
				 * the window?
				 */

			if(ThisTag = FindTagItem(SZ_AlignCentre,TagList))
			{
				if(ThisTag -> ti_Data)
				{
					Left = (SZ_WindowWidth - Width) / 2;

					MoveDown = FALSE;
				}
			}

				/* Are we to build a horizontal group
				 * using the following objects?
				 */

			if(ThisTag = FindTagItem(SZ_GroupCount,TagList))
			{
				LONG	Count = ThisTag -> ti_Data,
					Width = SZ_WindowWidth;

				if(!Width)
					Width = SZ_WindowWidth = SZ_Left + Count * (SZ_CurrentWidth + 2) + InterWidth;

				if(!Top)
					Top = SZ_Top;

					/* Use a specific left edge? */

				if(ThisTag = FindTagItem(SZ_UseLeft,TagList))
				{
					Width -= ThisTag -> ti_Data - InterWidth;

					SZ_GroupDelta	= SZ_CurrentWidth + ((Width - 2 * InterWidth) - (Count * SZ_CurrentWidth)) / (Count - 1);
					SZ_GroupX	= Left = ThisTag -> ti_Data;
				}
				else
				{
					SZ_GroupDelta	= SZ_CurrentWidth + ((Width - 2 * InterWidth) - (Count * SZ_CurrentWidth)) / (Count - 1);
					SZ_GroupX	= Left = SZ_Left;
				}

				MoveDown = FALSE;
			}
			else
			{
				if(ThisTag = FindTagItem(SZ_UseLeft,TagList))
					Left = ThisTag -> ti_Data;
			}

				/* Skip to the next group item position? */

			if(ThisTag = FindTagItem(SZ_GroupNext,TagList))
			{
				if(ThisTag -> ti_Data)
				{
					SZ_GroupX += SZ_GroupDelta;

					Left = SZ_GroupX;

					MoveDown = FALSE;
				}
			}

				/* Determine default label placement. */

			switch(Kind)
			{
				case MX_KIND:

					New -> ng_Flags	= (New -> ng_Flags & ~(PLACETEXT_LEFT|PLACETEXT_RIGHT|PLACETEXT_ABOVE|PLACETEXT_BELOW|PLACETEXT_IN)) | PLACETEXT_LEFT;

					SaveWidth = FALSE;

					break;

				case CHECKBOX_KIND:

					SaveWidth = FALSE;
					break;

				default:

					if(New -> ng_Flags & PLACETEXT_ABOVE)
						Top += INTERHEIGHT + SZ_TextFont -> tf_YSize;

					break;
			}

				/* Use a specific top edge? */

			if(ThisTag = FindTagItem(SZ_UseTop,TagList))
				Top = ThisTag -> ti_Data;

				/* Gadget is to be as wide as the entire window? */

			if(ThisTag = FindTagItem(SZ_FullWidth,TagList))
			{
				if(ThisTag -> ti_Data)
					Width = SZ_WindowWidth - (InterWidth + Left);
			}

				/* Gadget is to be as high as the entire window? */

			if(ThisTag = FindTagItem(SZ_FullHeight,TagList))
			{
				if(ThisTag -> ti_Data)
					Height = SZ_WindowHeight - (AddHeight + SZ_Top + Top);
			}

				/* Leave space for folder icon? */

			if(ThisTag = FindTagItem(SZ_LeaveFolder,TagList))
			{
				if(ThisTag -> ti_Data && (Kind == STRING_KIND || Kind == PASSWORD_KIND))
					New -> ng_Width = Width - (2 + SZ_Width(FOLDER_KIND,"",0,0));
				else
					New -> ng_Width = Width;
			}
			else
				New -> ng_Width = Width;

				/* Fill in the NewGadget. */

			New -> ng_LeftEdge	= Left;
			New -> ng_TopEdge	= Top;
			New -> ng_Height	= Height;

				/* Add the default tags. */

			switch(Kind)
			{
/*				case SLIDER_KIND:

					MoreTags[0] . ti_Tag	= GTSL_MaxPixelLen;
					MoreTags[0] . ti_Data	= MaxPixelLen;

					MoreTags[1] . ti_Tag	= GTSL_Justification;
					MoreTags[1] . ti_Data	= GTJ_RIGHT;

					MoreTags[2] . ti_Tag	= TAG_MORE;
					MoreTags[2] . ti_Data	= (ULONG)TagList;

					if(MaxPixelLen)
						TagList = MoreTags;
					else
						TagList = MoreTags + 1;

					break;*/

				case PALETTE_KIND:

					MoreTags[0] . ti_Tag	= GTPA_IndicatorWidth;
					MoreTags[0] . ti_Data	= Width / 4;

					MoreTags[1] . ti_Tag	= GTPA_IndicatorHeight;
					MoreTags[1] . ti_Data	= Height;

					MoreTags[2] . ti_Tag	= TAG_MORE;
					MoreTags[2] . ti_Data	= (ULONG)TagList;

					TagList = MoreTags;

					break;

				case LISTVIEW_KIND:

					MoreTags[0] . ti_Tag	= GTLV_ScrollWidth;
					MoreTags[0] . ti_Data	= 2 + 2 * SZ_AverageGlyphWidth + 2;

					MoreTags[1] . ti_Tag	= TAG_MORE;
					MoreTags[1] . ti_Data	= (ULONG)TagList;

					TagList = MoreTags;

					break;

				case CHECKBOX_KIND:

					MoreTags[0] . ti_Tag	= GTCB_Scaled;
					MoreTags[0] . ti_Data	= TRUE;

					MoreTags[1] . ti_Tag	= TAG_MORE;
					MoreTags[1] . ti_Data	= (ULONG)TagList;

					TagList = MoreTags;

					break;

				case MX_KIND:

					MoreTags[0] . ti_Tag	= GTMX_Scaled;
					MoreTags[0] . ti_Data	= TRUE;

					MoreTags[1] . ti_Tag	= TAG_MORE;
					MoreTags[1] . ti_Data	= (ULONG)TagList;

					TagList = MoreTags;

					break;

				case STRING_KIND:

					MoreTags[0] . ti_Tag	= GTST_EditHook;
					MoreTags[0] . ti_Data	= (ULONG)&CommandHook;

					MoreTags[1] . ti_Tag	= GA_Immediate;
					MoreTags[1] . ti_Data	= TRUE;

					MoreTags[2] . ti_Tag	= TAG_MORE;
					MoreTags[2] . ti_Data	= (ULONG)TagList;

					if(!FindTagItem(GTST_EditHook,TagList))
						TagList = MoreTags;
					else
						TagList = MoreTags + 1;

					break;

				case PASSWORD_KIND:

					MoreTags[0] . ti_Tag	= GTST_EditHook;
					MoreTags[0] . ti_Data	= (ULONG)&PasswordHook;

					MoreTags[1] . ti_Tag	= GA_Immediate;
					MoreTags[1] . ti_Data	= TRUE;

					MoreTags[2] . ti_Tag	= TAG_MORE;
					MoreTags[2] . ti_Data	= (ULONG)TagList;

					TagList = MoreTags;
					break;

				case INTEGER_KIND:

					MoreTags[0] . ti_Tag	= GTIN_EditHook;
					MoreTags[0] . ti_Data	= (ULONG)&CommandHook;

					MoreTags[1] . ti_Tag	= GA_Immediate;
					MoreTags[1] . ti_Data	= TRUE;

					MoreTags[2] . ti_Tag	= TAG_MORE;
					MoreTags[2] . ti_Data	= (ULONG)TagList;

					if(!FindTagItem(GTIN_EditHook,TagList))
						TagList = MoreTags;
					else
						TagList = MoreTags + 1;

					break;

				case FILE_KIND:
				case FOLDER_KIND:
				case SCREEN_KIND:

					New -> ng_GadgetText = "";
					break;
			}

				/* Restore default object width if necessary. */

			if(SaveWidth)
				SZ_CurrentWidth	= Width;

			if(!DropTop)
			{
					/* Are we to leave the top edge untouched? */

				if(SetBelow)
				{
						/* Extend window height if necessary. */

					if(Top + Height + AddHeight > SZ_WindowHeight)
						SZ_WindowHeight = Top + Height + AddHeight;
				}
				else
				{
					SZ_CurrentTop = Top;

					if(MoveDown)
					{
							/* Move down to next object. */

						SZ_CurrentTop = SZ_CurrentTop + Height + AddHeight;

							/* Extend window height if necessary. */

						if(SZ_CurrentTop > SZ_WindowHeight)
							SZ_WindowHeight = SZ_CurrentTop;
					}
					else
					{
							/* Extend window height if necessary. */

						if(SZ_CurrentTop + Height + AddHeight > SZ_WindowHeight)
							SZ_WindowHeight = SZ_CurrentTop + Height + AddHeight;

						SZ_MaxWidth = 0;
					}
				}
			}

			if(!DropLeft)
			{
					/* Update maximum object width. */

				if(Width > SZ_MaxWidth)
					SZ_MaxWidth = Width;

					/* Extend window width if necessary. */

				if(Left + SZ_MaxWidth + InterWidth > SZ_WindowWidth)
					SZ_WindowWidth = Left + SZ_MaxWidth + InterWidth;

					/* Are we to leave the left edge untouched? */

				if(!SetLeft)
					SZ_CurrentLeft = Left;
			}

				/* Remember last object position and size. */

			SZ_LastLeft	= New -> ng_LeftEdge;
			SZ_LastWidth	= New -> ng_Width;
			SZ_LastHeight	= New -> ng_Height;

			if(New -> ng_Flags & PLACETEXT_ABOVE)
				SZ_LastTop = New -> ng_TopEdge - (INTERHEIGHT + SZ_TextFont -> tf_YSize);
			else
				SZ_LastTop = New -> ng_TopEdge;
		}
	}

		/* Now for keystroke activation... */

	switch(Kind)
	{
		case STRING_KIND:
		case INTEGER_KIND:
		case BUTTON_KIND:
		case CHECKBOX_KIND:
		case CYCLE_KIND:
		case LISTVIEW_KIND:
		case MX_KIND:
		case PALETTE_KIND:
		case SCROLLER_KIND:
		case SLIDER_KIND:
		case PASSWORD_KIND:

				/* Do we have a gadget label? */

			if(New -> ng_GadgetText)
			{
					/* Look for the underscore
					 * character...
					 */

				for(i = 0 ; i < strlen(New -> ng_GadgetText) ; i++)
				{
					if(New -> ng_GadgetText[i] == '_')
					{
							/* Is it a printable character? */

						if((WORD)(New -> ng_GadgetText[i + 1]) > ' ')
						{
								/* Create information buffer. */

							if(Info = (struct GadgetSupportInfo *)AllocMem(sizeof(struct GadgetSupportInfo),MEMF_ANY|MEMF_CLEAR))
							{
								STATIC struct TagItem EvenMoreTags[2];

								EvenMoreTags[0] . ti_Tag	= GT_Underscore;
								EvenMoreTags[0] . ti_Data	= '_';

								EvenMoreTags[1] . ti_Tag	= TAG_MORE;
								EvenMoreTags[1] . ti_Data	= (ULONG)TagList;

								TagList = EvenMoreTags;

									/* Set up the default data. */

								Info -> PointBack	= Info;
								Info -> Kind		= Kind;
								Info -> Key		= New -> ng_GadgetText[i + 1];

									/* Add object-specific information. */

								if(Info -> Kind == PALETTE_KIND)
								{
									Info -> Current	= 1;
									Info -> Max	= 1;
								}

								if(Info -> Kind == LISTVIEW_KIND)
									Info -> Current	= ~0;

								break;
							}
						}
					}
				}
			}

			break;

		default:

			break;
	}

		/* No GadgetInfo supplied yet? Does this object need any? */

	if(!Info && (Kind == PASSWORD_KIND || Kind == FILE_KIND || Kind == FOLDER_KIND || Kind == SCREEN_KIND || Flags || NewGadgetTitle))
	{
			/* Allocate GadgetInfo. */

		if(Info = (struct GadgetSupportInfo *)AllocMem(sizeof(struct GadgetSupportInfo),MEMF_ANY|MEMF_CLEAR))
		{
			Info -> PointBack	= Info;
			Info -> Kind		= Kind;
		}
	}

		/* Fill in object info if available. */

	New -> ng_UserData = Info;

		/* Fill in the new title if available. */

	if(NewGadgetTitle)
		New -> ng_GadgetText = NewGadgetTitle;

		/* Did we create any object info? */

	if(Info)
	{
		struct Node	*Node;
		struct List	*List;
		LONG		 Count;
		STRPTR		*Strings;
		struct TagItem	*NewTagList;

			/* The following code takes care of object-specific
			 * characteristics, such as currently selected items,
			 * current value, minimum value, maximum value, etc.
			 * and places them in the gadget info.
			 */

		switch(Kind)
		{
			case CHECKBOX_KIND:

				if(ThisTag = FindTagItem(GTCB_Checked,TagList))
					Info -> Current = ThisTag -> ti_Data;

				break;

			case CYCLE_KIND:

				if(ThisTag = FindTagItem(GTCY_Active,TagList))
					Info -> Current = ThisTag -> ti_Data;

				if(ThisTag = FindTagItem(GTCY_Labels,TagList))
				{
					Count = 0;

					if(Strings = (STRPTR *)ThisTag -> ti_Data)
					{
						while(Strings[Count])
							Count++;
					}

					if(Count)
						Info -> Max = Count - 1;
				}

				break;

			case LISTVIEW_KIND:

				if(ThisTag = FindTagItem(GTLV_Selected,TagList))
					Info -> Current = ThisTag -> ti_Data;

				if(ThisTag = FindTagItem(GTLV_ReadOnly,TagList))
				{
					if(ThisTag -> ti_Data)
						Info -> ReadOnly = TRUE;
				}

				if(ThisTag = FindTagItem(GTLV_ShowSelected,TagList))
				{
					if(ThisTag -> ti_Data)
						Info -> ReadOnly = FALSE;
				}

				if(ThisTag = FindTagItem(GTLV_Labels,TagList))
				{
					Count = 0;

					if(ThisTag -> ti_Data && ThisTag -> ti_Data != ~0)
					{
						List = (struct List *)ThisTag -> ti_Data;

						Node = List -> lh_Head;

						while(Node -> ln_Succ)
						{
							Count++;

							Node = Node -> ln_Succ;
						}
					}

					if(Count)
						Info -> Max = Count - 1;
				}

				break;

			case MX_KIND:

				if(ThisTag = FindTagItem(GTMX_Active,TagList))
					Info -> Current = ThisTag -> ti_Data;

				if(ThisTag = FindTagItem(GTMX_Labels,TagList))
				{
					Count = 0;

					if(Strings = (STRPTR *)ThisTag -> ti_Data)
					{
						while(Strings[Count])
							Count++;
					}

					if(Count)
						Info -> Max = Count - 1;
				}

				break;

			case SCROLLER_KIND:

				if(ThisTag = FindTagItem(GTSC_Top,TagList))
					Info -> Current = ThisTag -> ti_Data;

				if(ThisTag = FindTagItem(GTSC_Total,TagList))
					Info -> Max = ThisTag -> ti_Data;

				break;

			case SLIDER_KIND:

				if(ThisTag = FindTagItem(GTSL_Level,TagList))
					Info -> Current = ThisTag -> ti_Data;

				if(ThisTag = FindTagItem(GTSL_Min,TagList))
					Info -> Min = ThisTag -> ti_Data;

				if(ThisTag = FindTagItem(GTSL_Max,TagList))
					Info -> Max = ThisTag -> ti_Data;

				break;

			case PALETTE_KIND:

				if(ThisTag = FindTagItem(GTPA_Color,TagList))
					Info -> Current = ThisTag -> ti_Data;

				if(ThisTag = FindTagItem(GTPA_Depth,TagList))
					Info -> Max = (1 << ((LONG)ThisTag -> ti_Data)) - 1;

				break;
		}

			/* In order to avoid collisions with system software tags,
			 * we will clone the current tag item list and filter
			 * our private tags out before surrendering it to
			 * CreateGadget().
			 */

		if(NewTagList = CloneTagItems(TagList))
		{
				/* Remove the private tags. */

			FilterTagItems(NewTagList,PrivateTags,TAGFILTER_NOT);

				/* Are we to create a custom button object? */

			if(Kind == FOLDER_KIND || Kind == FILE_KIND || Kind == SCREEN_KIND)
			{
				BYTE Type;

					/* Which kind of object are we to create? */

				switch(Kind)
				{
					case FOLDER_KIND:

						Type = BUTTONIMAGE_FOLDER;
						break;

					case FILE_KIND:

						Type = BUTTONIMAGE_FILE;
						break;

					case SCREEN_KIND:

						Type = BUTTONIMAGE_SCREEN;
						break;
				}

					/* Create the images. */

				if(Info -> Image = CreateButtonImages(Type,New -> ng_Width,New -> ng_Height,SZ_Screen,VisualInfo))
				{
						/* Create the object and attach the info to it. */

					if(Result = OldCreateGadgetA(GENERIC_KIND,Previous,New,NewTagList))
					{
						Result -> GadgetType	|= GTYP_BOOLGADGET;
						Result -> GadgetRender	 = &Info -> Image[0];
						Result -> SelectRender	 = &Info -> Image[1];
						Result -> Flags		|= GFLG_GADGHIMAGE | GFLG_GADGIMAGE;
						Result -> Activation	|= GACT_RELVERIFY;
					}
				}
				else
					Result = NULL;
			}
			else
			{
					/* Are we to create a password entry string gadget? */

				if(Kind == PASSWORD_KIND)
				{
					LONG MaxChars = 0;

						/* Determine number of characters
						 * the gadget is to hold.
						 */

					if(ThisTag = FindTagItem(GTST_MaxChars,NewTagList))
						MaxChars = ThisTag -> ti_Data;

						/* Any size set? */

					if(MaxChars)
					{
							/* Allocate the auxilary buffers. */

						if(Info -> Original = (STRPTR)AllocVec((MaxChars + 2) * 2,MEMF_PUBLIC|MEMF_CLEAR))
						{
								/* Set up the text entry buffer. */

							Info -> Buffer = Info -> Original + (MaxChars + 2);

								/* If available find the original
								 * string this gadget is to be
								 * preset with.
								 */

							if(ThisTag = FindTagItem(GTST_String,NewTagList))
							{
								LONG Len;

									/* Remember the original data. */

								strcpy(Info -> Buffer, (STRPTR)ThisTag -> ti_Data);
								strcpy(Info -> Original,(STRPTR)ThisTag -> ti_Data);

									/* Determine length of the original text. */

								Len = strlen(Info -> Original);

									/* Provide fake input. */

								memset(SharedBuffer,'*',Len);

								SharedBuffer[Len] = 0;

									/* This is our own private tag item
									 * list, so why bother.
									 */

								ThisTag -> ti_Data = (ULONG)SharedBuffer;
							}

							Result = OldCreateGadgetA(STRING_KIND,Previous,New,NewTagList);
						}
						else
							Result = NULL;
					}
					else
						Result = NULL;
				}
				else
				{
						/* Create the object and attach the info to it. */

					Result = OldCreateGadgetA(Kind,Previous,New,NewTagList);
				}
			}

				/* Successful? */

			if(Result)
			{
					/* Fill in the data. */

				Info -> Gadget	= Result;
				Info -> Flags	= Flags;
				Info -> Title	= NewGadgetTitle;
			}
			else
			{
					/* Free the images. */

				if(Info -> Image && (Info -> Kind == FOLDER_KIND || Info -> Kind == FILE_KIND || Info -> Kind == SCREEN_KIND))
				{
					WORD i;

					for(i = 0 ; i < 2 ; i++)
						FreeVec(Info -> Image[i] . ImageData);

					FreeVec(Info -> Image);
				}

					/* Free the password gadget buffers. */

				if(Info -> Original && Info -> Kind == PASSWORD_KIND)
					FreeVec(Info -> Original);

					/* Free alternative gadget label. */

				if(NewGadgetTitle)
					FreeVec(NewGadgetTitle);

					/* Free the info buffer itself. */

				FreeMem(Info,sizeof(struct GadgetSupportInfo));
			}

				/* Free the private tag item list. */

			FreeTagItems(NewTagList);
		}
		else
		{
				/* Free alternative gadget label. */

			if(NewGadgetTitle)
				FreeVec(NewGadgetTitle);

				/* Free the info buffer itself. */

			FreeMem(Info,sizeof(struct GadgetSupportInfo));

			Result = NULL;
		}
	}
	else
	{
			/* No private objects, please. */

		if(Kind == FOLDER_KIND || Kind == FILE_KIND || Kind == SCREEN_KIND || Kind == PASSWORD_KIND)
			Result = NULL;
		else
		{
			struct TagItem *NewTagList;

				/* Copy the tag item list. */

			if(NewTagList = CloneTagItems(TagList))
			{
					/* Filter out the private tags if any. */

				FilterTagItems(NewTagList,PrivateTags,TAGFILTER_NOT);

					/* Create the object. */

				Result = OldCreateGadgetA(Kind,Previous,New,TagList);

					/* Free the private tag item list. */

				FreeTagItems(NewTagList);
			}
		}
	}

	va_end(VarArgs);

		/* Patch in the GACT_IMMEDIATE flag for string gadget objects under
		 * v37. Although this does not look very clean it is temporarily
		 * permitted for this single release.
		 */

	if(Result && GadToolsBase -> lib_Version == 37 && (Kind == STRING_KIND || Kind == INTEGER_KIND || Kind == PASSWORD_KIND))
		Result -> Activation |= GACT_IMMEDIATE;

	return(Result);
}

	/* GT_SetGadgetAttrs():
	 *
	 *	A frontend to the original routine.
	 */

VOID
GT_SetGadgetAttrs(struct Gadget *Gadget,struct Window *Window,struct Requester *Requester,...)
{
	va_list				 VarArgs;
	struct TagItem			*TagList,
					*ThisTag;
	struct GadgetSupportInfo	*Info;

		/* Get the tag item list. */

	va_start(VarArgs,Requester);

	TagList = (struct TagItem *)VarArgs;

		/* Does the object in question have an info structure attached? */

	if(Info = (struct GadgetSupportInfo *)Gadget -> UserData)
	{
			/* Does the data look sensible? */

		if(Info -> PointBack == Info && Info -> Gadget == Gadget)
		{
			struct Node	*Node;
			struct List	*List;
			LONG		 Count;
			STRPTR		*Strings;

				/* Update object-specific information. */

			switch(Info -> Kind)
			{
				case CHECKBOX_KIND:

					if(ThisTag = FindTagItem(GTCB_Checked,TagList))
						Info -> Current = ThisTag -> ti_Data;

					break;

				case CYCLE_KIND:

					if(ThisTag = FindTagItem(GTCY_Active,TagList))
						Info -> Current = ThisTag -> ti_Data;

					if(ThisTag = FindTagItem(GTCY_Labels,TagList))
					{
						Count = 0;

						if(Strings = (STRPTR *)ThisTag -> ti_Data)
						{
							while(Strings[Count])
								Count++;
						}

						if(Count)
							Info -> Max = Count - 1;
						else
							Info -> Max = 0;
					}

					break;

				case LISTVIEW_KIND:

					if(ThisTag = FindTagItem(GTLV_Selected,TagList))
						Info -> Current = ThisTag -> ti_Data;

					if(ThisTag = FindTagItem(GTLV_Labels,TagList))
					{
						Count = 0;

						if(ThisTag -> ti_Data && ThisTag -> ti_Data != ~0)
						{
							List = (struct List *)ThisTag -> ti_Data;

							Node = List -> lh_Head;

							while(Node -> ln_Succ)
							{
								Count++;

								Node = Node -> ln_Succ;
							}
						}

						if(Count)
							Info -> Max = Count - 1;
						else
							Info -> Max = 0;
					}

					break;

				case PALETTE_KIND:

					if(ThisTag = FindTagItem(GTPA_Color,TagList))
						Info -> Current = ThisTag -> ti_Data;

					break;

				case MX_KIND:

					if(ThisTag = FindTagItem(GTMX_Active,TagList))
						Info -> Current = ThisTag -> ti_Data;

					break;

				case SCROLLER_KIND:

					if(ThisTag = FindTagItem(GTSC_Top,TagList))
						Info -> Current = ThisTag -> ti_Data;

					if(ThisTag = FindTagItem(GTSC_Total,TagList))
						Info -> Max = ThisTag -> ti_Data;

					break;

				case SLIDER_KIND:

					if(ThisTag = FindTagItem(GTSL_Level,TagList))
						Info -> Current = ThisTag -> ti_Data;

					if(ThisTag = FindTagItem(GTSL_Min,TagList))
						Info -> Min = ThisTag -> ti_Data;

					break;

				case PASSWORD_KIND:

					if(FindTagItem(GTST_String,TagList))
					{
						struct TagItem *NewTagList;

						if(NewTagList = CloneTagItems(TagList))
						{
							LONG Len;

							ThisTag = FindTagItem(GTST_String,NewTagList);

							Forbid();

							strcpy(Info -> Original,(STRPTR)ThisTag -> ti_Data);
							strcpy(Info -> Buffer, (STRPTR)ThisTag -> ti_Data);

							Len = strlen(Info -> Buffer);

							memset(SharedBuffer,'*',Len);

							SharedBuffer[Len] = 0;

							ThisTag -> ti_Data = (ULONG)SharedBuffer;

							OldGT_SetGadgetAttrsA(Gadget,Window,Requester,NewTagList);

							FreeTagItems(NewTagList);

							Permit();
						}

						va_end(VarArgs);

						return;
					}

					break;
			}
		}
	}

		/* Call the original routine. */

	OldGT_SetGadgetAttrsA(Gadget,Window,Requester,TagList);

	va_end(VarArgs);
}

	/* FreeGadgets(struct Gadget *GadgetList):
	 *
	 *	A frontend to the original routine.
	 */

VOID
FreeGadgets(struct Gadget *GadgetList)
{
	struct Gadget			*Gadget = GadgetList;
	struct GadgetSupportInfo	*Info;

		/* Run down the gadget list. */

	while(Gadget)
	{
			/* Is this the previously active string gadget? */

		if(Gadget == ActiveGadget)
			ActiveGadget = NULL;

			/* Does it have an info structure attached?*/

		if(Info = (struct GadgetSupportInfo *)Gadget -> UserData)
		{
				/* Does the data make sense? */

			if(Info -> PointBack == Info && Info -> Gadget == Gadget)
			{
				if(Info -> Image && (Info -> Kind == FOLDER_KIND || Info -> Kind == FILE_KIND || Info -> Kind == SCREEN_KIND))
				{
					WORD i;

					for(i = 0 ; i < 2 ; i++)
						FreeVec(Info -> Image[i] . ImageData);

					FreeVec(Info -> Image);
				}

				if(Info -> Original && Info -> Kind == PASSWORD_KIND)
					FreeVec(Info -> Original);

				if(Info -> Title)
					FreeVec(Info -> Title);

				FreeMem(Info,sizeof(struct GadgetSupportInfo));
			}
		}

			/* Skip to next object. */

		Gadget = Gadget -> NextGadget;
	}

	OldFreeGadgets(GadgetList);
}

	/* GT_ReplyIMsg(struct IntuiMessage *Message):
	 *
	 *	A frontend to the original routine.
	 */

VOID
GT_ReplyIMsg(struct IntuiMessage *Message)
{
		/* Is this really a message? */

	if(Message)
	{
			/* The following input event classes are likely to
			 * affect a gadtools object.
			 */

		if(Message -> Class == IDCMP_GADGETUP || Message -> Class == IDCMP_GADGETDOWN || Message -> Class == IDCMP_MOUSEMOVE)
		{
			struct Gadget *Gadget;

				/* Take a look at the item attached. */

			if(Gadget = (struct Gadget *)Message -> IAddress)
			{
				struct GadgetSupportInfo *Info = Gadget -> UserData;

					/* If it's really a gadget, does it
					 * have an info structure attached?
					 */

				if(Info)
				{
						/* Does the data make sense? */

					if(Info -> PointBack == Info && Info -> Gadget == Gadget)
					{
						LONG Value = (WORD)Message -> Code;

							/* Update the object information. */

						switch(Info -> Kind)
						{
							case CHECKBOX_KIND:

								if(Gadget -> Flags & GFLG_SELECTED)
									Info -> Current = TRUE;
								else
									Info -> Current = FALSE;

								break;

							case CYCLE_KIND:

								Info -> Current = Message -> Code;

								break;

							case LISTVIEW_KIND:

								Info -> Current = Message -> Code;

								break;

							case MX_KIND:

								Info -> Current = Message -> Code;

								break;

							case SCROLLER_KIND:

								Info -> Current = Message -> Code;

								break;

							case SLIDER_KIND:

								if(Info -> Min >= 0 && Value < 0)
									Info -> Current = Message -> Code;
								else
									Info -> Current = Value;

								break;

							case PALETTE_KIND:

								Info -> Current = Message -> Code;

								break;

							default:

								break;
						}
					}
				}
			}
		}
	}

		/* Reply the message correctly. */

	OldGT_ReplyIMsg(Message);
}

	/* KeySelect():
	 *
	 *	Handles gadget keystroke activation.
	 */

VOID
KeySelect(struct Gadget **GadgetList,WORD Last,UBYTE Key,struct Window *Window,struct Gadget **Gadget,ULONG *IClass,LONG *Code)
{
	if(*IClass == IDCMP_VANILLAKEY)
	{
			/* If an escape code comes in and the window does in
			 * fact have a close gadget, return a close window
			 * event.
			 */

		if(Key == '\033' && (Window -> Flags & WFLG_CLOSEGADGET))
			*IClass = IDCMP_CLOSEWINDOW;
		else
		{
			struct GadgetSupportInfo	*Info;
			WORD				 i;
			BYTE				 Forward,
							 GotIt = FALSE;

				/* Run down all the gadgets. */

			for(i = 0 ; !GotIt && i <= Last ; i++)
			{
					/* Is there a gadget? */

				if(GadgetList[i])
				{
						/* Is this gadget enabled? */

					if(!(GadgetList[i] -> Flags & GFLG_DISABLED))
					{
							/* Does it have an info structure attached? */

						if(Info = GadgetList[i] -> UserData)
						{
								/* Does it make sense? */

							if(Info -> PointBack == Info && Info -> Gadget == GadgetList[i])
							{
									/* Do the keys match? */

								if(ToUpper(Info -> Key) == ToUpper(Key))
								{
										/* Are we cycle forward
										 * or backwards?
										 */

									if(ToUpper(Info -> Key) == Key)
										Forward = FALSE;
									else
										Forward = TRUE;

										/* Update the object information. */

									switch(Info -> Kind)
									{
										case PASSWORD_KIND:
										case STRING_KIND:
										case INTEGER_KIND:

											ActivateGadget(ActiveGadget = GadgetList[i],Window,NULL);

											*IClass	= IDCMP_GADGETDOWN;
											*Code	= 0;
											*Gadget	= GadgetList[i];

											GotIt = TRUE;

											break;

										case BUTTON_KIND:

											*IClass	= IDCMP_GADGETUP;
											*Code	= 0;
											*Gadget	= GadgetList[i];

											GotIt = TRUE;

											break;

										case CHECKBOX_KIND:

											GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
												GTCB_Checked,	Info -> Current ? FALSE : TRUE,
											TAG_DONE);

											*IClass	= IDCMP_GADGETUP;
											*Code	= Info -> Current;
											*Gadget	= GadgetList[i];

											GotIt = TRUE;

											break;

										case CYCLE_KIND:

											if(Forward)
											{
												GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
													GTCY_Active,	(Info -> Current == Info -> Max) ? 0 : Info -> Current + 1,
												TAG_DONE);
											}
											else
											{
												GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
													GTCY_Active,	Info -> Current ? Info -> Current - 1 : Info -> Max,
												TAG_DONE);
											}

											*IClass	= IDCMP_GADGETUP;
											*Code	= Info -> Current;
											*Gadget	= GadgetList[i];

											GotIt = TRUE;

											break;

										case LISTVIEW_KIND:

											if(Forward)
											{
												if(Info -> Current == ~0)
													Info -> Current = -1;

												if(Info -> Current < Info -> Max)
												{
													if(Info -> ReadOnly)
													{
														GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
															GTLV_Top,		Info -> Current + 1,
															GTLV_MakeVisible,	Info -> Current + 1,
														TAG_DONE);
													}
													else
													{
														GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
															GTLV_Selected,		Info -> Current + 1,
															GTLV_Top,		Info -> Current + 1,
															GTLV_MakeVisible,	Info -> Current + 1,
														TAG_DONE);
													}

													*IClass	= IDCMP_GADGETUP;
													*Code	= Info -> Current;
													*Gadget	= GadgetList[i];
												}
											}
											else
											{
												if(Info -> Current == ~0)
													Info -> Current = Info -> Max + 1;

												if(Info -> Current > Info -> Min)
												{
													if(Info -> ReadOnly)
													{
														GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
															GTLV_Top,		Info -> Current - 1,
															GTLV_MakeVisible,	Info -> Current - 1,
														TAG_DONE);
													}
													else
													{
														GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
															GTLV_Selected,		Info -> Current - 1,
															GTLV_Top,		Info -> Current - 1,
															GTLV_MakeVisible,	Info -> Current - 1,
														TAG_DONE);
													}

													*IClass	= IDCMP_GADGETUP;
													*Code	= Info -> Current;
													*Gadget	= GadgetList[i];
												}
											}

											GotIt = TRUE;

											break;

										case MX_KIND:

											if(Forward)
											{
												if(Info -> Current < Info -> Max)
												{
													GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
														GTMX_Active,	*Code = Info -> Current + 1,
													TAG_DONE);

													*IClass	= IDCMP_GADGETUP;
													*Code	= Info -> Current;
													*Gadget	= GadgetList[i];
												}
											}
											else
											{
												if(Info -> Current > Info -> Min)
												{
													GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
														GTMX_Active,	*Code = Info -> Current - 1,
													TAG_DONE);

													*IClass	= IDCMP_GADGETUP;
													*Code	= Info -> Current;
													*Gadget	= GadgetList[i];
												}
											}

											GotIt = TRUE;

											break;

										case PALETTE_KIND:

											if(Forward)
											{
												GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
													GTPA_Color,	(Info -> Current == Info -> Max) ? 0 : Info -> Current + 1,
												TAG_DONE);
											}
											else
											{
												GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
													GTPA_Color,	Info -> Current ? Info -> Current - 1 : Info -> Max,
												TAG_DONE);
											}

											*IClass	= IDCMP_GADGETUP;
											*Code	= Info -> Current;
											*Gadget	= GadgetList[i];

											GotIt = TRUE;

											break;

										case SCROLLER_KIND:

											if(Forward)
											{
												if(Info -> Current < Info -> Max)
												{
													GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
														GTSC_Top,	Info -> Current + 1,
													TAG_DONE);

													*IClass	= IDCMP_MOUSEMOVE;
													*Code	= Info -> Current;
													*Gadget	= GadgetList[i];
												}
											}
											else
											{
												if(Info -> Current > Info -> Min)
												{
													GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
														GTSC_Top,	Info -> Current - 1,
													TAG_DONE);

													*IClass	= IDCMP_MOUSEMOVE;
													*Code	= Info -> Current;
													*Gadget	= GadgetList[i];
												}
											}

											GotIt = TRUE;

											break;

										case SLIDER_KIND:

											if(Forward)
											{
												if(Info -> Current < Info -> Max)
												{
													GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
														GTSL_Level,	Info -> Current + 1,
													TAG_DONE);

													*IClass	= IDCMP_MOUSEMOVE;
													*Code	= Info -> Current;
													*Gadget	= GadgetList[i];
												}
											}
											else
											{
												if(Info -> Current > Info -> Min)
												{
													GT_SetGadgetAttrs(GadgetList[i],Window,NULL,
														GTSL_Level,	Info -> Current - 1,
													TAG_DONE);

													*IClass	= IDCMP_MOUSEMOVE;
													*Code	= Info -> Current;
													*Gadget	= GadgetList[i];
												}
											}

											GotIt = TRUE;

											break;
									}
								}
							}
						}
					}
				}
			}

				/* If no matching object was found, take a closer look at the types. */

			if(!GotIt)
			{
				for(i = 0 ; i <= Last ; i++)
				{
						/* If it's a string object, activate it. */

					if(GadgetList[i])
					{
						if(!(GadgetList[i] -> Flags & GFLG_DISABLED) && (GadgetList[i] -> GadgetType & GTYP_GTYPEMASK) == GTYP_STRGADGET)
						{
							ActivateGadget(ActiveGadget = GadgetList[i],Window,NULL);

							*IClass	= IDCMP_GADGETDOWN;
							*Code	= 0;
							*Gadget	= GadgetList[i];

							break;
						}
					}
				}
			}
		}
	}

		/* Conjure up the AmigaGuide help window. */

	if(*IClass == IDCMP_RAWKEY && *Code == 95)
		GuideSetup();
}

	/* SZ_ResetMaxWidth():
	 *
	 *	Reset maximum object width and return current maximum width.
	 */

LONG
SZ_ResetMaxWidth()
{
	LONG Width = SZ_WidthMax;

	SZ_WidthMax = 0;

	return(Width);
}

	/* SZ_UpdateMaxWidth():
	 *
	 *	Update current maximum object width.
	 */

VOID __regargs
SZ_UpdateMaxWidth(UBYTE Type,STRPTR Title,LONG Chars,STRPTR *Choices)
{
	LONG Width = SZ_Width(Type,Title,Chars,Choices);

	if(Width > SZ_WidthMax)
		SZ_WidthMax = Width;
}

	/* SZ_GetWindowWidth():
	 *
	 *	Get the current window width.
	 */

LONG
SZ_GetWindowWidth()
{
	return(SZ_WindowWidth);
}

	/* SZ_GetWindowHeight():
	 *
	 *	Get the current window height.
	 */

LONG
SZ_GetWindowHeight()
{
	return(SZ_WindowHeight + SZ_Screen -> WBorBottom);
}

	/* SZ_GetLeftEdge():
	 *
	 *	Get the current object left edge.
	 */

LONG
SZ_GetLeftEdge()
{
	return(SZ_CurrentLeft);
}

	/* SZ_GetTopEdge():
	 *
	 *	Get the current object top edge.
	 */

LONG
SZ_GetTopEdge()
{
	return(SZ_CurrentTop);
}

	/* SZ_SetTopEdge(LONG Top):
	 *
	 *	Set the current object top edge.
	 */

VOID __regargs
SZ_SetTopEdge(LONG Top)
{
	SZ_CurrentTop = Top;
}

	/* SZ_SetLeftEdge(LONG Left):
	 *
	 *	Set the current object left edge.
	 */

VOID __regargs
SZ_SetLeftEdge(LONG Left)
{
	SZ_CurrentLeft = Left;
}

	/* SZ_SetAbsoluteTop(LONG Top):
	 *
	 *	Set new inner window top edge.
	 */

VOID __regargs
SZ_SetAbsoluteTop(LONG Top)
{
	SZ_Top = Top;
}

	/* SZ_SetAbsoluteLeft(LONG Left):
	 *
	 *	Set new inner window left edge.
	 */

VOID __regargs
SZ_SetAbsoluteLeft(LONG Left)
{
	SZ_Left = Left;
}

	/* SZ_SetWidth(LONG Width):
	 *
	 *	Set current object width.
	 */

VOID __regargs
SZ_SetWidth(LONG Width)
{
	SZ_CurrentWidth = Width;
}

VOID
SZ_InitWidth()
{
	SZ_WindowWidth = SZ_Left + SZ_CurrentWidth + InterWidth;
}

	/* SZ_GetDimension(LONG *Width,LONG *Height):
	 *
	 *	Get current window dimensions.
	 */

VOID __regargs
SZ_GetDimension(LONG *Width,LONG *Height)
{
	*Width	= SZ_WindowWidth;
	*Height	= SZ_WindowHeight + SZ_Screen -> WBorBottom;
}

	/* SZ_GetChecked(struct Gadget *Gadget):
	 *
	 *	Return whether a checkbox gadget is checked or not.
	 */

BYTE __regargs
SZ_GetChecked(struct Gadget *Gadget)
{
	if(Gadget -> Flags & GFLG_SELECTED)
		return((BYTE)TRUE);
	else
		return((BYTE)FALSE);
}

	/* SZ_GetInteger(struct Gadget *Gadget):
	 *
	 *	Return the current settings of an integer gadget.
	 */

LONG __regargs
SZ_GetInteger(struct Gadget *Gadget)
{
	if((Gadget -> GadgetType & GTYP_GTYPEMASK) == GTYP_STRGADGET)
		return(((struct StringInfo *)(Gadget -> SpecialInfo)) -> LongInt);
	else
		return(0);
}

	/* SZ_GetString(struct Gadget *Gadget):
	 *
	 *	Returns the contents of a string gadet.
	 */

STRPTR __regargs
SZ_GetString(struct Gadget *Gadget)
{
	struct GadgetSupportInfo *Info;

		/* Does the object in question have an info structure attached? */

	if(Info = (struct GadgetSupportInfo *)Gadget -> UserData)
	{
			/* Does the data look sensible? */

		if(Info -> PointBack == Info && Info -> Gadget == Gadget)
		{
			if(Info -> Kind == PASSWORD_KIND)
				return(Info -> Buffer);
		}
	}

	if((Gadget -> GadgetType & GTYP_GTYPEMASK) == GTYP_STRGADGET)
		return(((struct StringInfo *)(Gadget -> SpecialInfo)) -> Buffer);
	else
		return("");
}

	/* SZ_TextWidth(STRPTR String):
	 *
	 *	Determine string width in pixels.
	 */

LONG __regargs
SZ_TextWidth(STRPTR String)
{
	UBYTE	TempString[256];
	WORD	i,j;
	BYTE	LastSpace = FALSE;

	for(i = j = 0 ; String[i] ; i++)
	{
		if(String[i] == ' ')
			LastSpace = TRUE;
		else
			LastSpace = FALSE;

		if(String[i] != '_')
			TempString[j++] = String[i];
	}

	if(LastSpace)
	{
		TempString[j] = 0;

		String = NewTitle(TempString,0,NULL,FALSE);

		j = strlen(String);

		return(TextLength(SZ_RPort,String,j));
	}
	else
		return(TextLength(SZ_RPort,TempString,j));
}

	/* SZ_AddLeftOffset(LONG Offset):
	 *
	 *	Update current object left offset.
	 */

VOID __regargs
SZ_AddLeftOffset(LONG Offset)
{
	SZ_CurrentLeft += Offset;
}

	/* SZ_LeftOffsetN(LONG DataArray,...):
	 *
	 *	Determine the maximum length of a number of
	 *	gadget labels (first, second, third item, -1 terminates
	 *	the array).
	 */

LONG __stdargs
SZ_LeftOffsetN(LONG DataArray,...)
{
	extern STRPTR __regargs	LocaleString(LONG ID);

	LONG	*Data = &DataArray,
		 Len,
		 Max = 0;

	while(*Data != -1)
	{
		if((Len = SZ_TextWidth(LocaleString(*Data++))) > Max)
			Max = Len;
	}

	return(Max + INTERWIDTH);
}

	/* SZ_LeftOffsetDelta(LONG From,LONG To):
	 *
	 *	Determine the maximum length of a number of
	 *	gadget labels (given as first and last gadget).
	 */

LONG __regargs
SZ_LeftOffsetDelta(LONG From,LONG To)
{
	extern STRPTR __regargs	LocaleString(LONG ID);

	LONG	i,
		Len,
		Max = 0;

	for(i = From ; i <= To ; i++)
	{
		if((Len = SZ_TextWidth(LocaleString(i))) > Max)
			Max = Len;
	}

	return(Max + INTERWIDTH);
}

	/* SZ_SizeCleanup():
	 *
	 *	Free data allocated by SZ_SizeSetup().
	 */

VOID
SZ_SizeCleanup()
{
	if(SZ_TextFont)
	{
		CloseFont(SZ_TextFont);

		SZ_TextFont = NULL;
	}

	if(SZ_RPort)
	{
		FreeVec(SZ_RPort);

		SZ_RPort = NULL;
	}
}

	/* SZ_SizeSetup(struct Screen *Screen,struct TextAttr *TextAttr,BYTE Title):
	 *
	 *	Perform setups for gadget creation.
	 */

BYTE __regargs
SZ_SizeSetup(struct Screen *Screen,struct TextAttr *TextAttr,BYTE Title)
{
	struct DrawInfo *DrawInfo;

	SZ_SizeCleanup();

	if(DrawInfo = GetScreenDrawInfo(Screen))
	{
		SZ_TextPen = DrawInfo -> dri_Pens[TEXTPEN];

		FreeScreenDrawInfo(Screen,DrawInfo);
	}

	SZ_Screen = Screen;

	if(SZ_RPort = (struct RastPort *)AllocVec(sizeof(struct RastPort),MEMF_ANY | MEMF_CLEAR))
	{
		InitRastPort(SZ_RPort);

		if(!TextAttr)
			TextAttr = Screen -> Font;

		if(SZ_TextFont = (struct TextFont *)OpenDiskFont(TextAttr))
		{
			UWORD	i,Width;
			UBYTE	Char;

			InterWidth = INTERWIDTH;

			if(SZ_TextFont -> tf_YSize <= 8)
				InterHeight = 1;
			else
				InterHeight = SZ_TextFont -> tf_YSize / 4;

			SetFont(SZ_RPort,SZ_TextFont);

			SZ_AverageGlyphWidth	= 0;
			SZ_MaxGlyphWidth	= 0;
			SZ_MaxNumericWidth	= 0;

			for(i = 0 ; i < 256 ; i++)
			{
				Char = i;

				Width = TextLength(SZ_RPort,&Char,1);

				if(Width > SZ_MaxGlyphWidth)
					SZ_MaxGlyphWidth = Width;

				SZ_AverageGlyphWidth += Width;
			}

			SZ_AverageGlyphWidth /= 256;

			for(i = '0' ; i <= '0' ; i++)
			{
				Char = i;

				if((Width = TextLength(SZ_RPort,&Char,1)) > SZ_MaxNumericWidth)
					SZ_MaxNumericWidth = Width;
			}

			if((Width = TextLength(SZ_RPort," ",1)) > SZ_MaxNumericWidth)
				SZ_MaxNumericWidth = Width;

			if((Width = TextLength(SZ_RPort,",",1)) > SZ_MaxNumericWidth)
				SZ_MaxNumericWidth = Width;

			if((Width = TextLength(SZ_RPort,".",1)) > SZ_MaxNumericWidth)
				SZ_MaxNumericWidth = Width;

			if(SZ_AverageGlyphWidth > SZ_MaxNumericWidth)
				SZ_MaxNumericWidth = SZ_AverageGlyphWidth;

			if(Title)
				SZ_Top	= Screen -> WBorTop + Screen -> Font -> ta_YSize + 1 + InterHeight;
			else
				SZ_Top	= Screen -> WBorTop + InterHeight;

			SZ_Left		= InterWidth;

			SZ_CurrentLeft	= InterWidth;
			SZ_CurrentTop	= SZ_Top;

			SZ_WindowWidth	= 0;
			SZ_WindowHeight	= 0;

			SZ_CurrentWidth	= 0;
			SZ_MaxWidth	= 0;

			return(TRUE);
		}
	}

	SZ_SizeCleanup();

	return(FALSE);
}

	/* SZ_GetNumericLen(STRPTR String):
	 *
	 *	Get the string pixel width, measured using the maximum
	 *	numeric glyph width.
	 */

ULONG __regargs
SZ_GetNumericLen(STRPTR String)
{
	return(strlen(String) * SZ_MaxNumericWidth);
}

	/* SZ_GetGlyphLen(STRPTR String):
	 *
	 *	Get the string pixel width, measured using the maximum
	 *	glyph width.
	 */

ULONG __regargs
SZ_GetGlyphLen(STRPTR String)
{
	return(strlen(String) * SZ_MaxGlyphWidth);
}

	/* SZ_GetLen(STRPTR String):
	 *
	 *	Get the string pixel width, measured using the average
	 *	glyph width.
	 */

ULONG __regargs
SZ_GetLen(STRPTR String)
{
	return(strlen(String) * SZ_AverageGlyphWidth);
}

	/* SZ_Height(UBYTE Type,LONG Lines,BYTE Display):
	 *
	 *	Determine the height of an object.
	 */

LONG __regargs
SZ_Height(UBYTE Type,LONG Lines,BYTE Display)
{
	LONG Height = 0;

	switch(Type)
	{
		case LISTVIEW_KIND:

			Height = 4 + Lines * SZ_TextFont -> tf_YSize + 4;

			if(Display == SZLV_Display)
				Height += 2 + SZ_TextFont -> tf_YSize + 2;

			if(Display == SZLV_String)
				Height += 3 + SZ_TextFont -> tf_YSize + 3;

			break;

		case FILE_KIND:
		case FOLDER_KIND:
		case SCREEN_KIND:

			if(SZ_TextFont -> tf_YSize < 6)
				Height = 12;
			else
				Height = 3 + SZ_TextFont -> tf_YSize + 3;

			break;

		case PASSWORD_KIND:
		case INTEGER_KIND:
		case STRING_KIND:

			Height = 3 + SZ_TextFont -> tf_YSize + 3;
			break;

		case CYCLE_KIND:
		case TEXT_KIND:
		case BUTTON_KIND:

			Height = 2 + SZ_TextFont -> tf_YSize + 2;
			break;

		case CHECKBOX_KIND:

			if(SZ_TextFont -> tf_YSize < 11)
				Height = 11;
			else
				Height = SZ_TextFont -> tf_YSize;

			break;

		case SLIDER_KIND:

			Height = 1 + SZ_TextFont -> tf_YSize + 1;
			break;

		case MX_KIND:

			Height = Lines * (SZ_TextFont -> tf_YSize + 1) - 1;
			break;

		case PALETTE_KIND:

			Height = 2 * SZ_TextFont -> tf_YSize;
			break;
	}

	return(Height);
}

	/* SZ_Width(UBYTE Type,STRPTR Title,LONG Chars,STRPTR *Choices):
	 *
	 *	Determine the width of an object.
	 */

LONG __regargs
SZ_Width(UBYTE Type,STRPTR Title,LONG Chars,STRPTR *Choices)
{
	LONG	Width = 0,
		Len,Max = 0,i;

	switch(Type)
	{
		case LISTVIEW_KIND:

			Width = (4 + Chars * SZ_AverageGlyphWidth + 4) + (2 + 2 * SZ_AverageGlyphWidth + 2) + 4;	/* KLUDGE + 4 */
			break;

		case TEXT_KIND:
		case BUTTON_KIND:

			if(Title)
				Width = 6 + SZ_TextWidth(Title) + 6;
			else
				Width = 6 + Chars * SZ_AverageGlyphWidth + 6;

			break;

		case FILE_KIND:
		case FOLDER_KIND:
		case SCREEN_KIND:

			if(SZ_AverageGlyphWidth <= 10)
				Width = 20;
			else
				Width = 2 * SZ_AverageGlyphWidth;

			if(Chars)
				Width = 6 + Chars * SZ_AverageGlyphWidth + 6 - (2 + Width);

			break;

		case PASSWORD_KIND:
		case SLIDER_KIND:
		case STRING_KIND:
		case INTEGER_KIND:

			Width = 6 + Chars * SZ_AverageGlyphWidth + 6;
			break;

		case CHECKBOX_KIND:

			if(SZ_AverageGlyphWidth < 13)
				Width = 26;
			else
				Width = 2 * SZ_AverageGlyphWidth;

			break;

		case CYCLE_KIND:

			if(Choices)
			{
				for(i = 0 ; Choices[i] ; i++)
				{
					if((Len = TextLength(SZ_RPort,Choices[i],strlen(Choices[i]))) > Max)
						Max = Len;
				}

				Width = 24 + Max + 4;
			}
			else
				Width = 24 + Chars * SZ_AverageGlyphWidth + 4;

			break;

		case MX_KIND:

			if(SZ_AverageGlyphWidth < 8)
				Width = 17;
			else
				Width = 2 * SZ_AverageGlyphWidth;

			break;
	}

	return(Width);
}

	/* SZ_FreeBox(struct TextBox *Box):
	 *
	 *	Free a text box.
	 */

VOID __regargs
SZ_FreeBox(struct TextBox *Box)
{
	if(Box)
	{
		if(Box -> Text)
		{
			LONG i;

			for(i = 0 ; i < Box -> NumLines ; i++)
			{
				if(Box -> Text[i])
					FreeVec(Box -> Text[i]);
			}

			FreeVec(Box -> Text);
		}

		if(Box -> Title)
			FreeVec(Box -> Title);

		FreeVec(Box);
	}
}

	/* SZ_FreeBoxes(struct TextBox *FirstBox):
	 *
	 *	Free a number of text boxes.
	 */

VOID __regargs
SZ_FreeBoxes(struct TextBox *FirstBox)
{
	if(FirstBox)
	{
		struct TextBox *NextBox;

		do
		{
			NextBox = FirstBox -> NextBox;

			SZ_FreeBox(FirstBox);

			FirstBox = NextBox;
		}
		while(FirstBox);
	}
}

	/* SZ_BoxWidth(LONG Chars):
	 *
	 *	Determine the width of a text box.
	 */

LONG __regargs
SZ_BoxWidth(LONG Chars)
{
	return((LONG)(4 + SZ_AverageGlyphWidth * Chars + 4));
}

	/* SZ_BoxHeight(LONG Lines):
	 *
	 *	Determine the height of a text box.
	 */

LONG __regargs
SZ_BoxHeight(LONG Lines)
{
	return(2 + SZ_TextFont -> tf_YSize * Lines + 2);
}

	/* SZ_SetTitlePen(struct TextBox *Box,LONG FgPen,LONG BgPen):
	 *
	 *	Set the text box list title text rendering pens.
	 */

VOID __regargs
SZ_SetTitlePen(struct TextBox *Box,LONG FgPen,LONG BgPen)
{
	while(Box)
	{
		Box -> TitleFgPen = FgPen;
		Box -> TitleBgPen = BgPen;

		Box = Box -> NextBox;
	}
}

	/* SZ_CreateTextBox(struct TextBox **FirstBox,...):
	 *
	 *	Create a text box, this routine works similar
	 *	to the CreateGadget() frontend.
	 */

struct TextBox * __stdargs
SZ_CreateTextBox(struct TextBox **FirstBox,...)
{
	va_list				 VarArgs;
	struct TagItem			*TagList,
					*ThisTag;
	LONG				 Chars,Lines,
					 Width,
					 Height,
					 Left = SZ_CurrentLeft;
	BYTE				 AutoWidth	= FALSE,
					 MoveDown	= TRUE,
					 SetLeft	= FALSE,
					 SetBelow	= FALSE;

	struct TextBox			*Box;
	LONG				 i;

	va_start(VarArgs,FirstBox);

	TagList = (struct TagItem *)VarArgs;

	if(ThisTag = FindTagItem(SZ_Lines,TagList))
		Lines = (LONG)ThisTag -> ti_Data;
	else
		return(NULL);

	Height = 2 + SZ_TextFont -> tf_YSize * Lines + 2;

	if(ThisTag = FindTagItem(SZ_Chars,TagList))
		Chars = (LONG)ThisTag -> ti_Data;
	else
	{
		if(ThisTag = FindTagItem(SZ_AutoWidth,TagList))
			AutoWidth = ThisTag -> ti_Data;

		if(!AutoWidth)
			return(NULL);
		else
			Chars = (SZ_CurrentWidth - 8) / SZ_AverageGlyphWidth;
	}

	if(!(Box = (struct TextBox *)AllocVec(sizeof(struct TextBox),MEMF_ANY | MEMF_CLEAR)))
		return(NULL);

	if(ThisTag = FindTagItem(SZ_SaveX,TagList))
		SetLeft = ThisTag -> ti_Data;

	if(ThisTag = FindTagItem(SZ_SaveY,TagList))
		SetBelow = ThisTag -> ti_Data;

	if(ThisTag = FindTagItem(SZ_AlignBottom,TagList))
	{
		if(ThisTag -> ti_Data)
			SZ_CurrentTop = SZ_WindowHeight;
	}

	if(ThisTag = FindTagItem(SZ_AlignExtra,TagList))
	{
		if(ThisTag -> ti_Data)
			SZ_CurrentTop += InterHeight;
	}

	if(ThisTag = FindTagItem(SZ_NewColumn,TagList))
	{
		if(ThisTag -> ti_Data)
		{
			SZ_CurrentTop	= SZ_Top;
			Left		= Left + SZ_MaxWidth + InterWidth;

			SZ_MaxWidth	= 0;
		}
	}

	if(ThisTag = FindTagItem(SZ_AutoWidth,TagList))
		AutoWidth = ThisTag -> ti_Data;

	if(!AutoWidth)
		Width = SZ_BoxWidth(Chars);
	else
		Width = SZ_CurrentWidth;

	if(ThisTag = FindTagItem(SZ_AlignLeft,TagList))
	{
		if(ThisTag -> ti_Data)
		{
			Left = SZ_Left;

			MoveDown = FALSE;
		}
	}

	if(ThisTag = FindTagItem(SZ_AlignRight,TagList))
	{
		if(ThisTag -> ti_Data)
		{
			Left = SZ_WindowWidth - INTERWIDTH - Width;

			MoveDown = FALSE;
		}
	}

	if(ThisTag = FindTagItem(SZ_AlignCentre,TagList))
	{
		if(ThisTag -> ti_Data)
		{
			Left = (SZ_WindowWidth - Width) / 2;

			MoveDown = FALSE;
		}
	}

	if(ThisTag = FindTagItem(SZ_GroupCount,TagList))
	{
		LONG Count = ThisTag -> ti_Data,Width = SZ_WindowWidth;

		if(ThisTag = FindTagItem(SZ_UseLeft,TagList))
		{
			Width -= ThisTag -> ti_Data - InterWidth;

			SZ_GroupDelta	= SZ_CurrentWidth + ((Width - 2 * InterWidth) - (Count * SZ_CurrentWidth)) / (Count - 1);
			SZ_GroupX	= Left = ThisTag -> ti_Data;
		}
		else
		{
			SZ_GroupDelta	= SZ_CurrentWidth + ((Width - 2 * InterWidth) - (Count * SZ_CurrentWidth)) / (Count - 1);
			SZ_GroupX	= Left = SZ_Left;
		}

		MoveDown = FALSE;
	}
	else
	{
		if(ThisTag = FindTagItem(SZ_UseLeft,TagList))
			Left = ThisTag -> ti_Data;
	}

	if(ThisTag = FindTagItem(SZ_GroupNext,TagList))
	{
		if(ThisTag -> ti_Data)
		{
			SZ_GroupX += SZ_GroupDelta;

			Left = SZ_GroupX;

			MoveDown = FALSE;
		}
	}

	if(ThisTag = FindTagItem(SZ_UseTop,TagList))
		SZ_CurrentTop = ThisTag -> ti_Data;

	Box -> Left		= Left;
	Box -> Top		= SZ_CurrentTop;
	Box -> Width		= Width;
	Box -> Height		= Height;

	Box -> LineWidth	= Chars * SZ_AverageGlyphWidth;
	Box -> LineHeight	= SZ_TextFont -> tf_YSize;

	Box -> NumChars		= Chars;
	Box -> NumLines		= Lines;

	Box -> TitleFgPen	= SZ_TextPen;
	Box -> TitleBgPen	= 0;
	Box -> TextPen		= SZ_TextPen;

	if(!(Box -> Title = (STRPTR *)AllocVec(sizeof(STRPTR) * Lines,MEMF_ANY | MEMF_CLEAR)))
	{
		SZ_FreeBox(Box);

		return(NULL);
	}

	if(!(Box -> Text = (STRPTR *)AllocVec(sizeof(STRPTR) * Lines,MEMF_ANY | MEMF_CLEAR)))
	{
		SZ_FreeBox(Box);

		return(NULL);
	}

	for(i = 0 ; i < Lines ; i++)
	{
		if(!(Box -> Text[i] = (STRPTR)AllocVec(Chars + 1,MEMF_ANY | MEMF_CLEAR)))
		{
			SZ_FreeBox(Box);

			return(NULL);
		}
	}

	if(SetBelow)
		MoveDown = FALSE;

	if(MoveDown)
	{
		SZ_CurrentTop = SZ_CurrentTop + Height + InterHeight;

		if(SZ_CurrentTop > SZ_WindowHeight)
			SZ_WindowHeight = SZ_CurrentTop;
	}
	else
	{
		if(SZ_CurrentTop + Height + InterHeight > SZ_WindowHeight)
			SZ_WindowHeight = SZ_CurrentTop + Height + InterHeight;

		SZ_MaxWidth = 0;
	}

	if(Width > SZ_MaxWidth)
		SZ_MaxWidth = Width;

	if(Left + SZ_MaxWidth + INTERWIDTH > SZ_WindowWidth)
		SZ_WindowWidth = Left + SZ_MaxWidth + INTERWIDTH;

	if(!SetLeft)
		SZ_CurrentLeft = Left;

	if(!(*FirstBox))
		*FirstBox = Box;
	else
	{
		struct TextBox *CurrentBox = *FirstBox;

		while(CurrentBox -> NextBox)
			CurrentBox = CurrentBox -> NextBox;

		CurrentBox -> NextBox = Box;
	}

	return(Box);
}

	/* SZ_SetBoxTitles(struct TextBox *Box,STRPTR Array,...):
	 *
	 *	Set the titles displayed in a text box.
	 */

VOID __stdargs
SZ_SetBoxTitles(struct TextBox *Box,STRPTR Array,...)
{
	if(Box)
	{
		STRPTR	*Data = &Array;
		LONG	 i = 0;

		while(*Data != NULL)
		{
			if(i < Box -> NumLines)
				Box -> Title[i++] = *Data;

			Data++;
		}
	}
}

	/* SZ_SetLine():
	 *
	 *	Print a string into a text box, plain version.
	 */

VOID __regargs
SZ_SetLine(struct RastPort *RPort,struct TextBox *Box,LONG Line,STRPTR String)
{
	BYTE		 FgPen	= ReadAPen(RPort),
			 BgPen	= ReadBPen(RPort),
			 DrMd	= ReadDrMd(RPort);
	struct TextFont	*Font	= RPort -> Font;

	LONG		 Width,Len,
			 Left	= Box -> Left + 4,
			 Top	= Box -> Top + 2 + Line * Box -> LineHeight;

	if(DrMd != JAM2)
		SetDrMd(RPort,JAM2);

	if(FgPen != Box -> TextPen)
		SetAPen(RPort,Box -> TextPen);

	if(BgPen != 0)
		SetBPen(RPort,0);

	if(Font != UserTextFont)
		SetFont(RPort,UserTextFont);

	if(Len = strlen(String))
	{
		if(Len > Box -> NumChars)
			Len = Box -> NumChars;

		while(Len > 0 && TextLength(RPort,String,Len) > Box -> LineWidth)
			Len--;

		if(Len)
		{
			Width = TextLength(RPort,String,Len);

			Move(RPort,Left,Top + RPort -> Font -> tf_Baseline);
			Text(RPort,String,Len);
		}
		else
			Width = 0;
	}
	else
		Width = 0;

	if(Width != Box -> LineWidth)
	{
		if(FgPen != 0)
		{
			SetAPen(RPort,0);

			RectFill(RPort,Left + Width,Top,Left + Box -> LineWidth - 1,Top + Box -> LineHeight - 1);

			SetAPen(RPort,FgPen);
		}
		else
			RectFill(RPort,Left + Width,Top,Left + Box -> LineWidth - 1,Top + Box -> LineHeight - 1);
	}

	if(DrMd != JAM2)
		SetDrMd(RPort,DrMd);

	if(String != Box -> Text[Line])
	{
		if(Len > 0)
			memcpy(Box -> Text[Line],String,Len);

		Box -> Text[Line][Len] = 0;
	}

	if(Font != UserTextFont)
		SetFont(RPort,Font);

	if(BgPen != 0)
		SetBPen(RPort,BgPen);

	SetAPen(RPort,FgPen);
}

	/* SZ_PrintLine():
	 *
	 *	Print a string into a text box, varargs version.
	 */

VOID __stdargs
SZ_PrintLine(struct RastPort *RPort,struct TextBox *Box,LONG Line,STRPTR String,...)
{
	extern VOID __stdargs VSPrintf(APTR,STRPTR,va_list);

	va_list	VarArgs;
	UBYTE	Buffer[256];

	va_start(VarArgs,String);
	VSPrintf(Buffer,String,VarArgs);
	va_end(VarArgs);

	SZ_SetLine(RPort,Box,Line,Buffer);
}

	/* SZ_DrawBox(struct RastPort *RPort,struct TextBox *Box):
	 *
	 *	(Re-)Draw a text box.
	 */

VOID __regargs
SZ_DrawBox(struct RastPort *RPort,struct TextBox *Box)
{
	if(Box)
	{
		LONG		 LineY,i,Len,FgPen = ReadAPen(RPort),BgPen = ReadBPen(RPort),DrMd = ReadDrMd(RPort);
		struct TextFont	*Font = RPort -> Font;

		if(Font != UserTextFont)
			SetFont(RPort,UserTextFont);

		if(FgPen != 0)
		{
			SetAPen(RPort,0);

			RectFill(RPort,Box -> Left,Box -> Top,Box -> Left + Box -> Width - 1,Box -> Top + Box -> Height - 1);

			SetAPen(RPort,FgPen);
		}
		else
			RectFill(RPort,Box -> Left,Box -> Top,Box -> Left + Box -> Width - 1,Box -> Top + Box -> Height - 1);

		DrawBevelBox(RPort,Box -> Left,Box -> Top,Box -> Width,Box -> Height,
			GT_VisualInfo,	VisualInfo,
			GTBB_Recessed,	TRUE,
		TAG_DONE);

		LineY = Box -> Top + 2 + RPort -> Font -> tf_Baseline;

		SetAPen(RPort,Box -> TitleFgPen);
		SetBPen(RPort,Box -> TitleBgPen);
		SetDrMd(RPort,JAM2);

		for(i = 0 ; i < Box -> NumLines ; i++)
		{
			if(Len = strlen(Box -> Title[i]))
			{
				Move(RPort,Box -> Left - INTERWIDTH - TextLength(RPort,Box -> Title[i],Len),LineY);
				Text(RPort,Box -> Title[i],Len);
			}

			LineY += Box -> LineHeight;
		}

		for(i = 0 ; i < Box -> NumLines ; i++)
			SZ_PrintLine(RPort,Box,i,Box -> Text[i]);

		SetAPen(RPort,FgPen);
		SetBPen(RPort,BgPen);

		SetDrMd(RPort,DrMd);

		if(Font != UserTextFont)
			SetFont(RPort,Font);
	}
}

	/* SZ_DrawBoxes(struct RastPort *RPort,struct TextBox *FirstBox):
	 *
	 *	(Re-)Draw a number of text boxes.
	 */

VOID __regargs
SZ_DrawBoxes(struct RastPort *RPort,struct TextBox *FirstBox)
{
	do
		SZ_DrawBox(RPort,FirstBox);
	while(FirstBox = FirstBox -> NextBox);
}

	/* SZ_MoveBox(struct TextBox *Box,LONG Left,LONG Top):
	 *
	 *	Change the location of a text box.
	 */

VOID __regargs
SZ_MoveBox(struct TextBox *Box,LONG Left,LONG Top)
{
	Box -> Left	+= Left;
	Box -> Top	+= Top;
}

	/* SZ_MoveBoxes(struct TextBox *FirstBox,LONG Left,LONG Top):
	 *
	 *	Change the locations of a number of text boxes.
	 */

VOID __regargs
SZ_MoveBoxes(struct TextBox *FirstBox,LONG Left,LONG Top)
{
	do
		SZ_MoveBox(FirstBox,Left,Top);
	while(FirstBox = FirstBox -> NextBox);
}

	/* SZ_SetBox(struct TextBox *Box,LONG Left,LONG Top):
	 *
	 *	Set the location of a text box.
	 */

VOID __regargs
SZ_SetBox(struct TextBox *Box,LONG Left,LONG Top)
{
	if(Left >= 0)
		Box -> Left = Left;

	if(Top >= 0)
		Box -> Top = Top;
}

	/* SZ_SetBoxes(struct TextBox *FirstBox,LONG Left,LONG Top):
	 *
	 *	Set the locations of a number of text boxes.
	 */

VOID __regargs
SZ_SetBoxes(struct TextBox *FirstBox,LONG Left,LONG Top)
{
	do
		SZ_SetBox(FirstBox,Left,Top);
	while(FirstBox = FirstBox -> NextBox);
}

	/* SZ_GetBoxInfo(struct TextBox *Box,LONG Type):
	 *
	 *	Query information on a certain text box.
	 */

LONG __regargs
SZ_GetBoxInfo(struct TextBox *Box,LONG Type)
{
	switch(Type)
	{
		case BOX_LEFT:

			return(Box -> Left);

		case BOX_TOP:

			return(Box -> Top);

		case BOX_WIDTH:

			return(Box -> Width);

		case BOX_HEIGHT:

			return(Box -> Height);

		default:

			return(0);
	}
}
