/*  GadTools layout toolkit
**
**  Copyright © 1993-1994 by Olaf `Olsen' Barthel
**  Freely distributable.
*/

#include <exec/execbase.h>
#include <exec/memory.h>

#include <graphics/videocontrol.h>
#include <graphics/gfxmacros.h>
#include <graphics/gfxbase.h>

#include <intuition/intuitionbase.h>
#include <intuition/gadgetclass.h>
#include <intuition/imageclass.h>
#include <intuition/classusr.h>
#include <intuition/icclass.h>
#include <intuition/screens.h>
#include <intuition/classes.h>
#include <intuition/sghooks.h>
#include <libraries/gadtools.h>
#include <libraries/locale.h>
#include <devices/timer.h>

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <dos.h>

#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/diskfont_protos.h>
#include <clib/utility_protos.h>
#include <clib/keymap_protos.h>
#include <clib/locale_protos.h>
#include <clib/exec_protos.h>
#include <clib/macros.h>

#include <pragmas/intuition_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/gadtools_pragmas.h>
#include <pragmas/diskfont_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/keymap_pragmas.h>
#include <pragmas/locale_pragmas.h>
#include <pragmas/exec_pragmas.h>

#define _GTLAYOUT_C
#include "gtlayout.h"


/*****************************************************************************/


	// Keyboard qualifiers

#define QUALIFIER_SHIFT		(IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
#define QUALIFIER_ALT		(IEQUALIFIER_LALT | IEQUALIFIER_RALT)
#define QUALIFIER_CONTROL	(IEQUALIFIER_CONTROL)


/*****************************************************************************/


	// Undefine these to remove various chunks of support from this code

#ifdef LIB_CODE
#define DO_PASSWORD_KIND
#define DO_FRACTION_KIND
#define DO_GAUGE_KIND
#define DO_TAPEDECK_KIND
#define DO_HEXHOOK
#define DO_PICKSHORTCUTS
#define DO_CLONING
#else
#define DO_PASSWORD_KIND
//#define DO_FRACTION_KIND
#define DO_GAUGE_KIND
//#define DO_HEXHOOK
#define DO_PICKSHORTCUTS
#define DO_CLONING
#define DO_TAPEDECK_KIND
#endif	/* LIB_CODE */


/*****************************************************************************/


	// Stuff from amiga.lib that we want to declare stdargs/regargs

VOID __regargs	NewList(struct MinList *list);
VOID __regargs	TimeDelay(LONG unit,ULONG secs,ULONG micros);
ULONG __regargs	DoSuperMethodA(struct IClass *cl, Object *obj, Msg message);
APTR __asm	AsmCreatePool(register __d0 ULONG MemFlags,register __d1 ULONG PuddleSize,register __d2 ULONG ThreshSize,register __a6 struct ExecBase *SysBase);
VOID __asm	AsmDeletePool(register __a0 APTR PoolHeader,register __a6 struct ExecBase *SysBase);
APTR __asm	AsmAllocPooled(register __a0 APTR PoolHeader, register __d0 ULONG Size,register __a6 struct ExecBase *SysBase);
VOID __asm	AsmFreePooled(register __a0 APTR PoolHeader,register __a1 APTR Memory,register __d0 ULONG MemSize,register __a6 struct ExecBase *SysBase);

VOID __stdargs	kprintf(STRPTR,...);


/*****************************************************************************/


	// Scan an Exec list

#define SCANLIST(l,n)	for(n = (APTR)((struct MinList *)l) -> mlh_Head; \
			((struct MinNode *)n) -> mln_Succ; \
			n = (APTR)((struct MinNode *)n) -> mln_Succ)

	// Scan the objects of a group

#define SCANGROUP(g,n)	for(n = (APTR)g -> Special . Group . ObjectList . mlh_Head; \
			((struct MinNode *)n) -> mln_Succ; \
			n = (APTR)((struct MinNode *)n) -> mln_Succ)

	// Scan the objects of a group, only allowing active pages

#define SCANPAGE(g,n,p)	for(p = 0, n = (APTR)g -> Special . Group . ObjectList . mlh_Head; \
			((struct MinNode *)n) -> mln_Succ; \
			n = (APTR)((struct MinNode *)n) -> mln_Succ) \
			if ((p++ == g -> Special . Group . ActivePage) || !g -> Special . Group . Paging)

	// Get an object pointer from a gadget pointer

#define GETOBJECT(g,n)	((n = (ObjectNode *)g -> UserData) && (n -> Host == g) && (n -> PointBack == n))


/*****************************************************************************/


#ifdef DO_PASSWORD_KIND
#define TEST_PASSWORD_KIND(n) || (n -> Type == PASSWORD_KIND)
#else
#define TEST_PASSWORD_KIND(n) || (FALSE)
#endif

#ifdef DO_FRACTION_KIND
#define TEST_FRACTION_KIND(n) || (n -> Type == FRACTION_KIND)
#else
#define TEST_FRACTION_KIND(n) || (FALSE)
#endif

	// Is an object derived from STRING_KIND?

#define LIKE_STRING_KIND(n)	((n -> Type == STRING_KIND) TEST_PASSWORD_KIND(n) TEST_FRACTION_KIND(n))


/*****************************************************************************/


typedef UBYTE BOOLEAN;

#define INCREMENTER_KIND	42
#define PICKER_KIND		43
#define GROUP_KIND		44

typedef struct GaugeExtra
{
	LONG			 InfoLength;
	STRPTR			 InfoText;
} GaugeExtra;

typedef struct TapeDeckExtra
{
	struct Image		*ButtonImage;
	LONG			 ButtonWidth,
				 ButtonHeight;
	BOOLEAN			 ButtonType,
				 Toggle,
				 Pressed,
				 Smaller;
} TapeDeckExtra;

typedef struct ButtonExtra
{
	BOOLEAN			 ReturnKey;
	BOOLEAN			 EscKey;
	BOOLEAN			 ExtraFat;
} ButtonExtra;

typedef struct BarExtra
{
	struct ObjectNode	*Parent;
	BOOLEAN			 FullSize;
} BarExtra;

typedef struct BoxExtra
{
	STRPTR			*Labels;
	STRPTR			*Lines;
	AlignmentTypes		 AlignText;
	WORD			 MaxSize;
	BOOLEAN			 DrawBox;
	BOOLEAN			 ReserveSpace;
} BoxExtra;

typedef struct FrameExtra
{
	UWORD			 InnerWidth;
	UWORD			 InnerHeight;
	BOOLEAN			 DrawBox;
} FrameExtra;

typedef struct PickerExtra
{
	struct Gadget		*Parent;
	struct Image		*Image;
} PickerExtra;

typedef struct IncrementerExtra
{
	struct Gadget		*Parent;
	struct Image		*Image;
	LONG			 Amount;
} IncrementerExtra;

typedef struct GroupExtra
{
	struct MinList		 ObjectList;
	struct ObjectNode	*ParentGroup;
	UWORD			 ExtraLeft;
	UWORD			 ExtraTop;
	LONG			 MaxOffset;
	LONG			 MaxSize;
	ULONG			 ActivePage;
	BOOLEAN			 Horizontal;
	BOOLEAN			 Paging;
	BOOLEAN			 Spread;
	BOOLEAN			 SameSize;
	BOOLEAN			 LastAttributes;
} GroupExtra;

typedef struct ListExtra
{
	struct List		*Labels;
	struct Gadget		*Link;
	LONG			 LinkID;
	struct Hook		*CallBack;
	ULONG			 ClickSeconds;
	ULONG			 ClickMicros;
	STRPTR			*ExtraLabels;
	UWORD			 ExtraLabelWidth;
	UWORD			 MaxPen;
	UWORD			 MaxGrowX,MaxGrowY;
	BOOLEAN			 ReadOnly;
	BOOLEAN			 CursorKey;
	BOOLEAN			 AllocatedList;
} ListExtra;

typedef struct RadioExtra
{
	STRPTR			*Choices;
	UWORD			 LabelWidth;
	UBYTE			 TitlePlace;
} RadioExtra;

typedef struct TextExtra
{
	STRPTR			 Text;
	struct Gadget		*Picker;
	UWORD			 Len;
	BOOLEAN			 Border;
	BOOLEAN			 CopyText;
	BOOLEAN			 UsePicker;
	UBYTE			 Justification;
} TextExtra;

typedef struct NumberExtra
{
	LONG			 Number;
	BOOLEAN			 Border;
	UBYTE			 Justification;
} NumberExtra;

typedef struct CycleExtra
{
	STRPTR			*Choices;
} CycleExtra;

typedef struct PalExtra
{
	UBYTE			*ColourTable;
	UWORD			 Depth;
	UWORD			 NumColours;
	BOOLEAN			 SmallPalette;
} PalExtra;

typedef struct SliderExtra
{
	STRPTR			 LevelFormat;
	DISPFUNC		 DispFunc;
	LONG			 MaxLevelLen;
	LONG			 LevelWidth;
	STRPTR			 OriginalLabel;
	UBYTE			 LevelPlace;
	BOOLEAN			 FullLevelCheck;
} SliderExtra;

typedef struct StringExtra
{
	struct LayoutHandle	*LayoutHandle;
	struct Hook		*HistoryHook;
	ULONG			 MaxHistoryLines;
	ULONG			 NumHistoryLines;
	struct Node		*CurrentNode;

	STRPTR			 String;
	LONG			 MaxChars;
	struct Hook		*EditHook;
	struct Gadget		*Picker;
	STRPTR			 RealString;
	STRPTR			 Original;
	LONG			 LinkID;
	LONG			 Min;
	LONG			 Max;
	UBYTE			 Justification;
	BOOLEAN			 LastGadget;
	BOOLEAN			 UsePicker;
} StringExtra;

typedef struct IntegerExtra
{
	struct LayoutHandle	*LayoutHandle;
	struct Hook		*HistoryHook;
	ULONG			 MaxHistoryLines;
	ULONG			 NumHistoryLines;
	struct Node		*CurrentNode;

	LONG			 Number;
	LONG			 MaxChars;
	struct Hook		*EditHook;
	LONG			 Min;
	LONG			 Max;
	UBYTE			 Justification;
	BOOLEAN			 LastGadget;
	BOOLEAN			 UseIncrementers;

	struct Gadget		*LeftIncrementer;
	struct Gadget		*RightIncrementer;

	struct Hook		*IncrementerHook;
} IntegerExtra;

typedef struct ScrollerExtra
{
	WORD			 Visible,
				 ArrowSize;
	BOOLEAN			 RelVerify,
				 Immediate,
				 Arrows,
				 Vertical,
				 Thin;
} ScrollerExtra;

typedef struct ObjectNode
{
	struct MinNode		 Node;
	struct Gadget		*Host;
	struct ObjectNode	*PointBack;
	STRPTR			 Label;
	LONG			 ID;
	UBYTE			 Type;
	PlacementTypes		 LabelPlace;
	BOOLEAN			 Disabled;
	PlacementTypes		 ObjectPlace;
	LONG			 Min;
	LONG			 Max;
	LONG			 Current;
	UWORD			 Left;
	UWORD			 Top;
	UWORD			 Width;
	UWORD			 Height;
	UWORD			 LabelWidth;
	UWORD			 Lines;
	UWORD			 Chars;
	WORD			 ExtraSpace;
	BOOLEAN			 NoKey;
	BOOLEAN			 HighLabel;
	BOOLEAN			 GroupIndent;
	BOOLEAN			 Centered;
	BOOLEAN			 PageSelector;
	UBYTE			 Key;
	APTR			 Storage;
	UBYTE			 StorageType;
	UBYTE			 Pad;

	union
	{
		GaugeExtra		Gauge;
		TapeDeckExtra		TapeDeck;
		ButtonExtra		Button;
		BarExtra		Bar;
		BoxExtra		Box;
		FrameExtra		Frame;
		PickerExtra		Picker;
		IncrementerExtra	Incrementer;
		GroupExtra		Group;
		ListExtra		List;
		RadioExtra		Radio;
		TextExtra		Text;
		NumberExtra		Number;
		CycleExtra		Cycle;
		PalExtra		Palette;
		SliderExtra		Slider;
		StringExtra		String;
		IntegerExtra		Integer;
		ScrollerExtra		Scroller;
	} Special;
} ObjectNode;

typedef struct LayoutHandle
{
		// WARNING: Beginning of this structure must match the definition in gui.h!

	struct Screen		*Screen;
	struct DrawInfo		*DrawInfo;
	struct Window		*Window;
	APTR			 VisualInfo;
	struct Image		*AmigaGlyph,
				*CheckGlyph;

	struct Screen		*PubScreen;
	struct MsgPort		*MsgPort;
	struct Gadget		**GadgetArray;
	struct Gadget		*Previous;
	struct Gadget		*List;
	ULONG			 IDCMP;

	ObjectNode		*ActiveIncrementer;
	LONG			 IncrementerCountdown;

		// Cached from DrawInfo structure

	UWORD			 TextPen;
	UWORD			 BackgroundPen;
	UWORD			 ShadowPen;
	UWORD			 ShinePen;
	UWORD			 AspectX;
	UWORD			 AspectY;

	struct Hook		*LocaleHook;
	struct TextAttr		*TextAttr;
	struct TextAttr		*InitialTextAttr;
	struct TTextAttr	 CopyTextAttr;
	struct RastPort		 RPort;
	APTR			 Pool;
	UWORD			 GlyphWidth;
	UWORD			 InterWidth;
	UWORD			 InterHeight;
	LONG			 Count;
	LONG			 Index;
	LONG			 MaxID;
	ObjectNode		*TopGroup;
	ObjectNode		*CurrentGroup;
	ObjectNode		*EscKey;
	ObjectNode		*ReturnKey;
	ObjectNode		*CursorKey;
	ObjectNode		*GrowView;
#ifdef DO_FRACTION_KIND
	struct Hook		 FracEditHook;
#endif
#ifdef DO_PASSWORD_KIND
	struct Hook		 PasswordEditHook;
#endif
#ifdef DO_HEXHOOK
	struct Hook		 HexEditHook;
#endif
	struct Hook		 DefaultEditHook;
	struct Hook		*StandardEditHook;
	struct Hook		 BackfillHook;

#ifdef DO_PICKSHORTCUTS
	UBYTE			 Keys[256];
#endif

	struct Hook		*HelpHook;		// Hook to call when help key is pressed
	LONG			 MaxPen;

	BOOLEAN			 AutoRefresh;
	BOOLEAN			 CloseFont;
	BOOLEAN			 Failed;
	BOOLEAN			 Rescaled;
	BOOLEAN			 AutoActivate;
	BOOLEAN			 MoveToWindow;

#ifdef DO_CLONING
	BOOLEAN			 CloningPermitted;	// Genetic engineering?
	BOOLEAN			 ExactClone;		// Make an exact copy, i.e. preserve all colours
	struct CloneExtra	*CloneExtra;		// Screen cloning information
#endif

	struct timerequest	 TimeRequest;
	struct MsgPort		 TimePort;

	struct Window		*Parent;		// Parent of this window
	BOOL			 BlockParent;

} LayoutHandle;

typedef struct LockNode
{
	struct MinNode		 MinNode;
	struct Window		*Window;
	LONG			 Count;
	ULONG			 OldIDCMPFlags;
	struct Requester	 Requester;
	LONG			 MinWidth;
	LONG			 MinHeight;
	LONG			 MaxWidth;
	LONG			 MaxHeight;
} LockNode;

typedef struct ImageInfo
{
	ULONG			 ImageType,
				 GlyphWidth,
				 GlyphHeight;
} ImageInfo;

#define IIA_ImageType	(TAG_USER+739)
#define IIA_GlyphWidth	(TAG_USER+740)
#define IIA_GlyphHeight	(TAG_USER+741)

typedef enum ImageTypes
{
	IMAGECLASS_PICKER,
	IMAGECLASS_LEFTINCREMENTER,
	IMAGECLASS_RIGHTINCREMENTER,
	IMAGECLASS_BACKWARD,
	IMAGECLASS_FORWARD,
	IMAGECLASS_PREVIOUS,
	IMAGECLASS_NEXT,
	IMAGECLASS_RECORD,
	IMAGECLASS_PLAY,
	IMAGECLASS_STOP,
	IMAGECLASS_PAUSE,
	IMAGECLASS_REWIND,
	IMAGECLASS_EJECT
} ImageTypes;

typedef enum StorageTypes
{
	STORAGE_BYTE,STORAGE_UBYTE,
	STORAGE_WORD,STORAGE_UWORD,
	STORAGE_LONG,STORAGE_ULONG,
	STORAGE_STRPTR
} StorageTypes;


/*****************************************************************************/


	// A single colour in 96 bit precision

struct ColourTriplet
{
	ULONG			Red,		// 32 bits each
				Green,
				Blue;
};

	// A colour table ready to submit to LoadRGB32

struct ColourRecord
{
	UWORD			NumColours,	// Number of colours in this chunk
				FirstColour;	// First colour entry to set

	struct ColourTriplet	Triplets[0];	// Table entries

	WORD			Arnold;		// Array terminator, needs to be set to zero
};

	// Screen reproduction

struct CloneExtra
{
	struct Screen		*Screen;
	LONG			*Pens;
	UWORD			*ScreenPens;
	UWORD			 MinWidth,
				 MinHeight,
				 MaxWidth,
				 MaxHeight,
				 TotalPens;
	UBYTE			 Depth;
	struct IBox		 Bounds;
};


/*****************************************************************************/


STATIC VOID __regargs LTK_LayoutGroup(LayoutHandle *Handle, ObjectNode *Group);


/*****************************************************************************/


STATIC ULONG ghostingPat = 0x44441111;

STATIC struct ExecBase		*SysBase;
STATIC struct Library		*IntuitionBase;
STATIC struct GfxBase		*GfxBase;
STATIC struct Library		*UtilityBase;
STATIC struct Library		*GadToolsBase;
STATIC struct Library		*KeymapBase;
STATIC struct Library		*LocaleBase;
STATIC struct Locale		*LTK_Locale;
STATIC struct SignalSemaphore	 LTK_LockSemaphore;
STATIC struct MinList		 LTK_LockList;
STATIC struct IClass		*LTK_ImageClass;

STATIC BOOLEAN			 V39,
				 V40;

#ifdef DO_PICKSHORTCUTS
STATIC UBYTE			 LTK_Keys[2][256];
STATIC struct SignalSemaphore	 LTK_KeySemaphore;
STATIC BOOLEAN			 LTK_KeysInitialized;
#endif


/*****************************************************************************/


STATIC ULONG __regargs
Atol(STRPTR String)
{
	ULONG Value = 0;

	while(*String == ' ' || *String == '\t')
		String++;

	while(*String)
	{
		if(*String >= '0' && *String <= '9')
			Value = (Value * 10) + (*String++) - '0';
		else
			break;
	}

	return(Value);
}


/*****************************************************************************/


STATIC ULONG __asm __saveds
LTK_BackfillRoutine(register __a0 struct Hook *Hook,register __a1 struct {ULONG Layer; struct Rectangle Bounds;} *Bounds,register __a2 struct RastPort *RPort)
{
	struct LayoutHandle	*Handle = (struct LayoutHandle *)Hook -> h_Data;
	struct RastPort		 RastPort = *RPort;

	RPort = &RastPort;

	RPort -> Layer = NULL;

	SetAPen(RPort,Handle -> BackgroundPen);
	SetDrMd(RPort,JAM1);

	RectFill(RPort,Bounds -> Bounds . MinX,Bounds -> Bounds . MinY,Bounds -> Bounds . MaxX,Bounds -> Bounds . MaxY);

	return(TRUE);
}


/*****************************************************************************/


#ifdef DO_HEXHOOK

	// Check whether the buffer only contains a valid hex/binary/octal number...

STATIC BOOLEAN __regargs
LTK_ConvertNum(BOOLEAN negAllowed,STRPTR buffer,LONG *value)
{
	UBYTE	ch;
	ULONG	num;
	UWORD	i;
	LONG	neg;

	num = 0;

	if((buffer[0] == '-') && negAllowed)
	{
		neg = -1;

		buffer++;
	}
	else
		neg = 1;

	if(((buffer[0] == '0') && (buffer[1] == 'x')) || (buffer[0] == '$'))
	{
		if (buffer[0] == '$')
			i = 1;
		else
			i = 2;

		while(buffer[i])
		{
			ch	= ToUpper(buffer[i]);
			num	= num * 16;

			if((ch >= 'A') && (ch <= 'F'))
				num += ch - 'A' + 10;
			else
			{
				if ((ch >= '0') && (ch <= '9'))
					num += ch - '0';
				else
					return(FALSE);
			}

			i++;
		}

		*value = num * neg;

		return(TRUE);
	}
	else
	{
		if(buffer[0] == '%')
		{
			i = 1;

			while(buffer[i])
			{
				ch = buffer[i];

				if((ch < '0') || (ch > '1'))
					return(FALSE);

				num = (num * 2) + ch - '0';

				i++;
			}
		}
		else
		{
			if(buffer[0] == '&')
			{
				i = 1;

				while(buffer[i])
				{
					ch = buffer[i];

					if((ch < '0') || (ch > '7'))
						return(FALSE);

					num = (num * 8) + ch - '0';

					i++;
				}
			}
			else
			{
				i = 0;

				while(buffer[i])
				{
					ch = buffer[i];

					if((ch < '0') || (ch > '9'))
						return(FALSE);

					num = (num * 10) + ch - '0';

					i++;
				}
			}
		}

		*value = num * neg;

		return(TRUE);
	}

	return(FALSE);
}
#endif


/*****************************************************************************/


STATIC VOID __regargs
LTK_AddHistory(struct SGWork *Work)
{
	ObjectNode *Node;

	if(GETOBJECT(Work -> Gadget,Node))
	{
		if(Node -> Special . String . HistoryHook)
		{
			struct MinList *List = Node -> Special . String . HistoryHook -> h_Data;

			while(Node -> Special . String . NumHistoryLines >= Node -> Special . String . MaxHistoryLines)
			{
				CallHookPkt(Node -> Special . String . HistoryHook,Work -> Gadget,NULL);

				Node -> Special . String . NumHistoryLines--;
			}

			Work -> WorkBuffer[Work -> NumChars] = 0;

			if(CallHookPkt(Node -> Special . String . HistoryHook,Work -> Gadget,Work -> WorkBuffer))
				Node -> Special . String . NumHistoryLines++;

			if(List -> mlh_Head -> mln_Succ)
				Node -> Special . String . CurrentNode = (struct Node *)List -> mlh_TailPred;
			else
				Node -> Special . String . CurrentNode = NULL;
		}
	}
}

STATIC VOID __regargs
LTK_HandleHistory(struct SGWork *Work)
{
	ObjectNode *Node;

	if(GETOBJECT(Work -> Gadget,Node))
	{
		if(Node -> Special . String . HistoryHook)
		{
			struct Node	*Choice	= NULL;
			struct MinList	*List	= Node -> Special . String . HistoryHook -> h_Data;

			if(Work -> IEvent -> ie_Qualifier & QUALIFIER_SHIFT)
			{
				if(Work -> IEvent -> ie_Code == CURSORUP)
				{
					if(List -> mlh_Head -> mln_Succ)
						Choice = (struct Node *)List -> mlh_Head;
				}
				else
				{
					if(List -> mlh_Head -> mln_Succ)
						Choice = (struct Node *)List -> mlh_TailPred;
				}
			}
			else
			{
				struct Node *Current = Node -> Special . String . CurrentNode;

				if(Work -> IEvent -> ie_Code == CURSORUP)
				{
					if(Current)
					{
						if(Current -> ln_Pred -> ln_Pred)
							Choice = Current -> ln_Pred;
						else
							Choice = Current;
					}
					else
					{
						if(List -> mlh_Head -> mln_Succ)
							Choice = (struct Node *)List -> mlh_TailPred;
					}
				}
				else
				{
					if(Current)
					{
						if(Current -> ln_Succ -> ln_Succ)
							Choice = Current -> ln_Succ;
					}
				}
			}

			if(Choice != Node -> Special . String . CurrentNode)
			{
				if(Choice)
				{
					strcpy(Work -> WorkBuffer,Choice -> ln_Name);

					Work -> NumChars = Work -> BufferPos = strlen(Work -> WorkBuffer);
				}
				else
				{
					Work -> WorkBuffer[0]	= 0;
					Work -> NumChars	= 0;
					Work -> BufferPos	= 0;
				}

				if(Node -> Type == INTEGER_KIND)
				{
#ifdef DO_HEXHOOK
					LTK_ConvertNum((Node -> Special . Integer . Min < 0),Work -> WorkBuffer,&Work -> LongInt);
#else
					Work -> LongInt = Atol(Work -> WorkBuffer);
#endif
				}

				Node -> Special . String . CurrentNode = Choice;

				Work -> Actions = (Work -> Actions & ~SGA_BEEP) | SGA_USE | SGA_REDISPLAY;
			}
		}
	}
}


/*****************************************************************************/


#ifdef DO_FRACTION_KIND
STATIC ULONG __asm __saveds
LTK_FracEditRoutine(register __a0 struct Hook *Hook,register __a2 struct SGWork *Work,register __a1 ULONG *Msg)
{
	if(*Msg == SGH_KEY)
	{
		if(Work -> IEvent -> ie_Code == CURSORUP || Work -> IEvent -> ie_Code == CURSORDOWN)
			LTK_HandleHistory(Work);

		if(Work -> EditOp == EO_ENTER)
		{
			LayoutHandle	*Handle = Hook -> h_Data;
			ObjectNode	*Node;
			BOOLEAN		 Activate = TRUE;

			if(GETOBJECT(Work -> Gadget,Node))
			{
				if(Node -> Type == FRACTION_KIND && Node -> Special . String . LastGadget)
					Activate = FALSE;
			}

			if(!(Work -> IEvent -> ie_Qualifier & QUALIFIER_SHIFT))
			{
				if(Activate && Handle -> AutoActivate)
					Work -> Actions |= SGA_NEXTACTIVE;

				if(!(Work -> Actions & SGA_NEXTACTIVE))
					Work -> Code = '\r';
			}
		}

		if(Work -> EditOp == EO_INSERTCHAR || Work -> EditOp == EO_REPLACECHAR)
		{
			UBYTE DecimalPoint = (LTK_Locale ? LTK_Locale -> loc_DecimalPoint[0] : '.');

			if(Work -> Code == DecimalPoint || (Work -> Code >= '0' && Work -> Code <= '9'))
			{
				if(Work -> Code == DecimalPoint)
				{
					BOOLEAN	UseIt = TRUE;
					LONG	i;

					for (i = 0 ; UseIt && i < Work -> NumChars ; i++)
					{
						if(Work -> PrevBuffer[i] == DecimalPoint)
							UseIt = FALSE;
					}

					if(!UseIt)
					{
						Work -> Actions &= ~SGA_USE;
						Work -> Actions |=  SGA_BEEP;
					}
				}
			}
			else
			{
				Work -> Actions &= ~SGA_USE;
				Work -> Actions |=  SGA_BEEP;
			}
		}

		if(Work -> Actions & SGA_END)
			LTK_AddHistory(Work);
	}
	else
	{
		if(*Msg != SGH_CLICK)
			return(FALSE);
	}

	return(TRUE);
}
#endif


/*****************************************************************************/


STATIC ULONG __asm __saveds
LTK_DefaultEditRoutine(register __a0 struct Hook *Hook,register __a2 struct SGWork *Work,register __a1 ULONG *Msg)
{
	if(*Msg == SGH_KEY)
	{
		if(Work -> IEvent -> ie_Code == CURSORUP || Work -> IEvent -> ie_Code == CURSORDOWN)
			LTK_HandleHistory(Work);

		if(Work -> EditOp == EO_ENTER)
		{
			LayoutHandle	*Handle = Hook -> h_Data;
			ObjectNode	*Node;
			BOOLEAN		 Activate = TRUE;

			if(GETOBJECT(Work -> Gadget,Node))
			{
				if(Node -> Type == STRING_KIND)
				{
					if(Node -> Special . String . LastGadget)
						Activate = FALSE;
				}
#ifndef DO_HEXHOOK
				else
				{
					if(Node -> Type == INTEGER_KIND)
					{
						if(Node -> Special . Integer . LastGadget)
							Activate = FALSE;
					}
				}
#endif
			}

			if(!(Work -> IEvent -> ie_Qualifier & QUALIFIER_SHIFT))
			{
				if(Activate && Handle -> AutoActivate)
					Work -> Actions |= SGA_NEXTACTIVE;

				if(!(Work -> Actions & SGA_NEXTACTIVE))
					Work -> Code = '\r';
			}
		}

		if(Work -> IEvent -> ie_Code == CURSORRIGHT && (Work -> IEvent -> ie_Qualifier & QUALIFIER_CONTROL))
		{
			if(Work -> BufferPos != Work -> NumChars)
			{
				WORD i,Position = -1;

				for(i = Work -> BufferPos ; i < Work -> NumChars ; i++)
				{
					if(Work -> WorkBuffer[i] == ' ')
					{
						for( ; i < Work -> NumChars ; i++)
						{
							if(Work -> WorkBuffer[i] != ' ')
							{
								Position = i;

								break;
							}
						}

						break;
					}
				}

				if(Position != -1)
					Work -> BufferPos = Position;
				else
					Work -> BufferPos = Work -> NumChars;

				Work -> EditOp = EO_MOVECURSOR;
			}
		}

		if(Work -> IEvent -> ie_Code == CURSORLEFT && (Work -> IEvent -> ie_Qualifier & QUALIFIER_CONTROL))
		{
			if(Work -> BufferPos)
			{
				WORD i,Position = -1;

				for(i = Work -> BufferPos ; i >= 0 ; i--)
				{
					if(Work -> WorkBuffer[i] != ' ')
					{
						Position = i;

						break;
					}
				}

				if(Position == -1)
					Position = 0;

				if(Position)
				{
					i = Position;

					Position = -1;

					for( ; i >= 0 ; i--)
					{
						if(Work -> WorkBuffer[i] == ' ')
						{
							Position = i + 1;

							break;
						}
					}
				}

				if(Position != -1)
					Work -> BufferPos = Position;
				else
					Work -> BufferPos = 0;

				Work -> EditOp = EO_MOVECURSOR;
			}
		}

		if(Work -> Actions & SGA_END)
			LTK_AddHistory(Work);
	}
	else
	{
		if(*Msg != SGH_CLICK)
			return(FALSE);
	}

	return(TRUE);
}


/*****************************************************************************/


#ifdef DO_HEXHOOK
STATIC ULONG __asm __saveds
LTK_HexEditRoutine(register __a0 struct Hook *hook,register __a2 struct SGWork *sgw,register __a1 ULONG *msg)
{
	ULONG result = FALSE;

	if(*msg == SGH_KEY)
	{
		BOOLEAN		 activate	= TRUE;
		LayoutHandle	*handle		= hook -> h_Data;
		ObjectNode	*node;

		if(sgw -> IEvent -> ie_Code == CURSORUP || sgw -> IEvent -> ie_Code == CURSORDOWN)
			LTK_HandleHistory(sgw);

		if(GETOBJECT(sgw -> Gadget,node))
		{
			if(node -> Type == INTEGER_KIND)
			{
				if(node -> Special . Integer . LastGadget)
					activate = FALSE;

				result = TRUE;

				if(!LTK_ConvertNum((node -> Special . Integer . Min < 0),sgw -> WorkBuffer,(LONG *)&sgw -> StringInfo -> LongInt))
				{
					sgw -> EditOp	= EO_BADFORMAT;
					sgw -> Actions	= SGA_BEEP;
				}
			}
		}

		if(sgw -> EditOp == EO_ENTER && !(sgw -> IEvent -> ie_Qualifier & QUALIFIER_SHIFT))
		{
			if(activate && handle -> AutoActivate)
				sgw -> Actions |= SGA_NEXTACTIVE;

			if(!(sgw -> Actions & SGA_NEXTACTIVE))
				sgw -> Code = '\r';
		}

		if(sgw -> Actions & SGA_END)
			LTK_AddHistory(sgw);
	}

	return(result);
}
#endif


/*****************************************************************************/


#ifdef DO_PASSWORD_KIND
STATIC ULONG __asm __saveds
LTK_PasswordEditRoutine(register __a0 struct Hook *Hook,register __a2 struct SGWork *Work,register __a1 ULONG *Msg)
{
	ObjectNode *Node;

	if(GETOBJECT(Work -> Gadget,Node))
	{
		STRPTR	Buffer		= Node -> Special . String . RealString;
		STRPTR	Original	= Node -> Special . String . Original;
		WORD	Len;

/*		if(Work -> IEvent -> ie_Code == CURSORUP || Work -> IEvent -> ie_Code == CURSORDOWN)*/
/*			LTK_HandleHistory(Work);*/

		switch(*Msg)
		{
			case SGH_KEY:

				switch(Work -> EditOp)
				{
					case EO_INSERTCHAR:

						Buffer[Work -> BufferPos - 1]	= Work -> Code;
						Buffer[Work -> NumChars]	= 0;

						Work -> Code = (WORD)'·';

						Work -> WorkBuffer[Work -> BufferPos - 1] = (UBYTE)'·';

						break;

					case EO_RESET:

						Len = strlen(Original);

						strcpy(Buffer,Original);

						memset(Work -> WorkBuffer,'·',Len);

						Work -> WorkBuffer[Len] = 0;

						Work -> NumChars	= Len;
						Work -> BufferPos	= Len;
						Work -> EditOp		= EO_BIGCHANGE;

						break;

					case EO_CLEAR:

						Work -> WorkBuffer[0]	= 0;
						Work -> NumChars	= 0;
						Work -> BufferPos	= 0;
						Work -> EditOp		= EO_BIGCHANGE;

						Buffer[0] = 0;

						break;

					case EO_DELBACKWARD:

						Buffer[Work -> NumChars] = 0;
						break;

					case EO_NOOP:

						break;

					case EO_ENTER:

						strcpy(Original,Buffer);

						if(!(Work -> IEvent -> ie_Qualifier & QUALIFIER_SHIFT))
						{
							if(!Node -> Special . String . LastGadget && Node -> Special . String . LayoutHandle -> AutoActivate)
								Work -> Actions |= SGA_NEXTACTIVE;

							if(!(Work -> Actions & SGA_NEXTACTIVE))
								Work -> Code = '\r';
						}

						break;

					case EO_MOVECURSOR:
					case EO_DELFORWARD:

						Work -> Actions &= ~SGA_USE;
						break;

					default:

						Work -> Actions &= ~SGA_USE;
						Work -> Actions |=  SGA_BEEP;
						break;
				}

				break;

			case SGH_CLICK:

				Work -> BufferPos	= Work -> NumChars;
				Work -> EditOp		= EO_BIGCHANGE;

				break;
		}

/*		if(Work -> Actions & SGA_END)*/
/*			LTK_AddHistory(Work);*/

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


/*****************************************************************************/


STATIC VOID __regargs
LTK_SetPens(struct RastPort *rp, UWORD apen, UWORD bpen,UBYTE mode)
{
	if(V39)
		SetABPenDrMd(rp,apen,bpen,mode);
	else
	{
		if(apen != rp -> FgPen)
			SetAPen(rp,apen);

		if(bpen != rp -> BgPen)
			SetBPen(rp,bpen);

		if(mode != rp -> DrawMode)
			SetDrMd(rp,mode);
	}
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_SetAPen(struct RastPort *rp,UWORD apen)
{
	if(V39)
	{
		if(GetAPen(rp) != apen)
			SetAPen(rp,apen);
	}
	else
	{
		if(apen != rp -> FgPen)
			SetAPen(rp,apen);
	}
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_SetFont(LayoutHandle *handle,struct TextFont *font)
{
	SetFont(&handle -> RPort,font);

	if(handle -> Window)
		SetFont(handle -> Window -> RPort,font);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_DrawBevelBox(LayoutHandle *handle,ObjectNode *node)
{
	DrawBevelBox(&handle -> RPort,node -> Left,node -> Top,node -> Width,node -> Height,
		GT_VisualInfo,	handle -> VisualInfo,
		GTBB_Recessed,	TRUE,
	TAG_DONE);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_PlaceText(LayoutHandle *handle,STRPTR text,ULONG textLen,LONG x,LONG y)
{
	Move(&handle -> RPort,x,y);
	Text(&handle -> RPort,text,textLen);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_GetDisplayClip(struct Screen *screen,WORD *left,WORD *top,WORD *width,WORD *height)
{
	struct TagItem tags[2] = { VTAG_VIEWPORTEXTRA_GET, NULL, TAG_DONE };

	if(!VideoControl(screen -> ViewPort . ColorMap,tags))
	{
		struct ViewPortExtra	*vpe = (struct ViewPortExtra *)tags[0] . ti_Data;
		struct Rectangle	 clip;

		QueryOverscan(GetVPModeID(&screen -> ViewPort),&clip,OSCAN_TEXT);

		*width	= vpe -> DisplayClip . MaxX - vpe -> DisplayClip . MinX + 1;
		*height	= vpe -> DisplayClip . MaxY - vpe -> DisplayClip . MinY + 1;

		if(*width < clip . MaxX - clip . MinX + 1)
			*width = clip . MaxX - clip . MinX + 1;

		if(*height < clip . MaxY - clip . MinY + 1)
			*height = clip . MaxY - clip . MinY + 1;

		if(*width > screen -> Width)
			*width = screen -> Width;

		if(*height > screen -> Height)
			*height = screen -> Height;
	}
	else
	{
		*width	= screen -> Width;
		*height = screen -> Height;
	}

	*left = screen -> LeftEdge;
	*top  = screen -> TopEdge;

	if(*left > 0)
		*left = 0;
	else
		*left = -(*left);

	if(*top > 0)
		*top = 0;
	else
		*top = -(*top);

	if((*left > screen -> Width) || (*left < 0))
		*left = 0;

	if ((*top > screen -> Height) || (*top < 0))
		*top = 0;
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_MoveToWindow(LayoutHandle *handle)
{
	if(V39 && handle -> MoveToWindow)
	{
		WORD left,top,width,height;

		LTK_GetDisplayClip(handle->Screen,&left,&top,&width,&height);

		if(handle -> Window -> Width < width)
			left = (width - handle -> Window -> Width) / 2;
		else
			left = 0;

		if(handle -> Window -> Height < height)
			top = (height - handle -> Window -> Height) / 2;
		else
			top = 0;

		if((left = handle -> Window -> LeftEdge - left) < 0)
			left = 0;

		if((top = handle -> Window -> TopEdge - top) < 0)
			top = 0;

		ScreenPosition(handle -> Screen,SPOS_MAKEVISIBLE,left,top,left + width - 1,top + height - 1);
	}
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_GetStorage(ObjectNode *Node)
{
	if(Node -> Storage)
	{
		LONG	Number = 0;
		STRPTR	String = NULL;

		switch(Node -> StorageType)
		{
			case STORAGE_BYTE:

				Number = *(BYTE *)Node -> Storage;
				break;

			case STORAGE_UBYTE:

				Number = *(UBYTE *)Node -> Storage;
				break;

			case STORAGE_WORD:

				Number = *(WORD *)Node -> Storage;
				break;

			case STORAGE_UWORD:

				Number = *(UWORD *)Node -> Storage;
				break;

			case STORAGE_LONG:

				Number = *(LONG *)Node -> Storage;
				break;

			case STORAGE_ULONG:

				Number = *(ULONG *)Node -> Storage;
				break;

			case STORAGE_STRPTR:

				String = Node -> Storage;
				break;
		}

		switch(Node -> Type)
		{
			case CHECKBOX_KIND:
			case LISTVIEW_KIND:
			case MX_KIND:
			case CYCLE_KIND:
			case PALETTE_KIND:
			case SLIDER_KIND:
			case SCROLLER_KIND:
#ifdef DO_GAUGE_KIND
			case GAUGE_KIND:
#endif

#ifdef DO_TAPEDECK_KIND
			case TAPEDECK_KIND:
#endif	/* DO_TAPEDECK_KIND */

#ifdef DO_FRACTION_KIND
			case FRACTION_KIND:
#endif
				Node -> Current = Number;
				break;

			case STRING_KIND:
#ifdef DO_PASSWORD_KIND
			case PASSWORD_KIND:
#endif
				Node -> Special . String . String = String;
				break;

			case INTEGER_KIND:

				Node -> Special . Integer . Number = Number;
				break;
		}
	}
}

STATIC VOID __regargs
LTK_PutStorage(ObjectNode *Node)
{
	if(Node -> Storage)
	{
		LONG	Number = 0;
		STRPTR	String = NULL;

		switch(Node -> Type)
		{
			case CHECKBOX_KIND:
			case LISTVIEW_KIND:
			case MX_KIND:
			case CYCLE_KIND:
			case PALETTE_KIND:
			case SLIDER_KIND:
			case SCROLLER_KIND:
#ifdef DO_GAUGE_KIND
			case GAUGE_KIND:
#endif

#ifdef DO_TAPEDECK_KIND
			case TAPEDECK_KIND:
#endif	/* DO_TAPEDECK_KIND */
				Number = Node -> Current;
				break;

#ifdef DO_FRACTION_KIND
			case FRACTION_KIND:

				Number = (LONG)LT_String2Fixed(((struct StringInfo *)Node -> Host -> SpecialInfo) -> Buffer);
				break;
#endif

#ifdef DO_PASSWORD_KIND
			case PASSWORD_KIND:

				String = Node -> Special . String . Original;
				break;
#endif

			case STRING_KIND:

				String = ((struct StringInfo *)Node -> Host -> SpecialInfo) -> Buffer;
				break;

			case INTEGER_KIND:

				Number = ((struct StringInfo *)Node -> Host -> SpecialInfo) -> LongInt;
				break;
		}

		switch(Node -> StorageType)
		{
			case STORAGE_BYTE:

				if(!String)
					*(BYTE *)Node -> Storage = (BYTE)Number;

				break;

			case STORAGE_UBYTE:

				if(!String)
					*(UBYTE *)Node -> Storage = (UBYTE)Number;

				break;

			case STORAGE_WORD:

				if(!String)
					*(WORD *)Node -> Storage = (WORD)Number;

				break;

			case STORAGE_UWORD:

				if(!String)
					*(UWORD *)Node -> Storage = (UWORD)Number;

				break;

			case STORAGE_LONG:

				if(!String)
					*(LONG *)Node -> Storage = (LONG)Number;

				break;

			case STORAGE_ULONG:

				if(!String)
					*(ULONG *)Node -> Storage = (ULONG)Number;

				break;

			case STORAGE_STRPTR:

				if(String)
					strcpy(Node -> Storage,String);

				break;
		}
	}
}


/*****************************************************************************/


STATIC VOID __stdargs
sprintf(STRPTR buffer, STRPTR formatString,...)
{
	va_list varArgs;

	va_start(varArgs,formatString);
	RawDoFmt(formatString,varArgs,(VOID (*)())(VOID (*))"\x16\xC0\x4E\x75",buffer);
	va_end(varArgs);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_BlinkButton(LayoutHandle *handle,struct Gadget *gadget)
{
	if((gadget -> Flags & (GFLG_GADGIMAGE | GFLG_GADGHIMAGE)) && gadget -> SelectRender)
		DrawImageState(&handle -> RPort,gadget -> SelectRender,gadget -> LeftEdge,gadget -> TopEdge,IDS_SELECTED,handle -> DrawInfo);

	if(handle -> TimeRequest . tr_node . io_Device)
	{
		Forbid();

		handle -> TimePort . mp_SigTask = SysBase -> ThisTask;

		handle -> TimeRequest . tr_node . io_Command	= TR_ADDREQUEST;
		handle -> TimeRequest . tr_time . tv_secs	= 0;
		handle -> TimeRequest . tr_time . tv_micro	= 80000;

		SetSignal(0,SIGF_SINGLE);
		DoIO(&handle -> TimeRequest);
		Permit();
	}
	else
		TimeDelay(UNIT_VBLANK,0,80000);

	if ((gadget -> Flags & GFLG_GADGIMAGE) && gadget -> GadgetRender)
		DrawImageState(&handle -> RPort,gadget -> GadgetRender,gadget -> LeftEdge,gadget -> TopEdge,IDS_NORMAL,handle -> DrawInfo);
}


/*****************************************************************************/


#ifdef DO_GAUGE_KIND
STATIC VOID __regargs
LTK_DrawGauge(LayoutHandle *handle,ObjectNode *node,LONG percent,BOOLEAN fullRefresh)
{
	struct RastPort *rp = &handle -> RPort;

	if(fullRefresh)
	{
		LONG top = node -> Top + node -> Height - (handle -> RPort . TxHeight + 2);

		LTK_SetAPen(rp,handle -> BackgroundPen);
		RectFill(rp,node -> Left,node -> Top,node -> Left + node -> Width - 1,node -> Top + node -> Height - 1);

		DrawBevelBox(rp,node -> Left,node -> Top,node -> Width,node -> Height - (handle -> InterHeight + handle -> RPort . TxHeight + 2),
			GT_VisualInfo,	handle -> VisualInfo,
			GTBB_Recessed,	TRUE,
		TAG_DONE);

		LTK_SetPens(rp,handle -> TextPen,0,JAM1);

		LTK_PlaceText(handle,"0%",2,node -> Left,top + handle -> RPort . TxBaseline);
		LTK_PlaceText(handle,"100%",4,node -> Left + node -> Width - TextLength(rp,"100%",4),top + handle -> RPort . TxBaseline);

		if(node -> Width >= TextLength(rp,"0% 50% 100%",11))
		{
			LONG i,left,total = node -> Width - 2;

			LTK_PlaceText(handle,"50%",3,node -> Left + (node -> Width - TextLength(rp,"50%",3)) / 2,top + handle -> RPort . TxBaseline);

			top -= handle -> InterHeight;

			for (i = 0 ; i < 5 ; i++)
			{
				left = (total * i) / 4;

				RectFill(rp,node -> Left + left,top,node -> Left + left + 1,top + 1);
			}
		}
		else
		{
			LONG i,left,total = node -> Width - 2;

			top -= handle -> InterHeight;

			for (i = 0; i < 3; i++)
			{
				left = (total * i) / 2;

				RectFill(rp,node -> Left + left,top,node -> Left + left + 1,top + 1);
			}
		}
	}

	if(node -> Special . Gauge . InfoLength && node -> Special . Gauge . InfoText[0])
	{
		LONG			height	= node -> Height - (1 + handle -> InterHeight + handle -> RPort . TxHeight + 2 + 1);
		LONG			width	= node -> Width - (2 + 2);
		LONG			left	= 0;
		LONG			right	= (width * percent) / 100;
		struct TextExtent	Extent;
		LONG			len;

		if(right)
		{
			LTK_SetAPen(rp,handle -> DrawInfo -> dri_Pens[FILLPEN]);

			RectFill(rp,node -> Left + 2 + left,node -> Top + 1,node -> Left + 2 + right - 1,node -> Top + 1 + height - 1);
		}

		if(right < width)
		{
			left	= right;
			right	= width;

			LTK_SetAPen(rp,handle -> BackgroundPen);
			RectFill(rp,node -> Left + 2 + left,node -> Top + 1,node -> Left + 2 + right - 1,node -> Top + 1 + height - 1);
		}

		if(handle -> DrawInfo -> dri_Pens[FILLTEXTPEN] == handle -> DrawInfo -> dri_Pens[BACKGROUNDPEN])
		{
			if(handle -> DrawInfo -> dri_Pens[TEXTPEN] == handle -> DrawInfo -> dri_Pens[FILLPEN])
				LTK_SetPens(rp,handle -> DrawInfo -> dri_Pens[TEXTPEN],handle -> BackgroundPen,JAM1 | COMPLEMENT);
			else
				LTK_SetPens(rp,handle -> DrawInfo -> dri_Pens[TEXTPEN],handle -> BackgroundPen,JAM1);
		}
		else
		{
			if(handle -> DrawInfo -> dri_Pens[FILLTEXTPEN] == handle -> DrawInfo -> dri_Pens[FILLPEN])
				LTK_SetPens(rp,handle -> DrawInfo -> dri_Pens[FILLTEXTPEN],handle -> BackgroundPen,JAM1 | COMPLEMENT);
			else
				LTK_SetPens(rp,handle -> DrawInfo -> dri_Pens[FILLTEXTPEN],handle -> BackgroundPen,JAM1);
		}

		len = TextFit(rp,node -> Special . Gauge . InfoText,strlen(node -> Special . Gauge . InfoText),&Extent,NULL,1,width,32767);

		LTK_PlaceText(handle,node -> Special . Gauge . InfoText,len,node -> Left + 2 + (width - Extent . te_Width) / 2,node -> Top + 1 + (height - rp -> Font -> tf_YSize) / 2 + rp -> Font -> tf_Baseline);

		LTK_SetPens(rp,handle -> TextPen,handle->BackgroundPen,JAM1);
	}
	else
	{
		if(node -> Current < percent)
		{
			LONG height	= node -> Height - (1 + handle -> InterHeight + handle -> RPort . TxHeight + 2 + 1);
			LONG width	= node -> Width - (2 + 2);
			LONG left	= (width * node -> Current) / 100;
			LONG right	= (width * percent) / 100;

			if(right)
			{
				LTK_SetAPen(rp,handle -> DrawInfo -> dri_Pens[FILLPEN]);
				RectFill(rp,node -> Left + 2 + left,node -> Top + 1,node -> Left + 2 + right - 1,node -> Top + 1 + height - 1);
			}
		}
		else
		{
			if(node -> Current > percent)
			{
				LONG height	= node -> Height - (1 + handle -> InterHeight + handle -> RPort . TxHeight + 2 + 1);
				LONG width	= node -> Width - (2 + 2);
				LONG left	= (width * percent) / 100;
				LONG right	= (width * node -> Current) / 100;

				if(right)
				{
					LTK_SetAPen(rp,handle -> BackgroundPen);
					RectFill(rp,node -> Left + 2 + left,node -> Top + 1,node -> Left + 2 + right - 1,node -> Top + 1 + height - 1);
				}
			}
		}
	}

	node -> Current = percent;

	LTK_PutStorage(node);
}
#endif


/*****************************************************************************/


STATIC VOID __regargs
LTK_PrintLine(LayoutHandle *handle,UBYTE alignType,LONG left,LONG top,LONG space,STRPTR line,LONG len)
{
	struct RastPort		*rp;
	LONG			 width;
	struct TextExtent	 extent;

	rp	= &handle -> RPort;
	len	= TextFit(rp,line,len,&extent,NULL,1,space,32767);
	width	= extent . te_Width;

	if(len)
	{
		LTK_SetPens(rp,handle -> TextPen,handle -> BackgroundPen,JAM2);

		if(alignType == ALIGNTEXT_CENTERED)
		{
			LTK_PlaceText(handle,line,len,left + (space - width) / 2,top + handle -> RPort . TxBaseline);

			if(width < space)
			{
				LONG fill;

				LTK_SetAPen(rp,handle -> BackgroundPen);

				if((fill = (space - width) / 2) > 0)
					RectFill(rp,left,top,left + fill - 1,top + handle -> RPort . TxHeight - 1);

				if((fill = space - ((space - width) / 2)) > 0)
					RectFill(rp,left + fill,top,left + space - 1,top + handle -> RPort . TxHeight - 1);
			}
		}
		else
		{
			if(alignType == ALIGNTEXT_LEFT)
			{
				LTK_PlaceText(handle,line,len,left,top + handle -> RPort.TxBaseline);

				if(left + width <= left + space - 1)
				{
					LTK_SetAPen(rp,handle -> BackgroundPen);

					RectFill(rp,left + width,top,left + space - 1,top + handle -> RPort . TxHeight - 1);
				}
			}
			else
			{
				LTK_PlaceText(handle,line,len,left + space - width,top + handle -> RPort . TxBaseline);

				if(width < space)
				{
					LTK_SetAPen(rp,handle -> BackgroundPen);
					RectFill(rp,left,top,left + space - width - 1,top + handle -> RPort . TxHeight - 1);
				}
			}
		}
	}
	else
	{
		LTK_SetAPen(rp,handle -> BackgroundPen);
		RectFill(rp,left,top,left + space - 1,top + handle -> RPort . TxHeight - 1);
	}
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_PrintLinePadded(LayoutHandle *Handle,LONG Left,LONG Top,LONG Space,STRPTR Line,LONG Len)
{
	struct RastPort	*RPort	= &Handle -> RPort;
	LONG		 Size	= 0;
	LONG		 Start	= 0;
	LONG		 Count	= 0;
	LONG		 i	= 0;
	LONG		 Width	= 0;

	while(i < Len && Line[i] == ' ')
	{
		Size++;
		i++;
	}

	while(i < Len)
	{
		while(i < Len && Line[i] != ' ')
		{
			Size++;
			i++;
		}

		Width += TextLength(RPort,&Line[Start],Size);

		Count++;

		while(i < Len && Line[i] == ' ')
			i++;

		Start	= i;
		Size	= 0;
	}

	if(Width)
	{
		LONG j;

		Space -= Width;

		j = Start = Size = 0;

		while(j < Len && Line[j] == ' ')
		{
			Size++;
			j++;
		}

		LTK_SetPens(RPort,Handle -> TextPen,Handle -> BackgroundPen,JAM2);
		Move(RPort,Left,Top + RPort -> TxBaseline);

		Count--;

		for(i = 0 ; i <= Count ; i++)
		{
			if(i)
			{
				LONG Delta;

				if(Delta = (Space * i) / Count - (Space * (i - 1)) / Count)
				{
					LTK_SetAPen(RPort,Handle -> BackgroundPen);
					RectFill(RPort,RPort -> cp_x,Top,RPort -> cp_x + Delta - 1,Top + RPort -> TxHeight - 1);

					LTK_SetAPen(RPort,Handle -> TextPen);
					Move(RPort,RPort -> cp_x + Delta,RPort -> cp_y);
				}
			}

			while(j < Len && Line[j] != ' ')
			{
				Size++;
				j++;
			}

			Text(RPort,&Line[Start],Size);

			while(j < Len && Line[j] == ' ')
				j++;

			Start = j;
			Size = 0;
		}
	}
	else
	{
		LTK_SetAPen(RPort,Handle -> BackgroundPen);
		RectFill(RPort,Left,Top,Left + Space - 1,Top + RPort -> TxHeight - 1);
	}
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_PrintBoxLine(LayoutHandle *handle,ObjectNode *node,LONG index)
{
	if(node -> Special . Box . Lines && node -> Special . Box . Lines[index])
	{
		LONG len = strlen(node -> Special . Box . Lines[index]);

		if(node -> Special . Box . AlignText == ALIGNTEXT_PAD)
			LTK_PrintLinePadded(handle,node -> Left + 4,node -> Top + 2 + index * handle -> RPort . TxHeight,node -> Width - 8,node -> Special . Box . Lines[index],len);
		else
			LTK_PrintLine(handle,node -> Special . Box . AlignText,node -> Left + 4,node -> Top + 2 + index * handle -> RPort . TxHeight,node -> Width - 8,node -> Special . Box . Lines[index],len);
	}
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_ResetGroups(ObjectNode *group)
{
	ObjectNode *node;

	group -> Special . Group . ExtraLeft	= 0;
	group -> Special . Group . ExtraTop	= 0;
	group -> Special . Group . MaxOffset	= 0;
	group -> Special . Group . MaxSize	= 0;
	group -> GroupIndent			= FALSE;
	group -> Left				= 0;
	group -> Top				= 0;
	group -> Width				= 0;
	group -> Height				= 0;

	SCANGROUP(group,node)
	{
		if(node -> Type == GROUP_KIND)
			LTK_ResetGroups(node);
	}
}


/*****************************************************************************/


STATIC BOOLEAN __regargs
LTK_Rescale(LayoutHandle *handle,BOOLEAN trimWidth,BOOLEAN trimHeight)
{
	STATIC struct TextAttr topazAttr =
	{
		"topaz.font",
		8,
		FS_NORMAL,
		FPF_ROMFONT | FPF_DESIGNED
	};

	LONG	oldWidth;
	LONG	oldHeight;
	UBYTE	glyph;
	LONG	glyphWidth;
	LONG	count;
	LONG	i;
	LONG	total;
	BOOLEAN	found;

	oldWidth	= handle -> GlyphWidth;
	oldHeight	= handle -> RPort . TxHeight;

	if(handle -> CloseFont)
	{
		CloseFont(handle -> RPort . Font);

		handle -> CloseFont = FALSE;
	}

	/* GfxBase -> DefaultFont is guaranteed not to ever be removed from
	 * memory,so we use that fact...
	 */

	LTK_SetFont(handle,GfxBase -> DefaultFont);

	handle -> TextAttr			= &handle -> CopyTextAttr;
	handle -> CopyTextAttr . tta_Name	= handle -> RPort . Font -> tf_Message . mn_Node . ln_Name;
	handle -> CopyTextAttr . tta_YSize	= handle -> RPort . TxHeight;
	handle -> CopyTextAttr . tta_Style	= handle -> RPort . Font -> tf_Style;
	handle -> CopyTextAttr . tta_Flags	= handle -> RPort . TxFlags;

	if(handle -> RPort . Font -> tf_Style & FSF_TAGGED)
		handle -> CopyTextAttr . tta_Tags = ((struct TextFontExtension *)handle -> RPort . Font -> tf_Extension) -> tfe_Tags;

	FOREVER
	{
		count = 0;
		total = 0;

		for(i = 32 ; i < 127 ; i++)
		{
			glyph = i;
			total += TextLength(&handle -> RPort,&glyph,1);
			count++;
		}

		for(i = 160 ; i < 256 ; i++)
		{
			glyph = i;
			total += TextLength(&handle -> RPort,&glyph,1);
			count++;
		}

		glyphWidth = total / count;

		found = TRUE;

		if(trimWidth && (glyphWidth >= oldWidth))
			found = FALSE;

		if(trimHeight && (oldHeight >= handle -> RPort . TxHeight))
			found = FALSE;

		if(found)
		{
			handle -> GlyphWidth	= glyphWidth;
			handle -> Rescaled	= TRUE;

			if(glyphWidth <= 8)
				handle -> InterWidth = 4;
			else
				handle -> InterWidth = glyphWidth / 2;

			if(handle -> RPort . TxHeight <= 8)
				handle -> InterHeight = 2;
			else
				handle -> InterHeight = handle -> RPort . TxHeight / 4;

			LTK_ResetGroups(handle -> TopGroup);

			return(TRUE);
		}

		if(handle -> TextAttr == &topazAttr)
			return(FALSE);

		LTK_SetFont(handle,OpenFont(&topazAttr));

		handle -> TextAttr	= &topazAttr;
		handle -> CloseFont	= TRUE;
	}
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_DrawIncrementer(struct RastPort *rp,BOOLEAN leftDirection,LONG left,LONG top,LONG width,LONG height)
{
	LONG middle;
	LONG x0,x1;
	LONG stop;

	middle = height / 2;

	if(leftDirection)
	{
		x0 = left + 2;
		x1 = left + width - 3;
	}
	else
	{
		x0 = left + width - 3;
		x1 = left + 2;
	}

	if(height > width)
		stop = (width - 4) / 2;
	else
		stop = (height - 4) / 2;

	Move(rp,x0,top + middle);
	Draw(rp,x1,top + middle - stop);
	Move(rp,x0,top + middle);
	Draw(rp,x1,top + middle + stop);
}


/*****************************************************************************/


#ifdef DO_TAPEDECK_KIND
STATIC VOID __regargs
LTK_DrawBackFore(struct RastPort *RPort,BOOLEAN Back,LONG Left,LONG Top,LONG Width,LONG Height)
{
	LONG i,Len,Start,ArrowWidth,ArrowHeight,Shift;

	ArrowWidth	= Width / 2;
	ArrowHeight	= Height;
	Left		= Left + (Width - ArrowWidth) / 2;
	Shift		= ArrowWidth / 2;

	if(Back)
	{
		for(i = 0 ; i < ArrowWidth ; i++)
		{
			Len	= (ArrowHeight * (i + 1)) / ArrowWidth;
			Start	= Top + (ArrowHeight - Len) / 2;

			Move(RPort,Left + i - Shift,Start);
			Draw(RPort,Left + i - Shift,Start + Len - 1);

			Move(RPort,Left + i + Shift,Start);
			Draw(RPort,Left + i + Shift,Start + Len - 1);
		}
	}
	else
	{
		for(i = 0 ; i < ArrowWidth ; i++)
		{
			Len	= (ArrowHeight * (i + 1)) / ArrowWidth;
			Start	= Top + (ArrowHeight - Len) / 2;

			Move(RPort,Left + ArrowWidth - 1 - i - Shift,Start);
			Draw(RPort,Left + ArrowWidth - 1 - i - Shift,Start + Len - 1);

			Move(RPort,Left + ArrowWidth - 1 - i + Shift,Start);
			Draw(RPort,Left + ArrowWidth - 1 - i + Shift,Start + Len - 1);
		}
	}
}
#endif	/* DO_TAPEDECK_KIND */


/*****************************************************************************/


#ifdef DO_TAPEDECK_KIND
STATIC VOID __regargs
LTK_DrawPrevNext(struct RastPort *RPort,BOOLEAN Prev,LONG Left,LONG Top,LONG Width,LONG Height)
{
	LONG i,Len,Start,ArrowWidth,ArrowHeight,LineWidth;

	LineWidth	= (Width + 15) / 16;
	ArrowWidth	= Width;
	ArrowHeight	= Height;
	Left		= Left - (LineWidth / 2);

	if(Prev)
	{
		for(i = 0 ; i < ArrowWidth ; i++)
		{
			Len	= (ArrowHeight * (i + 1) + ArrowWidth - 1) / ArrowWidth;
			Start	= Top + (ArrowHeight - Len) / 2;

			Move(RPort,Left + i,Start);
			Draw(RPort,Left + i,Start + Len - 1);
		}

		for(i = 0 ; i < LineWidth ; i++)
		{
			Move(RPort,Left + i,Top);
			Draw(RPort,Left + i,Top + Height - 1);
		}
	}
	else
	{
		for(i = 0 ; i < ArrowWidth ; i++)
		{
			Len	= (ArrowHeight * (i + 1) + ArrowWidth - 1) / ArrowWidth;
			Start	= Top + (ArrowHeight - Len) / 2;

			Move(RPort,Left + ArrowWidth - 1 - i,Start);
			Draw(RPort,Left + ArrowWidth - 1 - i,Start + Len - 1);
		}

		for(i = 0 ; i < LineWidth ; i++)
		{
			Move(RPort,Left + ArrowWidth - 1 - i,Top);
			Draw(RPort,Left + ArrowWidth - 1 - i,Top + Height - 1);
		}
	}
}
#endif	/* DO_TAPEDECK_KIND */


/*****************************************************************************/


STATIC VOID __regargs
LTK_DrawPicker(struct RastPort *RPort,BOOLEAN UpDirection,LONG Left,LONG Top,LONG Width,LONG Height)
{
	LONG i,Len,ArrowHeight,ArrowWidth,LineHeight,Start;

	LineHeight	= (Height + 15) / 16;
	ArrowHeight	= Height - (Height + 7) / 8;
	ArrowWidth	= Width;

	if(UpDirection)
	{
		for(i = 0 ; i < ArrowHeight ; i++)
		{
			Len	= (ArrowWidth * (i + 1) + ArrowHeight - 1) / ArrowHeight;
			Start	= Left + (ArrowWidth - Len) / 2;

			Move(RPort,Start,Top + i);
			Draw(RPort,Start + Len - 1,Top + i);
		}
	}
	else
	{
		for(i = 0 ; i < ArrowHeight ; i++)
		{
			Len	= (ArrowWidth * (i + 1) + ArrowHeight - 1) / ArrowHeight;
			Start	= Left + (ArrowWidth - Len) / 2;

			Move(RPort,Start,Top + ArrowHeight - 1 - i);
			Draw(RPort,Start + Len - 1,Top + ArrowHeight - 1 - i);
		}
	}

	for(i = 0 ; i < LineHeight ; i++)
	{
		Move(RPort,Left,Top + Height - 1 - i);
		Draw(RPort,Left + Width - 1,Top + Height - 1 - i);
	}
}


/*****************************************************************************/


#ifdef DO_TAPEDECK_KIND
STATIC VOID __regargs
LTK_RenderCircle(struct RastPort *RPort,LONG Left,LONG Top,LONG Radius,LONG AspectX,LONG AspectY)
{
	LONG x = 0,y = Radius,Delta = 2 * (1 - Radius),delta,Limit = 0,Length;

	while(y >= Limit)
	{
		if(Delta < 0)
		{
			delta = 2 * Delta + 2 * y - 1;

			if(delta > 0)
			{
				x = x + 1;
				y = y - 1;
				Delta = Delta + 2 * x - 2 * y + 2;
			}
			else
			{
				x = x + 1;
				Delta = Delta + 2 * x + 1;
			}
		}
		else
		{
			if(Delta > 0)
			{
				delta = 2 * Delta - 2 * x - 1;

				if(delta > 0)
				{
					y = y - 1;
					Delta = Delta - 2 * y + 1;
				}
				else
				{
					x = x + 1;
					y = y - 1;
					Delta = Delta + 2 * x - 2 * y + 2;
				}
			}
			else
			{
				x = x + 1;
				y = y - 1;
				Delta = Delta + 2 * x - 2 * y + 2;
			}
		}

		Length = (x * AspectY) / AspectX;

		Move(RPort,Left - Length,Top + y - 1);
		Draw(RPort,Left + Length - 1,Top + y - 1);
		Move(RPort,Left - Length,Top - y);
		Draw(RPort,Left + Length - 1,Top - y);
	}
}
#endif	/* DO_TAPEDECK_KIND */


/*****************************************************************************/


#ifdef DO_TAPEDECK_KIND
STATIC VOID __regargs
LTK_RenderArrow(struct RastPort *RPort,BOOLEAN LeftDirection,LONG Left,LONG Top,LONG Width,LONG Height)
{
	LONG i,Len,Start;

	if(LeftDirection)
	{
		for(i = 0 ; i < Width ; i++)
		{
			Len	= (Height * (i + 1) + Width - 1) / Width;
			Start	= Top + (Height - Len) / 2;

			Move(RPort,Left + i,Start);
			Draw(RPort,Left + i,Start + Len - 1);
		}
	}
	else
	{
		for(i = 0 ; i < Width ; i++)
		{
			Len	= (Height * (i + 1) + Width - 1) / Width;
			Start	= Top + (Height - Len) / 2;

			Move(RPort,Left + Width - 1 - i,Start);
			Draw(RPort,Left + Width - 1 - i,Start + Len - 1);
		}
	}
}
#endif	/* DO_TAPEDECK_KIND */


/*****************************************************************************/


#ifdef DO_TAPEDECK_KIND
STATIC VOID __regargs
LTK_DrawTapeButton(struct RastPort *RPort,ImageInfo *imageInfo,LONG Left,LONG Top,LONG Width,LONG Height,LONG AspectX,LONG AspectY,LONG Background)
{
	LONG		SizeX,SizeY,Mid,Size;
	ImageTypes	ImageType;

	ImageType	= imageInfo -> ImageType;
	SizeX		= imageInfo -> GlyphWidth;
	SizeY		= imageInfo -> GlyphHeight;

	Left	+= 6;
	Top	+= 3;

	Width	-= 2 * 6;
	Height	-= 2 * 3;

	if(!SizeY)
		SizeY = Height;

	if(!SizeX)
	{
		SizeX = (SizeY * AspectY) / AspectX;

		if(ImageType == IMAGECLASS_BACKWARD || ImageType == IMAGECLASS_FORWARD)
			SizeX *= 2;
	}

	if(SizeX > 0 && SizeY > 0)
	{
		switch(ImageType)
		{
			case IMAGECLASS_BACKWARD:
			case IMAGECLASS_FORWARD:

				Left	= Left + (Width - SizeX) / 2;
				Top	= Top + (Height - SizeY) / 2;

				LTK_DrawBackFore(RPort,ImageType == IMAGECLASS_BACKWARD,Left,Top,SizeX,SizeY);
				break;

			case IMAGECLASS_PREVIOUS:
			case IMAGECLASS_NEXT:

				Left	= Left + (Width - SizeX) / 2;
				Top	= Top + (Height - SizeY) / 2;

				LTK_DrawPrevNext(RPort,ImageType == IMAGECLASS_PREVIOUS,Left,Top,SizeX,SizeY);
				break;

			case IMAGECLASS_EJECT:

				Left	= Left + (Width - SizeX) / 2;
				Top	= Top + (Height - SizeY) / 2;

				LTK_DrawPicker(RPort,TRUE,Left,Top,SizeX,SizeY);
				break;

			case IMAGECLASS_RECORD:

				LTK_RenderCircle(RPort,Left + Width / 2,Top + Height / 2,SizeY / 2,AspectX,AspectY);
				break;

			case IMAGECLASS_PLAY:
			case IMAGECLASS_REWIND:

				Left	= Left + (Width - SizeX) / 2;
				Top	= Top + (Height - SizeY) / 2;

				LTK_RenderArrow(RPort,ImageType == IMAGECLASS_REWIND,Left,Top,SizeX,SizeY);
				break;

			case IMAGECLASS_STOP:

				Left	= Left + (Width - SizeX) / 2;
				Top	= Top + (Height - SizeY) / 2;

				RectFill(RPort,Left,Top,Left + SizeX - 1,Top + SizeY - 1);
				break;

			case IMAGECLASS_PAUSE:

				Left	= Left + (Width - SizeX) / 2;
				Top	= Top + (Height - SizeY) / 2;

				RectFill(RPort,Left,Top,Left + SizeX - 1,Top + SizeY - 1);

				Size	= SizeX - (2 * SizeX) / 3;
				Mid	= SizeX / 3;

				LTK_SetAPen(RPort,Background);
				RectFill(RPort,Left + Mid,Top,Left + Mid + Size - 1,Top + SizeY - 1);

				break;
		}
	}
}
#endif	/* DO_TAPEDECK_KIND */

/*****************************************************************************/


STATIC VOID __regargs
LTK_DrawBox(struct RastPort *rp,struct DrawInfo *drawInfo,LONG left,LONG top,LONG width,LONG height,BOOLEAN selected,BOOLEAN ghosted,ImageInfo *imageInfo)
{
	UWORD		*pens = drawInfo -> dri_Pens;
	UWORD		 pen1,pen2,pen3,pen4;
	ImageTypes	 imageType = imageInfo -> ImageType;

	if(selected)
	{
		pen1 = SHADOWPEN;
		pen2 = SHINEPEN;
		pen3 = FILLPEN;
		pen4 = FILLTEXTPEN;
	}
	else
	{
		pen1 = SHINEPEN;
		pen2 = SHADOWPEN;
		pen3 = BACKGROUNDPEN;
		pen4 = TEXTPEN;
	}

	LTK_SetPens(rp,pens[pen1],0,JAM1);
	Move(rp,left + 1,top + 1);
	Draw(rp,left + 1,top + height - 2);
	Draw(rp,left,top + height - 1);
	Draw(rp,left,top);
	Draw(rp,left + width - 2,top);

	LTK_SetAPen(rp,pens[pen2]);
	Move(rp,left + width - 2,top + height - 2);
	Draw(rp,left + width - 2,top + 1);
	Draw(rp,left + width - 1,top);
	Draw(rp,left + width - 1,top + height - 1);
	Draw(rp,left + 1,top + height - 1);

	LTK_SetAPen(rp,pens[pen3]);
	RectFill(rp,left + 2,top + 1,left + 2 + width - 5,top + 1 + height - 3);

	LTK_SetAPen(rp,pens[pen4]);

	switch(imageType)
	{
		case IMAGECLASS_PICKER:
		{
			LONG	Left	= left + 4,
				Top	= top + 2,
				Width	= width - 8,
				Height	= height - 4;

			LTK_DrawPicker(rp,FALSE,Left,Top,Width,Height);
			break;
		}

		case IMAGECLASS_LEFTINCREMENTER:
		case IMAGECLASS_RIGHTINCREMENTER:

			LTK_DrawIncrementer(rp,imageType == IMAGECLASS_LEFTINCREMENTER,left + 2,top + 1,width - 4,height - 2);
			break;
#ifdef DO_TAPEDECK_KIND
		case IMAGECLASS_BACKWARD:
		case IMAGECLASS_FORWARD:
		case IMAGECLASS_PREVIOUS:
		case IMAGECLASS_NEXT:
		case IMAGECLASS_RECORD:
		case IMAGECLASS_PLAY:
		case IMAGECLASS_STOP:
		case IMAGECLASS_PAUSE:
		case IMAGECLASS_EJECT:
		case IMAGECLASS_REWIND:

			LTK_DrawTapeButton(rp,imageInfo,left,top,width,height,drawInfo -> dri_Resolution . X,drawInfo -> dri_Resolution . Y,pens[pen3]);
			break;
#endif	/* DO_TAPEDECK_KIND */
	}

	if(ghosted)
	{
		if(width > 4 || height > 2)
		{
			LTK_SetAPen(rp,pens[BLOCKPEN]);

			SetAfPt(rp,(UWORD *)&ghostingPat,1);
			RectFill(rp,left + 2,top + 1,left + width - 1 - 2,top + height - 1 - 1);
			SetAfPt(rp,NULL,0);
		}
	}
}


/*****************************************************************************/


STATIC ULONG __regargs
LTK_ImageClassDraw(struct Image *image,struct impDraw *drawMsg,ImageInfo *imageInfo)
{
	LTK_DrawBox(drawMsg -> imp_RPort,drawMsg -> imp_DrInfo,image -> LeftEdge + drawMsg -> imp_Offset . X,image -> TopEdge + drawMsg -> imp_Offset . Y,image -> Width,image -> Height,drawMsg -> imp_State == IDS_SELECTED,drawMsg -> imp_State == IDS_DISABLED,imageInfo);

	return(TRUE);
}


/*****************************************************************************/


STATIC ULONG __regargs
LTK_ImageClassErase(struct Image *image,struct impErase *eraseMsg)
{
	LONG left;
	LONG top;

	left	= eraseMsg -> imp_Offset . X + image -> LeftEdge;
	top	= eraseMsg -> imp_Offset . Y + image -> TopEdge;

	LTK_SetAPen(eraseMsg -> imp_RPort,0);
	RectFill(eraseMsg -> imp_RPort,left,top,left + image -> Width - 1,top + image -> Height - 1);

	return(TRUE);
}


/*****************************************************************************/

STATIC ULONG __regargs
LTK_ImageClassNew(struct IClass *class,Object *object,struct opSet *SetMethod)
{
	struct TagItem *Tag;

	if(Tag = FindTagItem(IIA_ImageType,SetMethod -> ops_AttrList))
	{
		struct Image *NewImage;

		if(NewImage = (struct Image *)DoSuperMethodA(class,object,(Msg)SetMethod))
		{
			struct ImageInfo *MoreInfo = INST_DATA(class,NewImage);

			MoreInfo -> ImageType	= Tag -> ti_Data;
			MoreInfo -> GlyphWidth	= GetTagData(IIA_GlyphWidth,0,SetMethod -> ops_AttrList);
			MoreInfo -> GlyphHeight	= GetTagData(IIA_GlyphHeight,0,SetMethod -> ops_AttrList);

			return((ULONG)NewImage);
		}
	}

	return(NULL);
}


/*****************************************************************************/


STATIC ULONG __saveds __asm
LTK_ImageDispatch(register __a0 struct IClass *class,register __a2 Object *object,register __a1 Msg msg)
{
	switch(msg -> MethodID)
	{
		case IM_ERASE:

			return(LTK_ImageClassErase((struct Image *)object,(struct impErase *)msg));

		case IM_DRAW:

			return(LTK_ImageClassDraw((struct Image *)object,(struct impDraw *)msg,(ImageInfo *)INST_DATA(class,object)));

		case OM_NEW:

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

		default:

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


/*****************************************************************************/


STATIC VOID __regargs
LTK_TunnelDraw(struct RastPort *rp,LONG from,LONG to,LONG y,LONG left,LONG right)
{
	Draw(rp,from,y);
	Draw(rp,left - 1,y);
	Move(rp,right + 1,y);
	Draw(rp,to,y);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_DrawGroove(LayoutHandle *handle,LONG left,LONG top,LONG width,LONG height,LONG from,LONG to)
{
	struct RastPort *rp = &handle -> RPort;

	LTK_SetAPen(rp,handle -> ShadowPen);

	Move(rp,left + 1,top + 1);
	Draw(rp,left + 1,top + height - 2);
	Draw(rp,left,top + height - 1);
	LTK_TunnelDraw(rp,left,left + width - 2,top,from,to);
	Draw(rp,left + width - 4,top + 2);
	Draw(rp,left + width - 4,top + height - 3);
	Move(rp,left + width - 3,top + 2);
	Draw(rp,left + width - 3,top + height - 2);
	Draw(rp,left + 3,top + height - 2);

	LTK_SetAPen(rp,handle -> ShinePen);
	Move(rp,left + width - 2,top + height - 2);
	Draw(rp,left + width - 2,top + 1);
	Draw(rp,left + width - 1,top);
	Draw(rp,left + width - 1,top + height - 1);
	Draw(rp,left + 1,top + height - 1);
	Draw(rp,left + 3,top + height - 3);
	Draw(rp,left + 3,top + 2);
	Move(rp,left + 2,top + height - 3);
	LTK_TunnelDraw(rp,left + 2,left + width - 4,top + 1,from,to);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_DrawLabel(LayoutHandle *handle,ObjectNode *label)
{
	struct RastPort	*rp;
	LONG		 left;

	left	= label -> Left + (label -> Width - label -> LabelWidth) / 2;
	rp	= &handle -> RPort;

	LockLayerRom(rp -> Layer);

	LTK_DrawGroove(handle,label -> Left + handle -> GlyphWidth / 2,label -> Top + handle -> RPort . TxHeight / 2,label -> Width - handle -> GlyphWidth,label -> Height - (handle -> RPort . TxHeight + handle -> InterHeight) / 2,left,left + label -> LabelWidth - 1);

	LTK_SetPens(rp,handle -> DrawInfo -> dri_Pens[HIGHLIGHTTEXTPEN],0,JAM1);
	LTK_PlaceText(handle,label -> Label,strlen(label -> Label),left + handle -> GlyphWidth,label -> Top + handle -> RPort . TxBaseline);

	UnlockLayerRom(rp -> Layer);
}


/*****************************************************************************/


STATIC APTR __regargs
LTK_Alloc(LayoutHandle *handle,ULONG amount)
{
	APTR result = NULL;

	if(!handle -> Failed)
	{
		result = AsmAllocPooled(handle -> Pool,amount,SysBase);

		if(!result)
			handle -> Failed = TRUE;
	}

	return(result);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_Free(LayoutHandle *handle,APTR mem,ULONG memsize)
{
	if(mem)
		AsmFreePooled(handle -> Pool,mem,memsize,SysBase);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_DetermineSize(LayoutHandle *Handle,ObjectNode *Node)
{
	if(!Handle -> Failed)
	{
		LONG i,Len,Width,Max;

		if(Node -> Label)
			Node -> LabelWidth = LT_LabelWidth(Handle,Node -> Label);
		else
			Node -> LabelWidth = 0;

		switch(Node -> Type)
		{
			case GROUP_KIND:

				LTK_LayoutGroup(Handle,Node);

				break;

			case XBAR_KIND:

				Node -> Width	= Handle -> GlyphWidth;
				Node -> Height	= 6;

				break;

			case YBAR_KIND:

				Node -> Width	= 6;
				Node -> Height	= Handle -> RPort . TxHeight;

				break;

			case FRAME_KIND:

				if(Node -> Special . Frame . DrawBox)
				{
					Node -> Width	= 4 + Node -> Special . Frame . InnerWidth + 4;
					Node -> Height	= 2 + Node -> Special . Frame . InnerHeight + 2;
				}
				else
				{
					Node -> Width	= Node -> Special . Frame . InnerWidth;
					Node -> Height	= Node -> Special . Frame . InnerHeight;
				}

				break;

			case BOX_KIND:

				Node -> LabelWidth = 0;

				if(Node -> Special . Box . Labels)
				{
					for(i = 0 ; i < Node -> Lines ; i++)
					{
						if((Width = TextLength(&Handle -> RPort,Node -> Special . Box . Labels[i],strlen(Node -> Special . Box . Labels[i]))) > Node -> LabelWidth)
							Node -> LabelWidth = Width;
					}
				}

				Max = Node -> Chars;

				if(Node -> Special . Box . Lines)
				{
					LONG MaxWidth = 0;

					for(i = 0 ; i < Node -> Lines ; i++)
					{
						if(Node -> Special . Box . Lines[i])
						{
							if(Len = strlen(Node -> Special . Box . Lines[i]))
							{
								if((Width = TextLength(&Handle -> RPort,Node -> Special . Box . Lines[i],Len)) > MaxWidth)
									MaxWidth = Width;
							}
						}
					}

					if(Max < (MaxWidth + Handle -> GlyphWidth - 1) / Handle -> GlyphWidth)
						Max = (MaxWidth + Handle -> GlyphWidth - 1) / Handle -> GlyphWidth;
				}

				Node -> Height	= 2 + Node -> Lines * Handle -> RPort . TxHeight + 2;
				Node -> Width	= 4 + Max * Handle -> GlyphWidth + 4;

				break;

			case BUTTON_KIND:

				if(Node -> Chars * Handle -> GlyphWidth > Node -> LabelWidth)
					Node -> Width = Node -> Chars * Handle -> GlyphWidth;
				else
					Node -> Width = Node -> LabelWidth;

				Node -> LabelWidth = 0;
				Node -> LabelPlace = PLACE_IN;

				if(Node -> Special . Button . ExtraFat)
				{
					Node -> Width	= 6 + Handle -> GlyphWidth + Node -> Width + Handle -> GlyphWidth + 6;
					Node -> Height	= 2 + (3 * Handle -> RPort . TxHeight) / 2 + 2;
				}
				else
				{
					Node -> Width	= 6 + Node -> Width + 6;
					Node -> Height	= 3 + Handle -> RPort . TxHeight + 3;
				}

				break;

			case CHECKBOX_KIND:

				Node -> Width = Node -> Height = (3 * Handle -> RPort . TxHeight) / 2;

				if(!V39)
				{
					if(Node -> Width < CHECKBOX_WIDTH)
						Node -> Width = CHECKBOX_WIDTH;

					if(Node -> Height < CHECKBOX_HEIGHT)
						Node -> Height = CHECKBOX_HEIGHT;
				}
				else
				{
					Node -> Width = (Node -> Width * Handle -> AspectY) / Handle -> AspectX;

					if(Node -> Height < CHECKBOX_HEIGHT)
					{
						Node -> Height	= CHECKBOX_HEIGHT;
						Node -> Width	= (Node -> Height * Handle -> AspectY) / Handle -> AspectX;
					}
				}

				break;

			case LISTVIEW_KIND:

				i	= 0;
				Max	= 0;
				Width	= Node -> Chars * Handle -> GlyphWidth;

				if(Node -> Special . List . Labels)
				{
					struct Node *Item;

					SCANLIST(Node -> Special . List . Labels,Item)
					{
						i++;

						if((Len = TextLength(&Handle -> RPort,Item -> ln_Name,strlen(Item -> ln_Name))) > Max)
							Max = Len;
					}
				}

				Node -> Max = i - 1;

				if(Max > Width)
				{
					Width = Max;

					Node -> Chars = Width / Handle -> GlyphWidth;
				}

				if(!V39)
					Node -> Width = (4 + Width + 4) + (2 + 2 * Handle -> GlyphWidth + 2) + 8;
				else
					Node -> Width = (4 + Width + 4) + (2 + 2 * Handle -> GlyphWidth + 2);

				Node -> Height = 2 + Node -> Lines * Handle -> RPort . TxHeight + 2;

				if(Node -> Special . List . ReadOnly)
				{
					if(Node -> Special . List . LinkID != -1)
					{
						if(!V39)
							Node -> Height += 2 + Handle -> RPort . TxHeight + 2;
					}
				}
				else
				{
					if(Node -> Special . List . LinkID != -1 && Node -> Special . List . LinkID != NIL_LINK)
						Node -> Height += 3 + Handle -> RPort . TxHeight + 3;
				}

				if(Node -> Special . List . ExtraLabels)
				{
					for(i = Max = 0 ; Node -> Special . List . ExtraLabels[i] ; i++)
					{
						if((Len = TextLength(&Handle -> RPort,Node -> Special . List . ExtraLabels[i],strlen(Node -> Special . List . ExtraLabels[i]))) > Max)
							Max = Len;
					}

					Node -> Special . List . ExtraLabelWidth = Max;
				}

				if(Node -> LabelWidth > Node -> Width && (Node -> LabelPlace == PLACE_ABOVE || Node -> LabelPlace == PLACE_BELOW))
					Node -> Width = Node -> LabelWidth;

				break;

			case MX_KIND:

				i	= 0;
				Max	= 0;

				while(Node -> Special . Radio . Choices[i])
				{
					if((Width = TextLength(&Handle -> RPort,Node -> Special . Radio . Choices[i],strlen(Node -> Special . Radio . Choices[i]))) > Max)
						Max = Width;

					i++;
				}

				Node -> Max = i - 1;

				Node -> Special . Radio . LabelWidth = Max;

				Node -> Height = MAX(MX_HEIGHT,Handle -> RPort . TxHeight);

				if(!V39)
					Node -> Width = MX_WIDTH;
				else
				{
					Node -> Width = (Node -> Height * Handle -> AspectY) / Handle -> AspectX;

					if(Node -> Height < MX_HEIGHT)
					{
						Node -> Height = MX_HEIGHT;
						Node -> Width  = (Node -> Height * Handle -> AspectX) / Handle -> AspectY;
					}
				}

				Node -> Lines  = i;
				Node -> Height = Node -> Lines * (Node -> Height + Handle -> InterHeight) - Handle -> InterHeight;

				break;

			case TEXT_KIND:

				if(Node -> Special . Text . Text)
					Max = TextLength(&Handle -> RPort,Node -> Special . Text . Text,strlen(Node -> Special . Text . Text));
				else
					Max = 0;

				Width = Node -> Chars * Handle -> GlyphWidth;

				if(Max > Width)
					Width = Max;

				Node -> Width	= 6 + Width + 6;
				Node -> Height	= 3 + Handle -> RPort . TxHeight + 3;

				if(Node -> Special . Text . UsePicker)
					Node -> Width += (Node -> Height * Handle -> AspectY) / Handle -> AspectX;

				if(Node -> LabelWidth > Node -> Width && (Node -> LabelPlace == PLACE_ABOVE || Node -> LabelPlace == PLACE_BELOW))
					Node -> Width = Node -> LabelWidth;

				break;

#ifdef DO_GAUGE_KIND
			case GAUGE_KIND:

				Width = TextLength(&Handle -> RPort,"0% 100%",7);

				if(Handle -> GlyphWidth * Node -> Chars > Width)
					Width = Handle -> GlyphWidth * Node -> Chars;

				Node -> Width	= 6 + Width + 6;
				Node -> Height	= 3 + Handle -> RPort . TxHeight + 2 + Handle -> InterHeight + Handle -> RPort . TxHeight + 3;

				if(Node -> Label)
					Node -> LabelWidth = LT_LabelWidth(Handle,Node -> Label);
				else
					Node -> LabelWidth = 0;

				if(Node -> LabelWidth > Node -> Width && (Node -> LabelPlace == PLACE_ABOVE || Node -> LabelPlace == PLACE_BELOW))
					Node -> Width = Node -> LabelWidth;

				break;
#endif

#ifdef DO_TAPEDECK_KIND
			case TAPEDECK_KIND:
			{
				LONG Width,Height;

				Width = TextLength(&Handle -> RPort,"AA",2);

				if(Width < 2 * Handle -> GlyphWidth);
					Width = 2 * Handle -> GlyphWidth;

				if(Width < 2 * Handle -> RPort . TxHeight)
					Width = 2 * Handle -> RPort . TxHeight;

				if(Node -> Special . TapeDeck . Smaller)
					Width = (3 * Width) / 4;

				if(Width & 1)
					Width++;

				Width	= 1 + ((Width + 5) / 6) * 6;
				Height	= (Handle -> AspectX * Width) / (Handle -> AspectY * 2);

				if(!(Height & 1))
					Height++;

				if(Node -> Special . TapeDeck . ButtonType == TDBT_BACKWARD || Node -> Special . TapeDeck . ButtonType == TDBT_FORWARD)
					Node -> Special . TapeDeck . ButtonWidth = Width;
				else
					Node -> Special . TapeDeck . ButtonWidth = Width / 2;

				Node -> Special . TapeDeck . ButtonHeight = Height;

				if(Handle -> GlyphWidth * Node -> Chars > Width)
					Width = Handle -> GlyphWidth * Node -> Chars;

				Node -> Width	= 6 + Width + 6;
				Node -> Height	= 3 + Height + 3;

				if(Node -> LabelWidth > Node -> Width && (Node -> LabelPlace == PLACE_ABOVE || Node -> LabelPlace == PLACE_BELOW))
					Node -> Width = Node -> LabelWidth;

				break;
			}
#endif	/* DO_TAPEDECK_KIND */

			case NUMBER_KIND:

				Node -> Width	= 6 + Node -> Chars * Handle -> GlyphWidth + 6;
				Node -> Height	= 2 + Handle -> RPort . TxHeight + 2;

				break;

			case CYCLE_KIND:

				Width = Node -> Chars * Handle -> GlyphWidth;

				if(Node -> Special . Cycle . Choices)
				{
					for(i = 0 ; Node -> Special . Cycle . Choices[i] ; i++)
					{
						if((Len = TextLength(&Handle -> RPort,Node -> Special . Cycle . Choices[i],strlen(Node -> Special . Cycle . Choices[i]))) > Width)
							Width = Len;
					}

					Node -> Max = i - 1;
				}

				Node -> Width	= 26 + Width + 6;
				Node -> Height	= 3 + Handle -> RPort . TxHeight + 3;

				if(Node -> LabelWidth > Node -> Width && (Node -> LabelPlace == PLACE_ABOVE || Node -> LabelPlace == PLACE_BELOW))
					Node -> Width = Node -> LabelWidth;

				break;

			case PALETTE_KIND:

				if(Node -> Special . Palette . NumColours)
					Node -> Max = Node -> Min + Node -> Special . Palette . NumColours - 1;
				else
				{
					if(!Node -> Special . Palette . Depth)
						Node -> Special . Palette . Depth = 1;

					Node -> Special . Palette . NumColours = 1L << Node -> Special . Palette . Depth;

					Node -> Max = Node -> Min + Node -> Special . Palette . NumColours - 1;
				}

				if(Node -> Special . Palette . SmallPalette)
				{
					Node -> Width   = Handle -> GlyphWidth * (Node -> Max - Node -> Min + 1);
					Node -> Height  = 1 + Handle -> RPort . TxHeight + 1;
				}
				else
				{
					Node -> Width   = 2 * Handle -> GlyphWidth * (Node -> Max - Node -> Min + 1);
					Node -> Height  = 2 * Handle -> RPort . TxHeight;
				}

				if(Node -> Chars * Handle -> GlyphWidth > Node -> Width)
					Node -> Width = Node -> Chars * Handle -> GlyphWidth;

				if(Node -> Special . Palette . NumColours > 16)
				{
					Node -> Height *= 2;
					Node -> Width	/= 2;
				}

				if(Node -> Lines)
				{
					if(!Node -> Chars)
						Node -> Width = Handle -> GlyphWidth;
					else
						Node -> Width = Node -> Chars * Handle -> GlyphWidth;

					Node -> Height	= Node -> Lines * Handle -> RPort . TxHeight;
				}

				break;

			case SCROLLER_KIND:

				if(Node -> Special . Scroller . Vertical)
				{
					if(Node -> Special . Scroller . Thin)
						Node -> Width = 6 + Handle -> GlyphWidth + 6;
					else
						Node -> Width = 6 + (3 * Handle -> GlyphWidth) / 2 + 6;

					Node -> Height = 2 + Node -> Lines * Handle -> RPort . TxHeight;

					if(Node -> Special . Scroller . Arrows)
					{
						WORD ScrollerHeight = (Handle -> AspectX * Node -> Width) / Handle -> AspectY;

						if(Node -> Height < 2 + Node -> Lines * Handle -> RPort . TxHeight + 2 * ScrollerHeight)
							Node -> Height = 2 + Node -> Lines * Handle -> RPort . TxHeight + 2 * ScrollerHeight;

						Node -> Special . Scroller . ArrowSize = ScrollerHeight;
					}
				}
				else
				{
					Node -> Width  = 6 + Node -> Chars * Handle -> GlyphWidth + 6;
					Node -> Height = 2 + Handle -> RPort . TxHeight;

					if(Node -> Special . Scroller . Arrows)
					{
						WORD ScrollerWidth = (Handle -> AspectY * Node -> Height) / Handle -> AspectX;

						if(Node -> Width < 6 + Handle -> GlyphWidth + 2 * ScrollerWidth + 6)
							Node -> Width = 6 + Handle -> GlyphWidth + 2 * ScrollerWidth + 6;

						Node -> Special . Scroller . ArrowSize = ScrollerWidth;
					}
				}

				if(Node -> LabelWidth > Node -> Width && (Node -> LabelPlace == PLACE_ABOVE || Node -> LabelPlace == PLACE_BELOW))
					Node -> Width = Node -> LabelWidth;

				break;

			case SLIDER_KIND:

				Node -> Width	= 6 + Node -> Chars * Handle -> GlyphWidth + 6;
				Node -> Height	= 2 + Handle -> RPort . TxHeight + 1;

				LT_LevelWidth(Handle,Node -> Special . Slider . LevelFormat,Node -> Special . Slider . DispFunc,Node -> Min,Node -> Max,&Node -> Special . Slider . LevelWidth,&Node -> Special . Slider . MaxLevelLen,Node -> Special . Slider . FullLevelCheck);

				if(!V40)
				{
					if(Node -> Special . Slider . MaxLevelLen * Handle -> RPort . TxWidth < Node -> Special . Slider . LevelWidth)
						Node -> Special . Slider . MaxLevelLen = (Node -> Special . Slider . LevelWidth + Handle -> RPort . TxWidth - 1) / Handle -> RPort . TxWidth;
				}

				if(Node -> Special . Slider . LevelPlace == PLACETEXT_LEFT && Node -> Label)
				{
					LONG	space,len;
					STRPTR	Buffer;

					space = TextLength(&Handle -> RPort," ",1);
					space = (Node -> Special . Slider . LevelWidth + space - 1) / space;

					if(!V40 && (Handle -> RPort . TxFlags & FPF_PROPORTIONAL))
					{
						Node -> Special . Slider . MaxLevelLen++;

						space++;
					}

					if(Node -> Special . Slider . OriginalLabel)
						LTK_Free(Handle,Node -> Label,strlen(Node -> Label) + 1);
					else
						Node -> Special . Slider . OriginalLabel = Node -> Label;

					len = strlen(Node -> Special . Slider . OriginalLabel);

					if(Buffer = LTK_Alloc(Handle,len + space + 1))
					{
						strcpy(Buffer,Node -> Special . Slider . OriginalLabel);

						while(space > 0)
						{
							Buffer[len] = ' ';
							len++;
							space--;
						}

						Buffer[len] = 0;

						Node -> Label		= Buffer;
						Node -> LabelWidth	= LT_LabelWidth(Handle,Node -> Label);
					}
					else
						break;
				}

				if(Node -> LabelWidth > Node -> Width && (Node -> LabelPlace == PLACE_ABOVE || Node -> LabelPlace == PLACE_BELOW))
					Node -> Width = Node -> LabelWidth;

				break;

			case STRING_KIND:

				Node -> Width	= 6 + Node -> Chars * Handle -> GlyphWidth + 6;
				Node -> Height	= 3 + Handle -> RPort . TxHeight + 3;

				if(Node -> Special . String . UsePicker)
					Node -> Width += (Node -> Height * Handle -> AspectY) / Handle -> AspectX;

				if(Node -> LabelWidth > Node -> Width && (Node -> LabelPlace == PLACE_ABOVE || Node -> LabelPlace == PLACE_BELOW))
					Node -> Width = Node -> LabelWidth;

				break;

			case INTEGER_KIND:

				Node -> Width	= 6 + Node -> Chars * Handle -> GlyphWidth + 6;
				Node -> Height	= 3 + Handle -> RPort . TxHeight + 3;

				if(Node -> Special . Integer . UseIncrementers)
				{
					Node -> Width += (Node -> Height * 4) / 5;
					Node -> Width += (Node -> Height * 4) / 5;
				}

				if(Node -> LabelWidth > Node -> Width && (Node -> LabelPlace == PLACE_ABOVE || Node -> LabelPlace == PLACE_BELOW))
					Node -> Width = Node -> LabelWidth;

				break;

			case PASSWORD_KIND:
			case FRACTION_KIND:

				Node -> Width	= 6 + Node -> Chars * Handle -> GlyphWidth + 6;
				Node -> Height	= 3 + Handle -> RPort . TxHeight + 3;

				if(Node -> LabelWidth > Node -> Width && (Node -> LabelPlace == PLACE_ABOVE || Node -> LabelPlace == PLACE_BELOW))
					Node -> Width = Node -> LabelWidth;

				break;
		}
	}
}


/*****************************************************************************/


STATIC ULONG __asm __saveds
LTK_DefaultHistoryHook(register __a0 struct Hook *Hook,register __a1 STRPTR NewString,register __a2 struct Gadget *Gadget)
{
	ObjectNode		*Node	= (ObjectNode *)Gadget -> UserData;
	struct LayoutHandle	*Handle	= Node -> Special . String . LayoutHandle;
	struct MinList		*List	= (struct MinList *)Hook -> h_Data;

	if(NewString)
	{
		ULONG *Data;

		if(Data = (ULONG *)LTK_Alloc(Handle,sizeof(ULONG) + sizeof(struct Node) + strlen(NewString) + 1))
		{
			struct Node *NewNode;

			*Data++ = sizeof(ULONG) + sizeof(struct Node) + strlen(NewString) + 1;

			NewNode = (struct Node *)Data;

			NewNode -> ln_Name = (STRPTR)(NewNode + 1);

			strcpy(NewNode -> ln_Name,NewString);

			AddTail((struct List *)List,NewNode);
		}
		else
			return(FALSE);
	}
	else
	{
		if(List -> mlh_Head -> mln_Succ)
		{
			ULONG *Data = (ULONG *)RemHead((struct List *)List);

			LTK_Free(Handle,&Data[-1],Data[-1]);
		}
	}

	return(TRUE);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_Spread(LayoutHandle *Handle,ObjectNode *Group,LONG Width,LONG Height)
{
	ObjectNode	*Node;
	LONG		 Count;

	Count = 0;

	SCANGROUP(Group,Node)
	{
		Count++;
	}

	if(Group -> Special . Group . Horizontal)
	{
		LONG Room = Width - Group -> Width,Left,Width,i = 0;

		if(Count > 1)
		{
			Left = 0;

			Room += --Count * Handle -> InterWidth;
		}
		else
			Left = Room / 2;

		SCANGROUP(Group,Node)
		{
			if(!LIKE_STRING_KIND(Node) || Node -> Special . String . LinkID == -1)
			{
				Width = Node -> Width;

				switch(Node -> LabelPlace)
				{
					case PLACE_LEFT:

						if(Node -> Type == MX_KIND)
						{
							Left += Node -> Special . Radio . LabelWidth + INTERWIDTH;

							if(Node -> Label)
							{
								if(Node -> Special . Radio . TitlePlace == PLACETEXT_LEFT)
									Left += Node -> LabelWidth + INTERWIDTH;
								else
									Width += INTERWIDTH + Node -> LabelWidth;
							}
						}
						else
							Left += Node -> LabelWidth + INTERWIDTH;

						break;

					case PLACE_RIGHT:

						if(Node -> Type == MX_KIND)
						{
							Width += INTERWIDTH + Node -> Special . Radio . LabelWidth;

							if(Node -> Label)
							{
								if(Node -> Special . Radio . TitlePlace == PLACETEXT_LEFT)
									Left += Node -> LabelWidth + INTERWIDTH;
								else
									Width += INTERWIDTH + Node -> LabelWidth;
							}
						}
						else
							Width += INTERWIDTH + Node -> LabelWidth;

						break;
				}

				if(Node -> Type == LISTVIEW_KIND && Node -> Special . List . ExtraLabelWidth)
					Left += Node -> Special . List . ExtraLabelWidth + INTERWIDTH;

				if(Node -> Type == SLIDER_KIND && Node -> Special . Slider . LevelPlace == PLACETEXT_RIGHT)
					Width += INTERWIDTH + Node -> Special . Slider . LevelWidth;

				Node -> Left = Left;

				Left += Width + ((Room * (i + 1)) / Count - (Room * i) / Count);

				i++;
			}
		}
	}
	else
	{
		LONG Room = Height - Group -> Height,Height,Top,y,i = 0;

		if(Count > 1)
		{
			Top = 0;

			Room += --Count * Handle -> InterHeight;
		}
		else
			Top = Room / 2;

		SCANGROUP(Group,Node)
		{
			if(!LIKE_STRING_KIND(Node) || Node -> Special . String . LinkID == -1)
			{
				if(Count++)
				{
					if(Node -> ExtraSpace > 0)
						y = Top + Node -> ExtraSpace * Handle -> InterHeight;
					else
					{
						if(Node -> ExtraSpace < 0)
							y = Top - Node -> ExtraSpace;
						else
							y = Top + Handle -> InterHeight;
					}
				}
				else
					y = Top;

				if(Node -> LabelPlace == PLACE_ABOVE)
					y += Handle -> RPort . TxHeight + INTERHEIGHT;

				Node -> Top = y;

				if(Node -> LabelPlace == PLACE_BELOW && Node -> Label)
					Height = Node -> Height + INTERHEIGHT + Handle -> RPort . TxHeight;
				else
					Height = Node -> Height;

				Top = y + Height + ((Room * (i + 1)) / Count - (Room * i) / Count);

				i++;
			}
		}
	}
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_LayoutGroup(LayoutHandle *Handle,ObjectNode *Group)
{
	if(!Handle -> Failed)
	{
		ObjectNode	*Node;
		ObjectNode	*ReturnKey;
		ObjectNode	*EscKey;
		ObjectNode	*CursorKey;
		LONG		 Left;
		LONG		 Top;
		LONG		 Width;
		LONG		 Height;

		Left		= 0;
		Top		= 0;
		ReturnKey	= NULL;
		EscKey		= NULL;
		CursorKey	= NULL;

		if(Group -> Special . Group . SameSize)
		{
			LONG MaxWidth  = 0;
			LONG MaxHeight = 0;

			SCANGROUP(Group,Node)
			{
				LTK_DetermineSize(Handle,Node);

				if(Node -> Type == BUTTON_KIND)
				{
					if(Node -> Special . Button . ReturnKey)
						ReturnKey = Node;
					else
					{
						if(Node -> Special . Button . EscKey)
							EscKey = Node;
					}
				}

				if(Node -> Type == LISTVIEW_KIND && Node -> Special . List . CursorKey)
					CursorKey = Node;

				if(Node -> Width > MaxWidth)
					MaxWidth = Node -> Width;

				if(Node -> Height > MaxHeight)
					MaxHeight = Node -> Height;
			}

			if(ReturnKey)
			{
				SCANGROUP(Group,Node)
				{
					Node -> GroupIndent = TRUE;

					Node -> Width	+= 4 + 4;
					Node -> Height	+= 2 + 2;

					if(Node -> Width > MaxWidth)
						MaxWidth = Node -> Width;

					if(Node -> Height > MaxHeight)
						MaxHeight = Node -> Height;
				}
			}

			SCANGROUP(Group,Node)
			{
				if(Node -> Width < MaxWidth)
				{
					if(Node -> Type == GROUP_KIND)
						Node -> Special . Group . ExtraLeft += (MaxWidth - Node -> Width) / 2;

					Node -> Width = MaxWidth;
				}

				if(Node -> Height < MaxHeight)
				{
					if(Node -> Type == GROUP_KIND)
						Node -> Special . Group . ExtraTop += (MaxHeight - Node -> Height) / 2;

					Node -> Height = MaxHeight;
				}
			}
		}
		else
		{
			SCANGROUP(Group,Node)
			{
				LTK_DetermineSize(Handle,Node);

				if(Node -> Type == BUTTON_KIND)
				{
					if(Node -> Special . Button . ReturnKey)
						ReturnKey = Node;
					else
					{
						if(Node -> Special . Button . EscKey)
							EscKey = Node;
					}
				}

				if(Node -> Type == LISTVIEW_KIND && Node -> Special . List . CursorKey)
					CursorKey = Node;
			}

			if(ReturnKey)
			{
				SCANGROUP(Group,Node)
				{
					Node -> GroupIndent = TRUE;

					Node -> Width	+= 4 + 4;
					Node -> Height	+= 2 + 2;
				}
			}
		}

		if(Group -> Special . Group . Horizontal)
		{
			LONG x,y,w,MaxTop = 0,MaxWidth = 0,MaxHeight = 0,Count = 0;

			if(Group -> Node . mln_Pred -> mln_Pred && Group -> Special . Group . LastAttributes)
			{
				ObjectNode *LastGroup = (ObjectNode *)Group -> Node . mln_Pred;

				MaxTop		= LastGroup -> Special . Group . MaxOffset;
				MaxHeight	= LastGroup -> Special . Group . MaxSize;
			}

			SCANGROUP(Group,Node)
			{
				if(!LIKE_STRING_KIND(Node) || Node -> Special . String . LinkID == -1)
				{
					if(!Group -> Special . Group . Paging && Count++)
					{
						if(Node -> ExtraSpace > 0)
							x = Left + Node -> ExtraSpace * Handle -> InterWidth;
						else
						{
							if(Node -> ExtraSpace < 0)
								x = Left - Node -> ExtraSpace;
							else
								x = Left + Handle -> InterWidth;
						}
					}
					else
						x = Left;

					y = Top;
					w = Node -> Width;

					switch(Node -> LabelPlace)
					{
						case PLACE_LEFT:

							if(Node -> Type == MX_KIND)
							{
								x += Node -> Special . Radio . LabelWidth + INTERWIDTH;

								if(Node -> Label)
								{
									if(Node -> Special . Radio . TitlePlace == PLACETEXT_LEFT)
										x += Node -> LabelWidth + INTERWIDTH;
									else
										w += INTERWIDTH + Node -> LabelWidth;
								}
							}
							else
							{
								if(Node -> Label || Node -> Type == BOX_KIND)
									x += Node -> LabelWidth + INTERWIDTH;
							}

							break;

						case PLACE_RIGHT:

							if(Node -> Type == MX_KIND)
							{
								w += INTERWIDTH + Node -> Special . Radio . LabelWidth;

								if(Node -> Label)
								{
									if(Node -> Special . Radio . TitlePlace == PLACETEXT_LEFT)
										x += Node -> LabelWidth + INTERWIDTH;
									else
										w += INTERWIDTH + Node -> LabelWidth;
								}
							}
							else
							{
								if(Node -> Label || Node -> Type == BOX_KIND)
									w += INTERWIDTH + Node -> LabelWidth;
							}

							break;

						case PLACE_ABOVE:

							if(Node -> Label || Node -> Type == BOX_KIND)
								y += Handle -> RPort . TxHeight + INTERHEIGHT;

							break;

						case PLACE_BELOW:

							break;

						case PLACE_IN:

							break;
					}

					if(Node -> Type == SLIDER_KIND && Node -> Special . Slider . LevelPlace == PLACETEXT_RIGHT)
						w += INTERWIDTH + Node -> Special . Slider . LevelWidth;

					if(Node -> Type == LISTVIEW_KIND && Node -> Special . List . ExtraLabelWidth)
						x += Node -> Special . List . ExtraLabelWidth + INTERWIDTH;

					if(y > MaxTop)
						MaxTop = y;

					if(Node -> Height > MaxHeight)
						MaxHeight = Node -> Height;

					if(Node -> LabelPlace == PLACE_BELOW && Node -> Label)
					{
						LONG Height = Node -> Height + INTERHEIGHT + Handle -> RPort . TxHeight;

						if(Height > MaxHeight)
							MaxHeight = Height;
					}
					else
					{
						if(Node -> Height > MaxHeight)
							MaxHeight = Node -> Height;
					}

					if(x + w > MaxWidth)
						MaxWidth = x+w;

					Node -> Left = x;

					if(!Group -> Special . Group . Paging)
						Left = x + w;
				}
			}

			SCANGROUP(Group,Node)
			{
				if(!LIKE_STRING_KIND(Node) || Node -> Special . String . LinkID == -1)
				{
					Node -> Top = MaxTop;

					if(Node -> Height < MaxHeight)
					{
						if(Node -> Type == LISTVIEW_KIND || Node -> Type == PALETTE_KIND)
							Node -> Height = MaxHeight;
						else
						{
							if(Node -> Type == GROUP_KIND)
							{
								if(Node -> Special . Group . Spread)
									LTK_Spread(Handle,Node,Node -> Width,MaxHeight);
								else
									Node -> Special . Group . ExtraTop += (MaxHeight - Node -> Height) / 2; /* !FIX! */

								Node -> Height = MaxHeight;
							}
						}
					}
				}
			}

			Group -> Special . Group . MaxOffset = MaxTop;
			Group -> Special . Group . MaxSize   = MaxHeight;

			if(Group -> Special . Group . Paging)
				Width = MaxWidth;
			else
				Width = Left;

			Height  = MaxTop + MaxHeight;
		}
		else
		{
			LONG x,y,w,MaxLeft = 0,MaxWidth = 0,MaxHeight = 0,Count = 0;

			if(Group -> Node . mln_Pred -> mln_Pred && Group -> Special . Group . LastAttributes)
			{
				ObjectNode *LastGroup = (ObjectNode *)Group -> Node . mln_Pred;

				MaxLeft		= LastGroup -> Special . Group . MaxOffset;
				MaxWidth	= LastGroup -> Special . Group . MaxSize;
			}

			SCANGROUP(Group,Node)
			{
				if(!LIKE_STRING_KIND(Node) || Node -> Special . String . LinkID == -1)
				{
					if(!Group -> Special . Group . Paging && Count++)
					{
						if(Node -> ExtraSpace > 0)
							y = Top + Node -> ExtraSpace * Handle -> InterHeight;
						else
						{
							if(Node -> ExtraSpace < 0)
								y = Top - Node -> ExtraSpace;
							else
								y = Top + Handle -> InterHeight;
						}
					}
					else
						y = Top;

					w = Node -> Width;
					x = Left;

					switch(Node -> LabelPlace)
					{
						case PLACE_LEFT:

							if(Node -> Type == MX_KIND)
							{
								x += Node -> Special . Radio . LabelWidth + INTERWIDTH;

								if(Node -> Label)
								{
									if(Node -> Special . Radio . TitlePlace == PLACETEXT_LEFT)
										x += Node -> LabelWidth + INTERWIDTH;
								}
							}
							else
							{
								if(Node -> Label || Node -> Type == BOX_KIND)
									x += Node -> LabelWidth + INTERWIDTH;
							}

							break;

						case PLACE_RIGHT:

							if(Node -> Type == MX_KIND)
							{
								w += INTERWIDTH + Node -> Special . Radio . LabelWidth;

								if(Node -> Label)
								{
									if(Node -> Special . Radio . TitlePlace == PLACETEXT_LEFT)
										x += Node -> LabelWidth + INTERWIDTH;
									else
										w += INTERWIDTH + Node -> LabelWidth;
								}
							}
							else
							{
								if(Node -> Label || Node -> Type == BOX_KIND)
									w += INTERWIDTH + Node -> LabelWidth;
							}

							break;

						case PLACE_ABOVE:

							if(Node -> Label || Node -> Type == BOX_KIND)
								y += Handle -> RPort . TxHeight + INTERHEIGHT;

							break;

						case PLACE_BELOW:

							break;

						case PLACE_IN:

							break;
					}

					if(Node -> Type == SLIDER_KIND && Node -> Special . Slider . LevelPlace == PLACETEXT_RIGHT)
						w += INTERWIDTH + Node -> Special . Slider . LevelWidth;

					if(Node -> Type == LISTVIEW_KIND && Node -> Special . List . ExtraLabelWidth)
						x += Node -> Special . List . ExtraLabelWidth + INTERWIDTH;

					if(x > MaxLeft)
						MaxLeft = x;

					if(w > MaxWidth)
						MaxWidth = w;

					if(y + Node -> Height > MaxHeight)
						MaxHeight = y + Node -> Height;

					Node -> Top = y;

					if(!Group -> Special . Group . Paging)
						Top = y + Node -> Height;

					if(Node -> LabelPlace == PLACE_BELOW && Node -> Label)
						Top += INTERHEIGHT + Handle -> RPort . TxHeight;
				}
			}

			SCANGROUP(Group,Node)
			{
				if(!LIKE_STRING_KIND(Node) || Node -> Special . String . LinkID == -1)
				{
					Node -> Left = MaxLeft;

					if(Node -> Type == GROUP_KIND && Group -> Special . Group . Paging)
						Node -> Special . Group . ExtraTop += (MaxHeight - Node -> Height) / 2;

					if(Node -> Width < MaxWidth)
					{
						if(Node -> Type == GROUP_KIND)
						{
							if(Node -> Special . Group . Spread)
								LTK_Spread(Handle,Node,MaxWidth,Node -> Height);
							else
								Node -> Special . Group . ExtraLeft += (MaxWidth - Node -> Width) / 2;

							Node -> Width = MaxWidth;
						}
						else
						{
							if(Node -> Type == LISTVIEW_KIND || Node -> Type == TEXT_KIND || Node -> Type == GAUGE_KIND || Node -> Type == NUMBER_KIND || Node -> Type == CYCLE_KIND || Node -> Type == PALETTE_KIND || LIKE_STRING_KIND(Node) || Node -> Type == INTEGER_KIND || Node -> Type == BUTTON_KIND || Node -> Type == SLIDER_KIND || Node -> Type == BOX_KIND || Node -> Type == SCROLLER_KIND)
							{
								if(Node -> Label && Node -> LabelPlace == PLACE_RIGHT)
									Node -> Width = MaxWidth - (INTERWIDTH + Node -> LabelWidth);
								else
									Node -> Width = MaxWidth;
							}
							else
							{
								if(Node -> Type == SLIDER_KIND)
								{
									if(Node -> Special . Slider . LevelPlace == PLACE_RIGHT)
									{
										if(Node -> Width + Node -> Special . Slider . LevelWidth + INTERWIDTH < MaxWidth)
											Node -> Width = MaxWidth - (Node -> Special . Slider . LevelWidth + INTERWIDTH);
									}
									else
										Node -> Width = MaxWidth;
								}
							}
						}
					}
				}
			}

			Group -> Special . Group . MaxOffset  = MaxLeft;
			Group -> Special . Group . MaxSize    = MaxWidth;

			if(Group -> Special . Group . Paging)
				Height = MaxHeight;
			else
				Height = Top;

			Width = MaxLeft + MaxWidth;
		}

		Group -> Left	= 0;
		Group -> Top	= 0;
		Group -> Width	= Width;
		Group -> Height	= Height;

		if(Group -> Label)
		{
			Group -> LabelWidth = LT_LabelWidth(Handle,Group -> Label) + 2 * Handle -> GlyphWidth;

			if(Group -> LabelWidth > Group -> Width)
			{
				Group -> Special . Group . ExtraLeft += (Group -> LabelWidth - Group -> Width) / 2;

				Group -> Width = Group -> LabelWidth;
			}

			Group -> Width	+= 2 * (Handle -> GlyphWidth + Handle -> InterWidth) + 8;
			Group -> Height	+= Handle -> RPort . TxHeight + 2 * Handle -> InterHeight + 3;
		}

		if(ReturnKey)
			Handle -> ReturnKey = ReturnKey;

		if(EscKey)
			Handle -> EscKey = EscKey;

		if(CursorKey)
			Handle -> CursorKey = CursorKey;
	}
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_PlaceGroups(LayoutHandle *handle,ObjectNode *group,LONG left,LONG top)
{
	if(!handle -> Failed)
	{
		ObjectNode	*node;
		LONG		 plusLeft,
				 plusTop,
				 lastSpace;

		if(group -> Label)
		{
			plusLeft	= 4 + handle -> GlyphWidth + handle -> InterWidth + group -> Special . Group . ExtraLeft;
			plusTop		= handle -> RPort . TxHeight + handle -> InterHeight + group -> Special . Group . ExtraTop;
		}
		else
		{
			plusLeft	= group -> Special . Group . ExtraLeft;
			plusTop		= group -> Special . Group . ExtraTop;
		}

		if(group -> ExtraSpace)
		{
			if(group -> Special . Group . ParentGroup -> Special . Group . Horizontal)
				left += handle -> InterWidth;
			else
				top += handle -> InterHeight;
		}

		group -> Left	= left;
		group -> Top	= top;

		left	+= plusLeft;
		top	+= plusTop;

		if(group -> Special . Group . Horizontal)
		{
			lastSpace = left;

			SCANGROUP(group,node)
			{
				if(node -> Type == GROUP_KIND)
					LTK_PlaceGroups(handle,node,lastSpace,top);
				else
				{
					node -> Left	+= left;
					node -> Top	+= top;

					handle -> Count++;
				}

				if(!group -> Special . Group . Paging)
				{
					lastSpace = node -> Left + node -> Width + handle -> InterWidth;

					if(node -> Type == MX_KIND)
					{
						if((node -> LabelPlace == PLACETEXT_RIGHT) || (node -> LabelPlace == PLACETEXT_LEFT))
							lastSpace += INTERWIDTH + node -> Special . Radio . LabelWidth;
					}
				}

				if(node -> Centered)
				{
					// !!! needs to be smarter about object height,dealing with labels,and the like

					node -> Top = group -> Top + ((group -> Height - node -> Height) / 2);
				}
			}
		}
		else
		{
			lastSpace = top;

			SCANGROUP(group,node)
			{
				if(node -> Type == GROUP_KIND)
					LTK_PlaceGroups(handle,node,left,lastSpace);
				else
				{
					node -> Left	+= left;
					node -> Top	+= top;

					handle -> Count++;
				}

				if(!group -> Special . Group . Paging)
					lastSpace = node -> Top + node -> Height + handle -> InterHeight;

				if(node -> Centered)
				{
					// !!! needs to be smarter about object widths,like for MX_KIND
					node -> Left = group -> Left + ((group -> Width - node -> Width) / 2);
				}
			}
		}
	}
}


/*****************************************************************************/


STATIC ObjectNode * __regargs
LTK_FindNode_Position(ObjectNode *group,LONG x,LONG y)
{
	ObjectNode *node;

	SCANGROUP(group,node)
	{
		if(node -> Type == GROUP_KIND)
		{
			ObjectNode *result;

			if(result = LTK_FindNode_Position(node,x,y))
				return(result);
		}
		else
		{
			if(node -> Left <= x && node -> Left + node -> Width > x && node -> Top <= y && node -> Top + node -> Height > y)
			{
				if(node -> Type == PICKER_KIND)
					return(node -> Special . Picker . Parent -> UserData);

				if(node -> Type == INCREMENTER_KIND)
					return(node -> Special . Incrementer . Parent -> UserData);

				return(node);
			}
		}
	}

	return(NULL);
}


/*****************************************************************************/


STATIC ObjectNode * __regargs
LTK_FindNode(ObjectNode *group,LONG id)
{
	ObjectNode *node;
	ObjectNode *result;

	SCANGROUP(group,node)
	{
		if(node -> Type == GROUP_KIND)
		{
			if(node -> ID == id)
				return(node);

			if(result = LTK_FindNode(node,id))
				return(result);
		}
		else
		{
			if((node -> ID == id) && (node -> Type != PICKER_KIND) && (node -> Type != INCREMENTER_KIND))
				return(node);
		}
	}

	return(NULL);
}


/*****************************************************************************/


STATIC struct Gadget * __regargs
LTK_FindGadget(LayoutHandle *handle,LONG id)
{
	if(handle && handle -> GadgetArray)
	{
		LONG i = handle -> Count;

		while(i--)
		{
			if(handle -> GadgetArray[i])
			{
				if(handle -> GadgetArray[i] -> GadgetID == id && handle -> GadgetArray[i] -> UserData)
				{
					register struct ObjectNode *Node = (struct ObjectNode *)handle -> GadgetArray[i] -> UserData;

					if(Node -> PointBack == Node && Node -> Host == handle -> GadgetArray[i])
						return(handle -> GadgetArray[i]);
				}
			}
		}
	}

	return(NULL);
}


/*****************************************************************************/


#ifdef DO_PICKSHORTCUTS
STATIC VOID __regargs
LTK_SearchKeys(LayoutHandle *handle,ObjectNode *group)
{
	ObjectNode	*node;
	ULONG		 page;

	SCANPAGE(group,node,page)
	{
		if(node -> Type == GROUP_KIND)
			LTK_SearchKeys(handle,node);
		else
		{
			if(node -> Key)
			{
				WORD i;

				for(i = 0 ; i < 256 ; i++)
				{
					if(LTK_Keys[0][i] == node -> Key || LTK_Keys[1][i] == node -> Key)
					{
						handle -> Keys[LTK_Keys[0][i]] = TRUE;
						handle -> Keys[LTK_Keys[1][i]] = TRUE;

						break;
					}
				}

				if(LTK_Keys[1][node -> Key])
					node -> Key = LTK_Keys[0][node -> Key];
			}
			else
			{
				if((handle -> CursorKey == node) || (handle -> ReturnKey == node) || (handle -> EscKey == node))
					node -> NoKey = TRUE;
			}
		}
	}
}
#endif


/*****************************************************************************/


#ifdef DO_PICKSHORTCUTS
STATIC VOID __regargs
LTK_SelectKeys(LayoutHandle *handle,ObjectNode *group)
{
	UBYTE		 bunch;
	ObjectNode	*node;
	ULONG		 page;

	SCANPAGE(group,node,page)
	{
		bunch = 0;

		switch(node -> Type)
		{
			case GROUP_KIND:

				LTK_SelectKeys(handle,node);
				break;

			case TEXT_KIND:

				if(!node -> Special . Text . UsePicker)
					break;

			case FRACTION_KIND:
			case PASSWORD_KIND:
			case STRING_KIND:

				if(node -> Special . String . UsePicker)
					bunch = 255;

			case BUTTON_KIND:
			case CHECKBOX_KIND:
			case INTEGER_KIND:

				bunch++;

			case LISTVIEW_KIND:
			case MX_KIND:
			case CYCLE_KIND:
			case PALETTE_KIND:
			case SCROLLER_KIND:
			case SLIDER_KIND:

				bunch++;

				if(node -> Label && !node -> Key && !node -> NoKey)
				{
					WORD	i,len,code,glyph;
					STRPTR	label;

					label	= node -> Label;
					len	= strlen(label);

					for(i = 0 ; i < len ; i++)
					{
						glyph	= ToLower(label[i]);
						code	= -1;

						if(bunch == 1)	// Support increment & decrement
						{
							if(LTK_Keys[0][glyph] && LTK_Keys[1][glyph])
								code = LTK_Keys[0][glyph];
						}
						else
						{
							if(bunch == 2)
							{
								if(LTK_Keys[0][glyph])
									code = LTK_Keys[0][glyph];
								else
								{
									if(LTK_Keys[1][glyph] && LTK_Keys[0][glyph])
									{
										if(ToLower(LTK_Keys[1][glyph]) == glyph)
											code = LTK_Keys[0][glyph];
									}
								}
							}
						}

						if(code != -1 && !handle -> Keys[code])
						{
							STRPTR newLabel;

							if(newLabel = LTK_Alloc(handle,len + 2))
							{
								node -> Label	= newLabel;
								node -> Key	= code;

								if(i)
									CopyMem(label,newLabel,i);

								newLabel[i] = '_';

								CopyMem(label + i,&newLabel[i + 1],len - i);

								handle -> Keys[code] = handle -> Keys[LTK_Keys[1][code]] = TRUE;

								break;
							}
						}
					}
				}

				break;
		}
	}
}
#endif


/*****************************************************************************/


STATIC ObjectNode * __regargs
LTK_CreateObjectNode(LayoutHandle *handle,UBYTE type,ULONG id,STRPTR label)
{
	ObjectNode	*node;
	ULONG		 size,extra;

	switch(type)
	{
#ifdef DO_GAUGE_KIND

		case GAUGE_KIND:

			extra = sizeof(GaugeExtra);
			break;
#endif

#ifdef DO_TAPEDECK_KIND
		case TAPEDECK_KIND:

			extra = sizeof(TapeDeckExtra);
			break;
#endif	/* DO_TAPEDECK_KIND */

		case BUTTON_KIND:

			extra = sizeof(ButtonExtra);
			break;

		case XBAR_KIND:
		case YBAR_KIND:

			extra = sizeof(BarExtra);
			break;

		case BOX_KIND:

			extra = sizeof(BoxExtra);
			break;

		case FRAME_KIND:

			extra = sizeof(FrameExtra);
			break;

		case PICKER_KIND:

			extra = sizeof(PickerExtra);
			break;

		case INCREMENTER_KIND:

			extra = sizeof(IncrementerExtra);
			break;

		case LISTVIEW_KIND:

			extra = sizeof(ListExtra);
			break;

		case MX_KIND:

			extra = sizeof(RadioExtra);
			break;

		case TEXT_KIND:

			extra = sizeof(TextExtra);
			break;

		case NUMBER_KIND:

			extra = sizeof(NumberExtra);
			break;

		case CYCLE_KIND:

			extra = sizeof(CycleExtra);
			break;

		case PALETTE_KIND:

			extra = sizeof(PalExtra);
			break;

		case SLIDER_KIND:

			extra = sizeof(SliderExtra);
			break;

		case SCROLLER_KIND:

			extra = sizeof(ScrollerExtra);
			break;

		case INTEGER_KIND:

			extra = sizeof(IntegerExtra);
			break;

		case FRACTION_KIND:
		case PASSWORD_KIND:
		case STRING_KIND:

			extra = sizeof(StringExtra);
			break;

		case HORIZONTAL_KIND:
		case VERTICAL_KIND:

			extra = sizeof(GroupExtra);
			break;

		default:

			extra = 0;
			break;
	}

	size = offsetof(ObjectNode,Special) + extra;

	if(node = LTK_Alloc(handle,size))
	{
		node -> Type		= type;
		node -> Label		= label;
		node -> ID		= id;
		node -> PointBack	= node;

		switch(type)
		{
			case LISTVIEW_KIND:

				node -> LabelPlace		= PLACE_ABOVE;
				node -> Lines			= 1;
				node -> Current			= ~0;
				node -> Special . List . LinkID	= -1;

				break;

			case PASSWORD_KIND:
			case STRING_KIND:

				node -> Special . String . LayoutHandle		= handle;
				node -> Special . String . LinkID		= -1;
				node -> Special . String . MaxChars		= 256;
				node -> Special . String . MaxHistoryLines	= 0;

				break;

			case INTEGER_KIND:

				node -> Special . Integer . LayoutHandle	= handle;
				node -> Special . Integer . Min			= -2147483647;
				node -> Special . Integer . Max			= 2147483647;
				node -> Special . Integer . MaxChars		= 11;
				node -> Special . Integer . MaxHistoryLines	= 0;

				break;

#ifdef DO_FRACTION_KIND
			case FRACTION_KIND:

				node -> Special . String . LayoutHandle		= handle;
				node -> Special . String . LinkID		= -1;
				node -> Special . String . Min			= 0;
				node -> Special . String . Max			= 2147483647;
				node -> Special . String . MaxChars		= 12;
				node -> Special . String . MaxHistoryLines	= 0;

				break;
#endif

#ifdef DO_TAPEDECK_KIND
			case TAPEDECK_KIND:

				node -> LabelPlace = PLACE_BELOW;
				break;
#endif	/* DO_TAPEDECK_KIND */

			case MX_KIND:

				node -> LabelPlace				= PLACE_RIGHT;
				node -> Special . Radio . TitlePlace		= PLACETEXT_LEFT;

				break;

			case SCROLLER_KIND:

				node -> Special . Scroller . Visible = 2;
				break;

			case SLIDER_KIND:

				node -> Special . Slider . LevelPlace = PLACETEXT_LEFT;
				break;

			case FRAME_KIND:

				node -> Special . Frame . DrawBox = TRUE;
				break;

			case BOX_KIND:

				node -> Special . Box . DrawBox = TRUE;
				break;

			case HORIZONTAL_KIND:

				node -> Type = GROUP_KIND;
				node -> Special . Group . Horizontal = TRUE;
				break;

			case VERTICAL_KIND:

				node -> Type = GROUP_KIND;
				node -> Special . Group . Horizontal = FALSE;
				break;
		}
	}

	return(node);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_DeleteObjectNode(LayoutHandle *handle,ObjectNode *node)
{
	struct Image **ImagePtr = NULL;

	if(node -> Type == PICKER_KIND)
	{
		if(node -> Special . Picker . Image)
			ImagePtr = &node -> Special . Picker . Image;
	}
	else
	{
		if(node -> Type == INCREMENTER_KIND)
			ImagePtr = &node -> Special . Incrementer . Image;
		else
		{
			if(node -> Type == TAPEDECK_KIND)
				ImagePtr = &node -> Special . TapeDeck . ButtonImage;
		}
	}

	if(ImagePtr)
	{
		if(*ImagePtr)
		{
			DisposeObject(*ImagePtr);

			*ImagePtr = NULL;
		}
	}
}


/*****************************************************************************/


STATIC struct Gadget * __regargs
LTK_CreatePicker(LayoutHandle *handle,ObjectNode *parentNode,struct Gadget *parentGadget,struct NewGadget *ng)
{
	ObjectNode	*node;
	struct Gadget	*picker;

	if(node = LTK_CreateObjectNode(handle,PICKER_KIND,parentGadget -> GadgetID,NULL))
	{
		ng -> ng_LeftEdge	= ng -> ng_LeftEdge + ng -> ng_Width;
		ng -> ng_Width		= (ng -> ng_Height * handle -> AspectY) / handle -> AspectX;
		ng -> ng_GadgetText	= "";
		ng -> ng_UserData	= node;
		ng -> ng_Flags		= NULL;

		node -> Special . Picker . Image = NewObject(LTK_ImageClass,NULL,
			IIA_ImageType,	IMAGECLASS_PICKER,
			IA_Width,	ng -> ng_Width,
			IA_Height,	ng -> ng_Height,
		TAG_DONE);

		if(node -> Special . Picker . Image)
		{
			if(picker = CreateGadgetA(GENERIC_KIND,parentGadget,ng,NULL))
			{
				picker -> GadgetType	|= GTYP_BOOLGADGET;
				picker -> Flags		|= GFLG_GADGIMAGE | GFLG_GADGHIMAGE;
				picker -> Activation	|= GACT_RELVERIFY;
				picker -> GadgetRender	 = node -> Special . Picker . Image;
				picker -> SelectRender	 = node -> Special . Picker . Image;

				if(parentNode -> Disabled)
					picker -> Flags |= GFLG_DISABLED;

				node -> Host = picker;

				node -> Special . Picker . Parent = parentGadget;

				return(picker);
			}
			else
		                LTK_DeleteObjectNode(handle,node);
		}
	}

	return(NULL);
}


/*****************************************************************************/


STATIC struct Gadget * __regargs
LTK_CreateIncrementer(LayoutHandle *handle,ObjectNode *parentNode,struct Gadget *parentGadget,struct NewGadget *ng,BOOLEAN leftDirection,LONG incAmount)
{
	ObjectNode	*node;
	struct Gadget	*incer;

	if(node = LTK_CreateObjectNode(handle,INCREMENTER_KIND,parentGadget -> GadgetID,NULL))
	{
		ng -> ng_LeftEdge	= ng -> ng_LeftEdge + ng -> ng_Width;
		ng -> ng_Width		= (((ng -> ng_Height * handle -> AspectY) / handle -> AspectX) * 4) / 5,
		ng -> ng_GadgetText	= "";
		ng -> ng_UserData	= node;
		ng -> ng_Flags		= NULL;

		node -> Special . Incrementer . Amount = incAmount;

		if(leftDirection)
		{
			node -> Special . Incrementer . Image = NewObject(LTK_ImageClass,NULL,
				IIA_ImageType,	IMAGECLASS_LEFTINCREMENTER,
				IA_Width,	ng -> ng_Width,
				IA_Height,	ng -> ng_Height,
			TAG_DONE);
		}
		else
		{
			node -> Special . Incrementer . Image = NewObject(LTK_ImageClass,NULL,
				IIA_ImageType,	IMAGECLASS_RIGHTINCREMENTER,
				IA_Width,	ng -> ng_Width,
				IA_Height,	ng -> ng_Height,
			TAG_DONE);
		}

		if(node -> Special . Incrementer . Image)
		{
			if(incer = CreateGadgetA(GENERIC_KIND,handle -> Previous,ng,NULL))
			{
				incer -> GadgetType	|= GTYP_BOOLGADGET;
				incer -> Flags		|= GFLG_GADGIMAGE | GFLG_GADGHIMAGE;
				incer -> Activation	|= GACT_IMMEDIATE | GACT_RELVERIFY;
				incer -> GadgetRender	 = node -> Special . Incrementer . Image;
				incer -> SelectRender	 = node -> Special . Incrementer . Image;

				if(parentNode -> Disabled)
					incer -> Flags |= GFLG_DISABLED;

				node -> Host = incer;

				node -> Special . Incrementer . Parent = parentGadget;

				return(incer);
			}
			else
				LTK_DeleteObjectNode(handle,node);
		}
	}

	return(NULL);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_LayoutGadgets(LayoutHandle *Handle,ObjectNode *Group)
{
	if(!Handle -> Failed)
	{
		STATIC struct TagItem NoUnderscoreVanillaTags[] =
		{
			{TAG_DONE}
		};

		STATIC struct TagItem VanillaTags[] =
		{
			{GT_Underscore,	'_'},
			{TAG_DONE}
		};

		STATIC struct TagItem DisabledTags[] =
		{
			{GT_Underscore,	'_' },
			{GA_Disabled,	TRUE},
			{TAG_DONE}
		};

		STATIC struct TagItem NoUnderscoreDisabledTags[] =
		{
			{GA_Disabled,	TRUE},
			{TAG_DONE}
		};

		struct NewGadget	 NewGadget;

		struct Gadget		*Gadget,
					*Extra;
		struct TagItem		*Tags;
		ULONG			 page;
		ObjectNode		*Node;

		if(!Handle -> List)
		{
			if(!(Handle -> Previous = CreateContext(&Handle -> List)))
			{
				Handle -> Failed = TRUE;

				return;
			}
		}

		NewGadget . ng_TextAttr   = Handle -> TextAttr;
		NewGadget . ng_VisualInfo = Handle -> VisualInfo;

		SCANPAGE(Group,Node,page)
		{
			if(Node -> Type != FRAME_KIND && Node -> Type != BOX_KIND && Node -> Type != XBAR_KIND && Node -> Type != YBAR_KIND)
			{
				Extra = NULL;

				if(Node -> GroupIndent)
				{
					NewGadget . ng_LeftEdge	= Node -> Left + 4;
					NewGadget . ng_TopEdge	= Node -> Top + 2;
					NewGadget . ng_Width	= Node -> Width - (4 + 4);
					NewGadget . ng_Height	= Node -> Height - (2 + 2);
				}
				else
				{
					NewGadget . ng_LeftEdge	= Node -> Left;
					NewGadget . ng_TopEdge	= Node -> Top;
					NewGadget . ng_Width	= Node -> Width;
					NewGadget . ng_Height	= Node -> Height;
				}

				NewGadget . ng_GadgetText	= Node -> Label ? Node -> Label : (STRPTR)"";
				NewGadget . ng_GadgetID		= Node -> ID;
				NewGadget . ng_UserData		= Node;

				if(Node -> NoKey)
				{
					if(Node -> Disabled)
					{
						if(!V39 && (Node -> Type == MX_KIND || Node -> Type == LISTVIEW_KIND))
							Tags = NoUnderscoreVanillaTags;
						else
							Tags = NoUnderscoreDisabledTags;
					}
					else
						Tags = NoUnderscoreVanillaTags;
				}
				else
				{
					if(Node -> Disabled)
					{
						if(!V39 && (Node -> Type == MX_KIND || Node -> Type == LISTVIEW_KIND))
							Tags = VanillaTags;
						else
							Tags = DisabledTags;
					}
					else
						Tags = VanillaTags;
				}

				switch(Node -> LabelPlace)
				{
					case PLACE_LEFT:

						NewGadget . ng_Flags = PLACETEXT_LEFT;
						break;

					case PLACE_RIGHT:

						NewGadget . ng_Flags = PLACETEXT_RIGHT;
						break;

					case PLACE_ABOVE:

						NewGadget . ng_Flags = PLACETEXT_ABOVE;
						break;

					case PLACE_BELOW:

						NewGadget . ng_Flags = PLACETEXT_BELOW;
						break;

					case PLACE_IN:

						NewGadget . ng_Flags = PLACETEXT_IN;
						break;

					default:

						NewGadget . ng_Flags = NULL;
						break;
				}

				if(Node -> HighLabel)
					NewGadget . ng_Flags |= NG_HIGHLABEL;

				switch(Node -> Type)
				{
					case BUTTON_KIND:

						Gadget = CreateGadgetA(BUTTON_KIND,Handle -> Previous,&NewGadget,Tags);

						break;

					case GROUP_KIND:

						LTK_LayoutGadgets(Handle,Node);

						continue;

					case CHECKBOX_KIND:

						Gadget = CreateGadget(CHECKBOX_KIND,Handle -> Previous,&NewGadget,
							GTCB_Checked,	Node -> Current,
							GTCB_Scaled,	TRUE,
						TAG_MORE,Tags);

						break;

					case LISTVIEW_KIND:

						if(!Node -> Special . List . ReadOnly && Node -> Special . List . LinkID != -1 && Node -> Special . List . LinkID != NIL_LINK)
							Gadget = Node -> Special . List . Link = LTK_FindGadget(Handle,Node -> Special . List . LinkID);
						else
							Gadget = NULL;

						Gadget = CreateGadget(LISTVIEW_KIND,Handle -> Previous,&NewGadget,
							GTLV_Selected,		Node -> Current,
							GTLV_ReadOnly,		Node -> Special . List . ReadOnly,
							GTLV_ScrollWidth,	2 + 2 * Handle -> GlyphWidth + 2,

							Node -> Current >= 0 ?			GTLV_Top:		TAG_IGNORE,Node -> Current,
							Node -> Current >= 0 ?			GTLV_MakeVisible:	TAG_IGNORE,Node -> Current,
							Node -> Special . List . Labels	?	GTLV_Labels:		TAG_IGNORE,Node -> Special . List . Labels,
							Node -> Special . List . LinkID != -1 ?	GTLV_ShowSelected:	TAG_IGNORE,Gadget,
							Node -> Special . List . CallBack ?	GTLV_CallBack:		TAG_IGNORE,Node -> Special . List . CallBack,
							Node -> Special . List . MaxPen ?	GTLV_MaxPen:		TAG_IGNORE,Node -> Special . List . MaxPen,
						TAG_MORE,Tags);

						break;

					case MX_KIND:

						NewGadget . ng_Height = Handle -> RPort . TxHeight;

						Gadget = CreateGadget(MX_KIND,Handle -> Previous,&NewGadget,
							GTMX_Labels,	Node -> Special . Radio . Choices,
							GTMX_Active,	Node -> Current,
							GTMX_Spacing,	Handle -> InterHeight,
							GTMX_Scaled,	TRUE,

							Node -> Label ? GTMX_TitlePlace : TAG_IGNORE,  Node -> Special . Radio . TitlePlace,
						TAG_MORE,Tags);

						break;

					case TEXT_KIND:

						if(Node -> Special . Text . UsePicker)
							NewGadget . ng_Width -= (NewGadget . ng_Height * Handle -> AspectY) / Handle -> AspectX;

						if(Gadget = CreateGadget(TEXT_KIND,Handle -> Previous,&NewGadget,
							GTTX_Border,		Node -> Special . Text . Border,
							GTTX_Justification,	Node -> Special . Text . Justification,

							Node -> Special . Text . Text ? GTTX_Text : TAG_IGNORE,Node -> Special . Text . Text,
						TAG_MORE,Tags))
						{
							if(Node -> Special . Text . UsePicker)
							{
								Extra = LTK_CreatePicker(Handle,Node,Gadget,&NewGadget);

								if(!Extra)
									Gadget = NULL;

								Node -> Special . Text . Picker = Extra;
							}
						}

						break;
#ifdef DO_GAUGE_KIND
					case GAUGE_KIND:

						NewGadget . ng_Height -= Handle -> InterHeight + Handle -> RPort . TxHeight + 2;

						Gadget = CreateGadgetA(TEXT_KIND,Handle -> Previous,&NewGadget,NULL);

						break;
#endif

#ifdef DO_TAPEDECK_KIND
					case TAPEDECK_KIND:

						if(Node -> Special . TapeDeck . ButtonType >= TDBT_BACKWARD && Node -> Special . TapeDeck . ButtonType < TDBTLAST)
						{
							LONG	ImageType;
							UWORD	Activation,
								Flags;

							if(Node -> Special . TapeDeck . Toggle)
								Activation = GACT_TOGGLESELECT | GACT_IMMEDIATE;
							else
								Activation = GACT_RELVERIFY;

							if(Node -> Current)
								Flags = GFLG_GADGIMAGE | GFLG_GADGHIMAGE | GFLG_SELECTED;
							else
								Flags = GFLG_GADGIMAGE | GFLG_GADGHIMAGE;

							switch(Node -> Special . TapeDeck . ButtonType)
							{
								case TDBT_BACKWARD:

									ImageType = IMAGECLASS_BACKWARD;
									break;

								case TDBT_FORWARD:

									ImageType = IMAGECLASS_FORWARD;
									break;

								case TDBT_PREVIOUS:

									ImageType = IMAGECLASS_PREVIOUS;
									break;

								case TDBT_NEXT:

									ImageType = IMAGECLASS_NEXT;
									break;

								case TDBT_RECORD:

									ImageType = IMAGECLASS_RECORD;
									break;

								case TDBT_PLAY:

									ImageType = IMAGECLASS_PLAY;
									break;

								case TDBT_STOP:

									ImageType = IMAGECLASS_STOP;
									break;

								case TDBT_PAUSE:

									ImageType = IMAGECLASS_PAUSE;
									break;

								case TDBT_REWIND:

									ImageType = IMAGECLASS_REWIND;
									break;

								case TDBT_EJECT:

									ImageType = IMAGECLASS_EJECT;
									break;
							}

							if(Node -> Special . TapeDeck . ButtonImage = NewObject(LTK_ImageClass,NULL,
								IIA_ImageType,	ImageType,
								IIA_GlyphWidth,	Node -> Special . TapeDeck . ButtonWidth,
								IIA_GlyphHeight,Node -> Special . TapeDeck . ButtonHeight,
								IA_Width,	NewGadget . ng_Width,
								IA_Height,	NewGadget . ng_Height,
							TAG_DONE))
							{
								NewGadget . ng_GadgetText	= "";
								NewGadget . ng_Flags		= NULL;

								if(Gadget = CreateGadgetA(GENERIC_KIND,Handle -> Previous,&NewGadget,NULL))
								{
									Gadget -> GadgetType	|= GTYP_BOOLGADGET;
									Gadget -> Flags		|= Flags;
									Gadget -> Activation	|= Activation;

									Gadget -> GadgetRender	 = Node -> Special . TapeDeck . ButtonImage;
									Gadget -> SelectRender	 = Node -> Special . TapeDeck . ButtonImage;

									if(Node -> Disabled)
										Gadget -> Flags |= GFLG_DISABLED;
								}
							}
						}

						break;
#endif	/* DO_TAPEDECK_KIND */
					case NUMBER_KIND:

						Gadget = CreateGadget(NUMBER_KIND,Handle -> Previous,&NewGadget,
							GTNM_Number,		Node -> Special . Number . Number,
							GTNM_Border,		Node -> Special . Number . Border,
							GTNM_Justification,	Node -> Special . Number . Justification,
							GTNM_Border,		Node -> Special . Number . Border,
							GTNM_Justification,	Node -> Special . Number . Justification,
						TAG_MORE,Tags);

						break;

					case CYCLE_KIND:

						Gadget = CreateGadget(CYCLE_KIND,Handle -> Previous,&NewGadget,
							GTCY_Labels,		Node -> Special . Cycle . Choices,
							GTCY_Active,		Node -> Current,
						TAG_MORE,Tags);

						break;

					case PALETTE_KIND:

						Gadget = CreateGadget(PALETTE_KIND,Handle -> Previous,&NewGadget,
							GTPA_Depth,		Node -> Special . Palette . Depth,
							GTPA_Color,		Node -> Current,
							GTPA_ColorOffset,	Node -> Min,
							GTPA_IndicatorWidth,	MAX(10,Node -> Width / (Node -> Max - Node -> Min + 1)),
							GTPA_IndicatorHeight,	Node -> Height,

							Node -> Special . Palette . ColourTable ? GTPA_ColorTable : TAG_IGNORE,Node -> Special . Palette . ColourTable,
						TAG_MORE,Tags);

						break;

					case SLIDER_KIND:

						Gadget = CreateGadget(SLIDER_KIND,Handle -> Previous,&NewGadget,
							GTSL_Min,		Node -> Min,
							GTSL_Max,		Node -> Max,
							GTSL_Level,		Node -> Current,
							GTSL_MaxLevelLen,	Node -> Special . Slider . MaxLevelLen,
							GTSL_LevelPlace,	Node -> Special . Slider . LevelPlace,
							GTSL_LevelFormat,	Node -> Special . Slider . LevelFormat ? Node -> Special . Slider . LevelFormat : (STRPTR)"%ld",
							GA_RelVerify,		TRUE,

							V40 ? GTSL_MaxPixelLen : TAG_IGNORE,	Node -> Special . Slider . LevelWidth,
							V40 ? GTSL_Justification : TAG_IGNORE,	GTJ_RIGHT,
							Node -> Special . Slider . DispFunc ? GTSL_DispFunc : TAG_IGNORE,Node -> Special . Slider . DispFunc,
						TAG_MORE,Tags);

						break;

					case SCROLLER_KIND:

						Gadget = CreateGadget(SCROLLER_KIND,Handle -> Previous,&NewGadget,
							GA_RelVerify,	Node -> Special . Scroller . RelVerify,
							GA_Immediate,	Node -> Special . Scroller . Immediate,
							GTSC_Top,	Node -> Current,
							GTSC_Total,	Node -> Max,
							GTSC_Visible,	Node -> Special . Scroller . Visible,
							PGA_Freedom,	Node -> Special . Scroller . Vertical ? LORIENT_VERT : LORIENT_HORIZ,

							Node -> Special . Scroller . Arrows ? GTSC_Arrows : TAG_IGNORE,Node -> Special . Scroller . ArrowSize,
						TAG_MORE,Tags);

						break;

					case STRING_KIND:

						if(Node -> Special . String . LinkID != -1)
						{
							ObjectNode *ListView = LTK_FindNode(Handle -> TopGroup,Node -> Special . String . LinkID);

							if(ListView)
							{
								NewGadget . ng_LeftEdge = ListView -> Left;
								NewGadget . ng_TopEdge  = ListView -> Top + ListView -> Height;
								NewGadget . ng_Width    = ListView -> Width;
							}
						}

						if(Node -> Special . String . UsePicker)
							NewGadget . ng_Width -= (NewGadget . ng_Height * Handle -> AspectY) / Handle -> AspectX;

						if(Gadget = CreateGadget(STRING_KIND,Handle -> Previous,&NewGadget,
							GTST_String,		Node -> Special . String . String,
							GTST_MaxChars,		Node -> Special . String . MaxChars,
							GTST_EditHook,		Node -> Special . String . EditHook ? Node -> Special . String . EditHook : Handle -> StandardEditHook,
							GA_Immediate,		TRUE,
							STRINGA_Justification,	Node -> Special . String . Justification,
						TAG_MORE,Tags))
						{
							if(!V39)
								Gadget -> Activation |= GACT_IMMEDIATE;

							if(Node -> Special . String . UsePicker)
							{
								Extra = LTK_CreatePicker(Handle,Node,Gadget,&NewGadget);

								if(!Extra)
									Gadget = NULL;

								Node -> Special . String . Picker = Extra;
							}
						}

						break;
#ifdef DO_FRACTION_KIND
					case FRACTION_KIND:

						if(Node -> Special . String . LinkID != -1)
						{
							ObjectNode *ListView = LTK_FindNode(Handle -> TopGroup,Node -> Special . String . LinkID);

							if(ListView)
							{
								NewGadget . ng_LeftEdge = ListView -> Left;
								NewGadget . ng_TopEdge    = ListView -> Top + ListView -> Height;
								NewGadget . ng_Width      = ListView -> Width;
							}
						}

						/* ALWAYS */
						{
							UBYTE LocalBuffer[20];

							sprintf(LocalBuffer,"%ld%lc%ld",Node -> Current / FIXED_UNITY,LTK_Locale ? LTK_Locale -> loc_DecimalPoint[0] : ' . ',Node -> Current % FIXED_UNITY);

							if(Gadget = CreateGadget(STRING_KIND,Handle -> Previous,&NewGadget,
								GTST_String,		LocalBuffer,
								GTST_MaxChars,		Node -> Special . String . MaxChars > 12 ? 12 : Node -> Special . String . MaxChars,
								GTST_EditHook,		&Handle -> FracEditHook,
								GA_Immediate,		TRUE,
								STRINGA_Justification,	Node -> Special . String . Justification,
							TAG_MORE,Tags))
							{
								if(!V39)
									Gadget -> Activation |= GACT_IMMEDIATE;
							}
						}

						break;
#endif

#ifdef DO_PASSWORD_KIND
					case PASSWORD_KIND:
					{
						WORD Len;

						if(Len = strlen(Node -> Special . String . Original))
							memset(Node -> Special . String . Original,'·',Len);

						if(Gadget = CreateGadget(STRING_KIND,Handle -> Previous,&NewGadget,
							GTST_String,		Node -> Special . String . Original,
							GTST_MaxChars,		Node -> Special . String . MaxChars,
							GTST_EditHook,		&Handle -> PasswordEditHook,
							GA_Immediate,		TRUE,
							STRINGA_Justification,	Node -> Special . String . Justification,
						TAG_MORE,Tags))
						{
							if(!V39)
								Gadget -> Activation |= GACT_IMMEDIATE;
						}

						if(Len)
							strcpy(Node -> Special . String . Original,Node -> Special . String . RealString);

						break;
					}
#endif

					case INTEGER_KIND:

						if(Node -> Special . Integer . UseIncrementers)
						{
							NewGadget . ng_Width -= (((NewGadget . ng_Height * Handle -> AspectY) / Handle -> AspectX) * 4) / 5;
							NewGadget . ng_Width -= (((NewGadget . ng_Height * Handle -> AspectY) / Handle -> AspectX) * 4) / 5;

							if(Node -> Special . Integer . IncrementerHook)
							{
								LONG Value;

								Value = (LONG)CallHookPkt(Node -> Special . Integer . IncrementerHook,(APTR)Node -> Special . Integer . Number,(APTR)INCREMENTERMSG_INITIAL);

								if(Value < Node -> Special . Integer . Min)
									Value = Node -> Special . Integer . Min;
								else
								{
									if(Value > Node -> Special . Integer . Max)
										Value = Node -> Special . Integer . Max;
								}

								Node -> Special . Integer . Number = Value;
							}
						}

#ifndef DO_HEXHOOK
						Gadget = CreateGadget(INTEGER_KIND,Handle -> Previous,&NewGadget,
							GTIN_Number,		Node -> Special . Integer . Number,
							GTIN_MaxChars,		Node -> Special . Integer . MaxChars,
							GTIN_EditHook,		Node -> Special . Integer . EditHook ? Node -> Special . Integer . EditHook : Handle -> StandardEditHook,
							GA_Immediate,		TRUE,
							STRINGA_Justification,	Node -> Special . Integer . Justification,
						TAG_MORE,Tags);
#else
						if(Node -> Special . Integer . EditHook)
						{
							Gadget = CreateGadget(INTEGER_KIND,Handle -> Previous,&NewGadget,
								GTIN_Number,		Node -> Special . Integer . Number,
								GTIN_MaxChars,		Node -> Special . Integer . MaxChars,
								GTIN_EditHook,		Node -> Special . Integer . EditHook,
								GA_Immediate,		TRUE,
								STRINGA_Justification,	Node -> Special . Integer . Justification,
							TAG_MORE,Tags);
						}
						else
						{
							UBYTE LocalBuffer[20];

							sprintf(LocalBuffer,"%ld",Node -> Special . Integer . Number);

							Gadget = CreateGadget(STRING_KIND,Handle -> Previous,&NewGadget,
								GTST_String,		LocalBuffer,
								GTST_MaxChars,		Node -> Special . Integer . MaxChars,
								GTST_EditHook,		&Handle -> HexEditHook,
								GA_Immediate,		TRUE,
								STRINGA_Justification,	Node -> Special . Integer . Justification,
							TAG_MORE,Tags);
						}

						if(Gadget)
						{
							struct StringInfo *StringInfo = (struct StringInfo *)Gadget -> SpecialInfo;

							StringInfo -> LongInt = Node -> Special . Integer . Number;
						}
#endif

						if(Gadget)
						{
							if(!V39)
								Gadget -> Activation |= GACT_IMMEDIATE;

							if(Node -> Special . Integer . UseIncrementers)
							{
								Handle -> Previous = Gadget;

								if(Node -> Special . Integer . LeftIncrementer = LTK_CreateIncrementer(Handle,Node,Gadget,&NewGadget,TRUE,-1))
								{
									Handle -> Previous = Node -> Special . Integer . LeftIncrementer;

									if(Node -> Special . Integer . RightIncrementer = LTK_CreateIncrementer(Handle,Node,Gadget,&NewGadget,FALSE,1))
										Extra = Node -> Special . Integer . RightIncrementer;
									else
										Gadget = NULL;
								}
								else
									Gadget = NULL;
							}
						}

						break;
				}

				if(Gadget)
				{
					Handle -> GadgetArray[Handle -> Index++] = Gadget;

					if(Extra)
						Handle -> Previous = Extra;
					else
						Handle -> Previous = Gadget;

					Node -> Host = Gadget;
				}
				else
				{
					Handle -> Failed = TRUE;

					return;
				}
			}
		}
	}
}


/*****************************************************************************/


STATIC BOOLEAN __regargs
LTK_GlyphSetup(struct LayoutHandle *Handle,struct TextAttr *TextAttr)
{
	struct TextFont *Font;

	if(TextAttr)
	{
		Handle -> TextAttr = TextAttr;

		if(!(Font = OpenFont(Handle -> TextAttr)))
		{
			struct Library *DiskfontBase;

			if(DiskfontBase = OpenLibrary("diskfont.library",0))
			{
				Font = OpenDiskFont(Handle -> TextAttr);

				CloseLibrary(DiskfontBase);
			}
		}

		if(Font)
		{
			if(Handle -> CloseFont)
				CloseFont(Handle -> RPort . Font);
			else
				Handle -> CloseFont = TRUE;
		}
	}
	else
	{
		Handle -> TextAttr = Handle -> Screen -> Font;

		Font = Handle -> DrawInfo -> dri_Font;

		if(Handle -> CloseFont)
		{
			CloseFont(Handle -> RPort . Font);

			Handle -> CloseFont = FALSE;
		}
	}

	if(Font)
	{
		LONG	Count = 0;
		LONG	Total = 0;
		UBYTE	Glyph;
		UWORD	i;

		LTK_SetFont(Handle,Font);

		for(i = 32 ; i < 127 ; i++,Count++)
		{
			Glyph = i;

			Total += TextLength(&Handle -> RPort,&Glyph,1);
		}

		for(i = 160 ; i < 256 ; i++,Count++)
		{
			Glyph = i;

			Total += TextLength(&Handle -> RPort,&Glyph,1);
		}

		Handle -> GlyphWidth = Total / Count;

		if(Handle -> GlyphWidth <= 8)
			Handle -> InterWidth = 4;
		else
			Handle -> InterWidth = Handle -> GlyphWidth / 2;

		if(Handle -> RPort . TxHeight <= 8)
			Handle -> InterHeight = 2;
		else
			Handle -> InterHeight = Handle -> RPort . TxHeight / 4;

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


/*****************************************************************************/

#ifdef DO_CLONING
STATIC VOID __regargs
LTK_CloneScreen(struct LayoutHandle *Handle,LONG Width,LONG Height)
{
	struct ColorSpec	*ColourSpec;
	struct ColourRecord	*ColourRecord;
	struct CloneExtra	*Extra = Handle -> CloneExtra;
	LONG			 AllocationSize;
	UWORD			 i,j,*Pens = Handle -> CloneExtra -> ScreenPens;
	struct Screen		*Screen;

	Height += Handle -> Screen -> BarHeight + 1;

	if(Width < Extra -> MinWidth)
		Width = Extra -> MinWidth;

	if(Height < Extra -> MinHeight)
		Height = Extra -> MinHeight;

	if(V39)
	{
		if(!(ColourRecord = (struct ColourRecord *)AsmAllocPooled(Handle -> Pool,AllocationSize = sizeof(struct ColourRecord) + sizeof(struct ColourTriplet) * Extra -> TotalPens,SysBase)))
		{
			Handle -> Failed = TRUE;

			return;
		}
		else
		{
			memset(ColourRecord,0,AllocationSize);

			ColourRecord -> NumColours = Extra -> TotalPens;

			ColourSpec = NULL;

			for(i = 0 ; i < Extra -> TotalPens ; i++)
				GetRGB32(Handle -> Screen -> ViewPort . ColorMap,Extra -> Pens[i],1,(ULONG *)&ColourRecord -> Triplets[i]);
		}
	}
	else
	{
		if(!(ColourSpec = (struct ColorSpec *)AsmAllocPooled(Handle -> Pool,AllocationSize = sizeof(struct ColorSpec) * (Extra -> TotalPens + 1),SysBase)))
		{
			Handle -> Failed = TRUE;

			return;
		}
		else
		{
			UWORD RGB;

			ColourRecord = NULL;

			for(i = 0 ; i < Extra -> TotalPens ; i++)
			{
				RGB = GetRGB4(Handle -> Screen -> ViewPort . ColorMap,Extra -> Pens[i]);

				ColourSpec[i] . ColorIndex	= i;
				ColourSpec[i] . Red		= (RGB >> 8) & 0xF;
				ColourSpec[i] . Green		= (RGB >> 4) & 0xF;
				ColourSpec[i] . Blue		= (RGB     ) & 0xF;
			}

			ColourSpec[i] . ColorIndex = -1;
		}
	}

	if(Handle -> ExactClone)
	{
		for(i = 0 ; i < Handle -> DrawInfo -> dri_NumPens ; i++)
			Pens[i] = Extra -> Pens[i];
	}
	else
	{
		for(i = 0 ; i < Handle -> DrawInfo -> dri_NumPens ; i++)
		{
			for(j = 0 ; j < Extra -> TotalPens ; j++)
			{
				if(Extra -> Pens[j] == Handle -> DrawInfo -> dri_Pens[i])
				{
					Pens[i] = j;

					break;
				}
			}
		}
	}

	Pens[i] = (UWORD)~0;

	if(Screen = OpenScreenTags(NULL,
		SA_Width,	Width,
		SA_Height,	Height,
		SA_Overscan,	OSCAN_TEXT,
		SA_BlockPen,	Pens[SHADOWPEN],
		SA_DetailPen,	Pens[BACKGROUNDPEN],
		SA_AutoScroll,	TRUE,
		SA_DisplayID,	GetVPModeID(&Handle -> Screen -> ViewPort),
		SA_Behind,	TRUE,
		SA_BackFill,	&Handle -> BackfillHook,
		SA_Depth,	Extra -> Depth,
		SA_Colors,	ColourSpec,
		SA_Colors32,	ColourRecord,
		SA_Pens,	Pens,
		SA_Font,	Handle -> TextAttr,
		SA_Title,	Handle -> Screen -> DefaultTitle,
	TAG_DONE))
	{
		struct DrawInfo *DrawInfo;

		if(DrawInfo = GetScreenDrawInfo(Screen))
		{
			APTR VisualInfo;

			if(VisualInfo = GetVisualInfoA(Screen,NULL))
			{
				UnlockPubScreen(NULL,Handle -> PubScreen);

				FreeScreenDrawInfo(Handle -> Screen,Handle -> DrawInfo);

				FreeVisualInfo(Handle -> VisualInfo);

				Handle -> PubScreen	= NULL;
				Handle -> Screen	= Screen;
				Handle -> DrawInfo	= DrawInfo;
				Handle -> VisualInfo	= VisualInfo;
				Handle -> TextPen	= Handle -> DrawInfo -> dri_Pens[TEXTPEN];
				Handle -> BackgroundPen	= Handle -> DrawInfo -> dri_Pens[BACKGROUNDPEN];
				Handle -> ShinePen	= Handle -> DrawInfo -> dri_Pens[SHINEPEN];
				Handle -> ShadowPen	= Handle -> DrawInfo -> dri_Pens[SHADOWPEN];
				Handle -> AspectX	= Handle -> DrawInfo -> dri_Resolution . X;
				Handle -> AspectY	= Handle -> DrawInfo -> dri_Resolution . Y;
				Handle -> MaxPen	= Extra -> TotalPens - 1;

				Extra -> Screen = Screen;

				if(ColourRecord)
					AsmFreePooled(Handle -> Pool,ColourRecord,AllocationSize,SysBase);

				if(ColourSpec)
					AsmFreePooled(Handle -> Pool,ColourSpec,AllocationSize,SysBase);

				return;
			}

			FreeScreenDrawInfo(Screen,DrawInfo);
		}

		CloseScreen(Screen);
	}

	if(ColourRecord)
		AsmFreePooled(Handle -> Pool,ColourRecord,AllocationSize,SysBase);

	if(ColourSpec)
		AsmFreePooled(Handle -> Pool,ColourSpec,AllocationSize,SysBase);

	Handle -> Failed = TRUE;
}

STATIC BOOLEAN __regargs
LTK_PrepareCloning(struct LayoutHandle *Handle)
{
	if(Handle -> CloneExtra)
		return(TRUE);
	else
	{
		struct DimensionInfo DimensionInfo;

		if(GetDisplayInfoData(NULL,(APTR)&DimensionInfo,sizeof(struct DimensionInfo),DTAG_DIMS,GetVPModeID(&Handle -> Screen -> ViewPort)))
		{
			if(Handle -> CloneExtra = (struct CloneExtra *)AsmAllocPooled(Handle -> Pool,sizeof(struct CloneExtra) + (sizeof(LONG) + sizeof(UWORD)) * Handle -> DrawInfo -> dri_NumPens + sizeof(UWORD),SysBase))
			{
				UWORD	 i,j,TotalPens = 0;
				LONG	*Pens;
				BOOLEAN	 NotFound;

				Handle -> CloneExtra -> Pens		= (LONG *)(Handle -> CloneExtra + 1);
				Handle -> CloneExtra -> ScreenPens	= (UWORD *)(&Handle -> CloneExtra -> Pens[Handle -> DrawInfo -> dri_NumPens]);
				Handle -> CloneExtra -> MinWidth	= DimensionInfo . MinRasterWidth;
				Handle -> CloneExtra -> MinHeight	= DimensionInfo . MinRasterHeight;
				Handle -> CloneExtra -> MaxWidth	= DimensionInfo . MaxRasterWidth;
				Handle -> CloneExtra -> MaxHeight	= DimensionInfo . MaxRasterHeight;

				Handle -> CloneExtra -> Bounds . Left	= 0;
				Handle -> CloneExtra -> Bounds . Top	= 0;
				Handle -> CloneExtra -> Bounds . Width	= DimensionInfo . MaxRasterWidth;
				Handle -> CloneExtra -> Bounds . Height	= DimensionInfo . MaxRasterHeight;

				Pens = Handle -> CloneExtra -> Pens;

				if(Handle -> ExactClone)
				{
					for(i = 0 ; i < Handle -> DrawInfo -> dri_NumPens ; i++)
						Pens[i] = Handle -> DrawInfo -> dri_Pens[i];

					TotalPens = Handle -> DrawInfo -> dri_NumPens;

					Handle -> CloneExtra -> Depth = Handle -> DrawInfo -> dri_Depth;
				}
				else
				{
					for(i = 0 ; i < Handle -> DrawInfo -> dri_NumPens ; i++)
					{
						for(j = 0,NotFound = TRUE ; NotFound && j < TotalPens ; j++)
						{
							if(Pens[j] == Handle -> DrawInfo -> dri_Pens[i])
								NotFound = FALSE;
						}

						if(NotFound)
							Pens[TotalPens++] = Handle -> DrawInfo -> dri_Pens[i];
					}

					for(i = 0 ; i < DimensionInfo . MaxDepth ; i++)
					{
						if(TotalPens <= (1L << i))
						{
							Handle -> CloneExtra -> Depth = i;

							break;
						}
					}
				}

				if((Handle -> CloneExtra -> TotalPens = TotalPens) && Handle -> CloneExtra -> Depth)
				{
					if(LTK_GlyphSetup(Handle,Handle -> InitialTextAttr))
					{
						Handle -> Failed = Handle -> Rescaled = FALSE;

						LTK_ResetGroups(Handle -> TopGroup);

						if(!V39 && (Handle -> RPort . TxFlags & FPF_PROPORTIONAL))
						{
							LTK_Rescale(Handle,FALSE,FALSE);

							Handle -> Rescaled = FALSE;
						}

						return(TRUE);
					}
				}
				else
				{
					AsmFreePooled(Handle -> Pool,Handle -> CloneExtra,sizeof(struct CloneExtra) + sizeof(LONG) * Handle -> DrawInfo -> dri_NumPens,SysBase);

					Handle -> CloneExtra = NULL;
				}
			}
		}
	}

	return(FALSE);
}
#endif

/*****************************************************************************/


STATIC VOID __regargs
LTK_CreateGadgets(LayoutHandle *Handle,struct IBox *Bounds,LONG Left,LONG Top,LONG PlusWidth,LONG PlusHeight)
{
	if(!Handle -> Failed)
	{
		BOOLEAN Fits;
		BOOLEAN Cloned = FALSE;

		if(!V39 && (Handle -> RPort . TxFlags & FPF_PROPORTIONAL))
		{
			LTK_Rescale(Handle,FALSE,FALSE);

			Handle -> Rescaled = FALSE;
		}

		do
		{
			LTK_LayoutGroup(Handle,Handle -> TopGroup);
			LTK_PlaceGroups(Handle,Handle -> TopGroup,Left,Top);

			Fits = TRUE;

			if(Handle -> TopGroup -> Width + PlusWidth > Bounds -> Width)
			{
				if(Handle -> TopGroup -> Width + PlusWidth > Handle -> Screen -> Width)
				{
					Fits = FALSE;

					if(Handle -> TopGroup -> Height + PlusHeight > Bounds -> Height)
					{
						if(!LTK_Rescale(Handle,TRUE,TRUE))
							Handle -> Failed = TRUE;
					}
					else
					{
						if(!LTK_Rescale(Handle,TRUE,FALSE))
							Handle -> Failed = TRUE;
					}
				}
			}
			else
			{
				if(Handle -> TopGroup -> Height + PlusHeight > Bounds -> Height)
				{
					if(Handle -> TopGroup -> Height + PlusHeight > Handle -> Screen -> Height)
					{
						Fits = FALSE;

						if(!LTK_Rescale(Handle,FALSE,TRUE))
							Handle -> Failed = TRUE;
					}
				}
			}

#ifdef DO_CLONING
			if(Handle -> Failed && !Cloned && Handle -> CloningPermitted)
			{
				if(LTK_PrepareCloning(Handle))
					Bounds = &Handle -> CloneExtra -> Bounds;

				Cloned = TRUE;
			}
#endif

			if(Fits && !Handle -> Rescaled && !Cloned && !Handle -> Failed && Handle -> GrowView)
			{
				LONG	Width	= Handle -> TopGroup -> Width + PlusWidth,
					Height	= Handle -> TopGroup -> Height + PlusHeight;

				if(Bounds -> Width > Width || Bounds -> Height > Height)
				{
					ObjectNode	*View = Handle -> GrowView;
					LONG		 PlusChars,
							 PlusLines;

					if(View -> Special . List . MaxGrowX)
					{
						PlusChars = (Bounds -> Width - Width) / Handle -> GlyphWidth;

						if(PlusChars > 0 && View -> Chars + PlusChars > View -> Special . List . MaxGrowX)
							PlusChars = View -> Special . List . MaxGrowX - View -> Chars;

						if(PlusChars < 0)
							PlusChars = 0;
					}
					else
						PlusChars = 0;

					if(View -> Special . List . MaxGrowY)
					{
						PlusLines = (Bounds -> Height - Height) / Handle -> RPort . TxHeight;

						if(PlusLines > 0 && View -> Lines + PlusLines > View -> Special . List . MaxGrowY)
							PlusLines = View -> Special . List . MaxGrowY - View -> Lines;

						if(PlusLines < 0)
							PlusLines = 0;
					}
					else
						PlusLines = 0;

					if(PlusChars || PlusLines)
					{
						View -> Chars += PlusChars;
						View -> Lines += PlusLines;

						LTK_ResetGroups(Handle -> TopGroup);

						if(!V39 && (Handle -> RPort . TxFlags & FPF_PROPORTIONAL))
						{
							LTK_Rescale(Handle,FALSE,FALSE);

							Handle -> Rescaled = FALSE;
						}

						Fits = FALSE;
					}
				}

				Handle -> GrowView = NULL;
			}
		}
		while(!Fits && !Handle -> Failed);

#ifdef DO_CLONING
		if(!Handle -> Failed && Cloned)
			LTK_CloneScreen(Handle,Handle -> TopGroup -> Width + PlusWidth,Handle -> TopGroup -> Height + PlusHeight);
#endif

		if(!Handle -> Failed)
		{
			if(Handle -> GadgetArray = LTK_Alloc(Handle,sizeof(struct Gadget *) * Handle -> Count))
			{
#ifdef DO_PICKSHORTCUTS
				LTK_SearchKeys(Handle,Handle -> TopGroup);
				LTK_SelectKeys(Handle,Handle -> TopGroup);
#endif
				LTK_LayoutGadgets(Handle,Handle -> TopGroup);

				if(Handle -> Failed)
				{
					FreeGadgets(Handle -> List);

					Handle -> List = NULL;
				}
			}
		}
	}
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_PrintLabel(LayoutHandle *handle,ObjectNode *node,UWORD left,UWORD top)
{
	STRPTR	label;
	LONG	len;

	LTK_SetAPen(&handle -> RPort,handle -> TextPen);

	Move(&handle -> RPort,left - (node -> LabelWidth + INTERWIDTH),top + handle -> RPort . TxBaseline + 1);

	label = node -> Label;

	len = 0;

	while(label[len] && (label[len] != '_'))
		len++;

	LockLayerRom(handle -> RPort . Layer);

	if(len)
		Text(&handle -> RPort,label,len);

	if(label[len] == '_' && label[len + 1])
	{
		SetSoftStyle(&handle -> RPort,FSF_UNDERLINED,AskSoftStyle(&handle -> RPort));
		Text(&handle -> RPort,label + len + 1,1);
		SetSoftStyle(&handle -> RPort,FS_NORMAL,AskSoftStyle(&handle -> RPort));

		label += len + 2;

		if(len = strlen(label))
			Text(&handle -> RPort,label,len);
	}

	UnlockLayerRom(handle -> RPort . Layer);
}


/*****************************************************************************/

STATIC VOID __regargs
LTK_DrawGroup(LayoutHandle *Handle,ObjectNode *Group)
{
	ObjectNode	*Node;
	LONG		 i;
	ULONG		 page;

	if(Group -> Label)
		LTK_DrawLabel(Handle,Group);

	SCANPAGE(Group,Node,page)
	{
		switch(Node -> Type)
		{
			case BUTTON_KIND:

				if(Handle -> ReturnKey == Node)
					LTK_DrawBevelBox(Handle,Node);

				break;
#ifdef DO_GAUGE_KIND
			case GAUGE_KIND:
			{
				LONG Percent = Node -> Current;

				Node -> Current = 0;

				LTK_DrawGauge(Handle,Node,Percent,TRUE);

				break;
			}
#endif
			case GROUP_KIND:

				if(Node -> Label)
					LTK_DrawLabel(Handle,Node);

				LTK_DrawGroup(Handle,Node);

				break;

			case XBAR_KIND:
			{
				WORD Top = Node -> Top + ((Node -> Height + 1) / 2) - 1;

				LockLayerRom(Handle -> RPort . Layer);

				if(Node -> Special . Bar . FullSize)
				{
					LTK_SetAPen(&Handle -> RPort,Handle -> ShadowPen);
					Move(&Handle -> RPort,Handle -> Window -> BorderLeft,Top);
					Draw(&Handle -> RPort,Handle -> Window -> Width - (Handle -> Window -> BorderRight + 1),Top);

					LTK_SetAPen(&Handle -> RPort,Handle -> ShinePen);
					Move(&Handle -> RPort,Handle -> Window -> BorderLeft,Top + 1);
					Draw(&Handle -> RPort,Handle -> Window -> Width - (Handle -> Window -> BorderRight + 1),Top + 1);
				}
				else
				{
					if(Node -> Special . Bar . Parent -> Label)
					{
						LTK_SetAPen(&Handle -> RPort,Handle -> ShadowPen);
						Move(&Handle -> RPort,Node -> Special . Bar . Parent -> Left + 2 * Handle -> GlyphWidth,Top + 1);
						Draw(&Handle -> RPort,Node -> Special . Bar . Parent -> Left + 2 * Handle -> GlyphWidth,Top);
						Draw(&Handle -> RPort,Node -> Special . Bar . Parent -> Left + Node -> Special . Bar . Parent -> Width - (2 * Handle -> GlyphWidth + 1) - 1,Top);

						LTK_SetAPen(&Handle -> RPort,Handle -> ShinePen);
						Move(&Handle -> RPort,Node -> Special . Bar . Parent -> Left + 2 * Handle -> GlyphWidth + 1,Top + 1);
						Draw(&Handle -> RPort,Node -> Special . Bar . Parent -> Left + Node -> Special . Bar . Parent -> Width - (2 * Handle -> GlyphWidth + 1),Top + 1);
						Draw(&Handle -> RPort,Node -> Special . Bar . Parent -> Left + Node -> Special . Bar . Parent -> Width - (2 * Handle -> GlyphWidth + 1),Top);
					}
					else
					{
						LTK_SetAPen(&Handle -> RPort,Handle -> ShadowPen);
						Move(&Handle -> RPort,Node -> Special . Bar . Parent -> Left,Top + 1);
						Draw(&Handle -> RPort,Node -> Special . Bar . Parent -> Left,Top);
						Draw(&Handle -> RPort,Node -> Special . Bar . Parent -> Left + Node -> Special . Bar . Parent -> Width - 1,Top);

						LTK_SetAPen(&Handle -> RPort,Handle -> ShinePen);
						Move(&Handle -> RPort,Node -> Special . Bar . Parent -> Left + 1,Top + 1);
						Draw(&Handle -> RPort,Node -> Special . Bar . Parent -> Left + Node -> Special . Bar . Parent -> Width - 1,Top + 1);
						Draw(&Handle -> RPort,Node -> Special . Bar . Parent -> Left + Node -> Special . Bar . Parent -> Width - 1,Top);
					}
				}

				UnlockLayerRom(Handle -> RPort . Layer);

				break;
			}

			case YBAR_KIND:
			{
				WORD Left = Node -> Left + ((Node -> Width + 1) / 2) - 1;

				LockLayerRom(Handle -> RPort . Layer);

				if(Node -> Special . Bar . Parent -> Label)
				{
					LTK_SetAPen(&Handle -> RPort,Handle -> ShadowPen);
					Move(&Handle -> RPort,Left + 1,Node -> Special . Bar . Parent -> Top + Handle -> RPort . TxHeight + Handle -> InterHeight);
					Draw(&Handle -> RPort,Left,Node -> Special . Bar . Parent -> Top + Handle -> RPort . TxHeight + Handle -> InterHeight);
					Draw(&Handle -> RPort,Left,Node -> Special . Bar . Parent -> Top + Node -> Special . Bar . Parent -> Height - (Handle -> RPort . TxHeight + Handle -> InterHeight + 1) - 1);

					LTK_SetAPen(&Handle -> RPort,Handle -> ShinePen);
					Move(&Handle -> RPort,Left + 1,Node -> Special . Bar . Parent -> Top + Handle -> RPort . TxHeight + Handle -> InterHeight + 1);
					Draw(&Handle -> RPort,Left + 1,Node -> Special . Bar . Parent -> Top + Node -> Special . Bar . Parent -> Height - (Handle -> RPort . TxHeight + Handle -> InterHeight + 1));
					Draw(&Handle -> RPort,Left,Node -> Special . Bar . Parent -> Top + Node -> Special . Bar . Parent -> Height - (Handle -> RPort . TxHeight + Handle -> InterHeight + 1));
				}
				else
				{
					LTK_SetAPen(&Handle -> RPort,Handle -> ShadowPen);
					Move(&Handle -> RPort,Left + 1,Node -> Special . Bar . Parent -> Top);
					Draw(&Handle -> RPort,Left,Node -> Special . Bar . Parent -> Top);
					Draw(&Handle -> RPort,Left,Node -> Special . Bar . Parent -> Top + Node -> Special . Bar . Parent -> Height - 1 - 1);

					LTK_SetAPen(&Handle -> RPort,Handle -> ShinePen);
					Move(&Handle -> RPort,Left + 1,Node -> Special . Bar . Parent -> Top + 1);
					Draw(&Handle -> RPort,Left + 1,Node -> Special . Bar . Parent -> Top + Node -> Special . Bar . Parent -> Height - 1);
					Draw(&Handle -> RPort,Left,Node -> Special . Bar . Parent -> Top + Node -> Special . Bar . Parent -> Height - 1);
				}

				UnlockLayerRom(Handle -> RPort . Layer);

				break;
			}

			case FRAME_KIND:

				if(Node -> Special . Frame . DrawBox)
					LTK_DrawBevelBox(Handle,Node);

				// FALL THROUGH TO...

			case TAPEDECK_KIND:

				if(Node -> Label)
				{
					LTK_SetPens(&Handle -> RPort,Handle -> TextPen,Handle -> BackgroundPen,JAM2);

					switch(Node -> LabelPlace)
					{
						case PLACE_LEFT:

							LTK_PlaceText(Handle,Node -> Label,strlen(Node -> Label),Node -> Left - (Node -> LabelWidth + INTERWIDTH),Node -> Top + (Node -> Height - Handle -> RPort . TxHeight) / 2 + Handle -> RPort . TxBaseline);
							break;

						case PLACE_RIGHT:

							LTK_PlaceText(Handle,Node -> Label,strlen(Node -> Label),Node -> Left + Node -> Width + INTERWIDTH,Node -> Top + (Node -> Height - Handle -> RPort . TxHeight) / 2 + Handle -> RPort . TxBaseline);
							break;

						case PLACE_ABOVE:

							LTK_PlaceText(Handle,Node -> Label,strlen(Node -> Label),Node -> Left + (Node -> Width - Node -> LabelWidth) / 2,Node -> Top - (Handle -> RPort . TxHeight + INTERHEIGHT) + Handle -> RPort . TxBaseline);
							break;

						case PLACE_BELOW:

							LTK_PlaceText(Handle,Node -> Label,strlen(Node -> Label),Node -> Left + (Node -> Width - Node -> LabelWidth) / 2,Node -> Top + Node -> Height + INTERHEIGHT + Handle -> RPort . TxBaseline);
							break;
					}
				}

				break;

			case MX_KIND:

				if(Node -> Label && !V39)
					LTK_PrintLabel(Handle,Node,Node -> Left,Node -> Top + (Node -> Height - Handle -> RPort . TxHeight) / 2);

				break;

			case LISTVIEW_KIND:

				if(Node -> Special . List . ExtraLabels)
				{
					LockLayerRom(Handle -> RPort . Layer);

					LTK_SetPens(&Handle -> RPort,Handle -> TextPen,Handle -> BackgroundPen,JAM2);

					for(i = 0 ; Node -> Special . List . ExtraLabels[i] ; i++)
					{
						LONG Len = strlen(Node -> Special . List . ExtraLabels[i]);

						LTK_PlaceText(Handle,Node -> Special . List . ExtraLabels[i],Len,Node -> Left - (TextLength(&Handle -> RPort,Node -> Special . List . ExtraLabels[i],Len) + INTERWIDTH),Node -> Top + 2 + i * Handle -> RPort . TxHeight + Handle -> RPort . TxBaseline);
					}

					UnlockLayerRom(Handle -> RPort . Layer);
				}

				break;

			case BOX_KIND:

				if(Node -> Special . Box . DrawBox)
					LTK_DrawBevelBox(Handle,Node);

				LockLayerRom(Handle -> RPort . Layer);

				for(i = 0 ; i < Node -> Lines ; i++)
				{
					if(Node -> Special . Box . Labels && Node -> Special . Box . Labels[i])
					{
						LONG Len = strlen(Node -> Special . Box . Labels[i]);

						LTK_SetPens(&Handle -> RPort,Handle -> TextPen,Handle -> BackgroundPen,JAM2);
						LTK_PlaceText(Handle,Node -> Special . Box . Labels[i],Len,Node -> Left - (TextLength(&Handle -> RPort,Node -> Special . Box . Labels[i],Len) + INTERWIDTH),Node -> Top + 2 + i * Handle -> RPort . TxHeight + Handle -> RPort . TxBaseline);
					}

					LTK_PrintBoxLine(Handle,Node,i);
				}

				UnlockLayerRom(Handle -> RPort . Layer);

				break;
		}
	}
}


/*****************************************************************************/


STATIC LONG __stdargs
DefaultDispFunc(struct Gadget *gad,WORD value)
{
	return(value);
}


VOID LIBENT
LT_LevelWidth(REG(a0) LayoutHandle *handle,REG(a1) STRPTR levelFormat,REG(a2) DISPFUNC dispFunc,REG(d0) LONG min,REG(d1) LONG max,REG(a3) LONG *maxWidth,REG(a5) LONG *maxLen,REG(d2) BOOL fullCheck)
{
	UBYTE	buffer[80];
	LONG	localMaxWidth;
	LONG	localMaxLen;
	LONG	len;

	localMaxWidth	= 0;
	localMaxLen	= 0;

	if(handle)
	{
		if(!dispFunc)
			dispFunc = DefaultDispFunc;

		if(!levelFormat)
			levelFormat = "%ld";

		if(fullCheck)
		{
			LONG i;

			for(i = min ; i <= max ; i++)
			{
				sprintf(buffer,levelFormat,(*dispFunc)(NULL,i));

				len = strlen(buffer);

				if(len > localMaxLen)
					localMaxLen = len;

				len = TextLength(&handle -> RPort,buffer,len);

				if(len > localMaxWidth)
					localMaxWidth = len;
			}
		}
		else
		{
			sprintf(buffer,levelFormat,(*dispFunc)(NULL,min));

			len = strlen(buffer);

			if(len > localMaxLen)
				localMaxLen = len;

			len = TextLength(&handle -> RPort,buffer,len);

			if(len > localMaxWidth)
				localMaxWidth = len;

			sprintf(buffer,levelFormat,(*dispFunc)(NULL,max));

			len = strlen(buffer);

			if(len > localMaxLen)
				localMaxLen = len;

			len = TextLength(&handle -> RPort,buffer,len);

			if(len > localMaxWidth)
				localMaxWidth = len;
		}
	}

	if(maxLen)
		*maxLen = localMaxLen;

	if(maxWidth)
		*maxWidth = localMaxWidth + handle -> GlyphWidth;
}


/*****************************************************************************/


VOID LIBENT
LT_DeleteHandle(REG(a0) LayoutHandle *Handle)
{
	if(Handle)
	{
		UnlockPubScreen(NULL,Handle -> PubScreen);
#ifdef DO_CLONING
		if(Handle -> CloneExtra)
		{
			if(Handle -> CloneExtra -> Screen)
				ScreenToBack(Handle -> CloneExtra -> Screen);
		}
#endif
		if(Handle -> List)
		{
			struct Gadget	*Gadget;
			ObjectNode	*Node;

			if(Handle -> Window)
				RemoveGList(Handle -> Window,Handle -> List,(UWORD)-1);

			Gadget = Handle -> List;

			while(Gadget)
			{
				if(GETOBJECT(Gadget,Node))
					LTK_DeleteObjectNode(Handle,Node);

				Gadget = Gadget -> NextGadget;
			}

			FreeGadgets(Handle -> List);
		}

		if(Handle -> Parent)
			LT_UnlockWindow(Handle -> Parent);

		if(Handle -> Window)
		{
			LT_DeleteWindowLock(Handle -> Window);

			if(Handle -> MsgPort && Handle -> Window -> UserPort)
			{
				struct IntuiMessage	*intuiMsg;
				struct Node		*next;

				Forbid();

				intuiMsg = (struct IntuiMessage *)Handle -> Window -> UserPort -> mp_MsgList . lh_Head;

				while(next = intuiMsg -> ExecMessage . mn_Node . ln_Succ)
				{
					if(intuiMsg -> IDCMPWindow == Handle -> Window)
					{
						Remove(intuiMsg);

						ReplyMsg(intuiMsg);
					}

					intuiMsg = (struct IntuiMessage *)next;
				}

				Handle -> Window -> UserPort = NULL;

				ModifyIDCMP(Handle -> Window,NULL);

				Permit();
			}

			ClearMenuStrip(Handle -> Window);
			CloseWindow(Handle -> Window);
		}

		DisposeObject(Handle -> AmigaGlyph);
		DisposeObject(Handle -> CheckGlyph);

		FreeScreenDrawInfo(Handle -> Screen,Handle -> DrawInfo);

		FreeVisualInfo(Handle -> VisualInfo);

		if(Handle -> RPort . Font && Handle -> CloseFont)
			CloseFont(Handle -> RPort . Font);
#ifdef DO_CLONING
		if(Handle -> CloneExtra)
		{
			if(Handle -> CloneExtra -> Screen)
				CloseScreen(Handle -> CloneExtra -> Screen);
		}
#endif
		if(Handle -> Pool)
			AsmDeletePool(Handle -> Pool,SysBase);

		if(Handle -> TimeRequest . tr_node . io_Device)
			CloseDevice(&Handle -> TimeRequest);

		FreeMem(Handle,sizeof(LayoutHandle));
	}
}


/*****************************************************************************/

LayoutHandle * LIBENT
LT_CreateHandle(REG(a0) struct Screen *Screen,REG(a1) struct TextAttr *Font)
{
	return(LT_CreateHandleTags(Screen,
		LH_Font,	Font,
	TAG_DONE));
}

LayoutHandle * __stdargs
LT_CreateHandleTags(struct Screen *Screen,...)
{
	LayoutHandle	*Handle;
	va_list		 VarArgs;

	va_start(VarArgs,Screen);
	Handle = LT_CreateHandleTagList(Screen,(struct TagItem *)VarArgs);
	va_end(VarArgs);

	return(Handle);
}

LayoutHandle * LIBENT
LT_CreateHandleTagList(REG(a0) struct Screen *Screen,REG(a1) struct TagItem *TagList)
{
	LayoutHandle	*Handle;
	struct Screen	*PubScreen;
	struct TextAttr	*textAttr;

#ifdef DO_PICKSHORTCUTS
	ObtainSemaphore(&LTK_KeySemaphore);

	if(!LTK_KeysInitialized)
	{
		UBYTE             mapBuffer[2 * 3];
		UBYTE             remapBuffer[10];
		LONG              i;
		struct InputEvent event;

		for(i = 0 ; i < 256; i++)
		{
			if((i > ' ' && i < 127) || i > 160)
			{
				remapBuffer[0] = i;
				remapBuffer[1] = 0;

				if(MapANSI(remapBuffer,1,mapBuffer,3,NULL) == 1)
				{
					if(!(mapBuffer[1] & ~QUALIFIER_SHIFT))
					{
						event . ie_NextEvent		= NULL;
						event . ie_Class		= IECLASS_RAWKEY;
						event . ie_SubClass		= 0;
						event . ie_Code			= mapBuffer[0];
						event . ie_Qualifier		= mapBuffer[1] & ~QUALIFIER_SHIFT;
						event . ie_position . ie_addr	= NULL;

						if(MapRawKey(&event,remapBuffer,10,NULL) == 1)
							LTK_Keys[0][i] = remapBuffer[0];

						event . ie_NextEvent		= NULL;
						event . ie_Class		= IECLASS_RAWKEY;
						event . ie_SubClass		= 0;
						event . ie_Code			= mapBuffer[0];
						event . ie_Qualifier		= mapBuffer[1] | QUALIFIER_SHIFT;
						event . ie_position . ie_addr	= NULL;

						if(MapRawKey(&event,remapBuffer,10,NULL) == 1)
							LTK_Keys[1][i] = remapBuffer[0];
					}
				}
			}
		}

		LTK_KeysInitialized = TRUE;
	}

	ReleaseSemaphore(&LTK_KeySemaphore);
#endif 	/* DO_PICKSHORTCUTS */

	textAttr = (struct TextAttr *)GetTagData(LH_Font,NULL,TagList);

	if(!Screen)
	{
		if(!(PubScreen = LockPubScreen(NULL)))
			return(NULL);
		else
			Screen = PubScreen;
	}
	else
		PubScreen = NULL;

	if(Handle = AllocMem(sizeof(LayoutHandle),MEMF_ANY | MEMF_PUBLIC | MEMF_CLEAR))
	{
		Handle -> Screen	= Screen;
		Handle -> PubScreen	= PubScreen;
		Handle -> AutoActivate	= GetTagData(LH_AutoActivate,FALSE,TagList);
		Handle -> LocaleHook	= (struct Hook *)GetTagData(LH_LocaleHook,NULL,TagList);

		Handle -> Parent	= (struct Window *)GetTagData(LH_Parent,NULL,TagList);
		Handle -> BlockParent	= GetTagData(LH_BlockParent,FALSE,TagList);

#ifdef DO_CLONING
		Handle -> CloningPermitted	= GetTagData(LH_CloningPermitted,TRUE,TagList);
		Handle -> ExactClone		= GetTagData(LH_ExactClone,FALSE,TagList);
#endif

#ifdef DO_FRACTION_KIND
		Handle -> FracEditHook . h_Entry	= (HOOKFUNC)LTK_FracEditRoutine;
		Handle -> FracEditHook . h_Data		= Handle;
#endif

#ifdef DO_PASSWORD_KIND
		Handle -> PasswordEditHook . h_Entry	= (HOOKFUNC)LTK_PasswordEditRoutine;
		Handle -> PasswordEditHook . h_Data	= Handle;
#endif

		Handle -> DefaultEditHook . h_Entry	= (HOOKFUNC)LTK_DefaultEditRoutine;
		Handle -> DefaultEditHook . h_Data	= Handle;

		Handle -> BackfillHook . h_Entry	= (HOOKFUNC)LTK_BackfillRoutine;
		Handle -> BackfillHook . h_Data		= Handle;

		Handle -> StandardEditHook = (struct Hook *)GetTagData(LH_EditHook,(ULONG)&Handle -> DefaultEditHook,TagList);

#ifdef DO_HEXHOOK
		Handle -> HexEditHook . h_Entry		= (HOOKFUNC)LTK_HexEditRoutine;
		Handle -> HexEditHook . h_Data		= Handle;
#endif
		if(Handle -> Pool = AsmCreatePool(MEMF_PUBLIC | MEMF_ANY | MEMF_CLEAR,1024,1024,SysBase))
		{
			if(Handle -> DrawInfo = GetScreenDrawInfo(Screen))
			{
				LONG i;

				Handle -> TextPen	= Handle -> DrawInfo -> dri_Pens[TEXTPEN];
				Handle -> BackgroundPen	= Handle -> DrawInfo -> dri_Pens[BACKGROUNDPEN];
				Handle -> ShinePen	= Handle -> DrawInfo -> dri_Pens[SHINEPEN];
				Handle -> ShadowPen	= Handle -> DrawInfo -> dri_Pens[SHADOWPEN];
				Handle -> AspectX	= Handle -> DrawInfo -> dri_Resolution . X;
				Handle -> AspectY	= Handle -> DrawInfo -> dri_Resolution . Y;

				if(V39 && FindTagItem(LH_MenuGlyphs,TagList))
				{
					LONG Size,FontHeight = Handle -> DrawInfo -> dri_Font -> tf_Baseline + 2;

					if(Screen -> Flags & SCREENHIRES)
						Size = SYSISIZE_MEDRES;
					else
						Size = SYSISIZE_LOWRES;

					if(Handle -> AmigaGlyph = NewObject(NULL,SYSICLASS,
						SYSIA_DrawInfo,	Handle -> DrawInfo,
						SYSIA_Size,	Size,
						SYSIA_Which,	AMIGAKEY,
						IA_Left,	0,
						LA_Top,		0,
						IA_Width,	(FontHeight * 3 * Handle -> AspectY) / (2 * Handle -> AspectX),
						IA_Height,	FontHeight,
					TAG_DONE))
					{
						if(!(Handle -> CheckGlyph = NewObject(NULL,SYSICLASS,
							SYSIA_DrawInfo,	Handle -> DrawInfo,
							SYSIA_Size,	Size,
							SYSIA_Which,	MENUCHECK,
							IA_Left,	0,
							LA_Top,		0,
							IA_Width,	(FontHeight * Handle -> AspectY) / Handle -> AspectX,
							IA_Height,	FontHeight,
						TAG_DONE)))
						{
							DisposeObject(Handle -> AmigaGlyph);

							Handle -> AmigaGlyph = NULL;
						}
					}
				}

				for(i = 0 ; i < Handle -> DrawInfo -> dri_NumPens ; i++)
				{
					if(Handle -> DrawInfo -> dri_Pens[i] > Handle -> MaxPen)
						Handle -> MaxPen = Handle -> DrawInfo -> dri_Pens[i];
				}

				if(Handle -> VisualInfo = GetVisualInfoA(Screen,NULL))
				{
					if(textAttr)
						Handle -> InitialTextAttr = textAttr;

					InitRastPort(&Handle -> RPort);

					if(LTK_GlyphSetup(Handle,textAttr))
					{
#ifdef DO_PICKSHORTCUTS
						memset(Handle -> Keys,TRUE,256);

						for(i = 0 ; i < 256 ; i++)
						{
							if(i != 32 && i != 160)
							{
								if(LTK_Keys[0][i])
									Handle -> Keys[LTK_Keys[0][i]] = FALSE;

								if(LTK_Keys[1][i])
									Handle -> Keys[LTK_Keys[1][i]] = FALSE;
							}
						}
#endif
						Handle -> TimePort . mp_Flags	= PA_SIGNAL;
						Handle -> TimePort . mp_SigBit	= SIGB_SINGLE;
						Handle -> TimePort . mp_SigTask	= SysBase -> ThisTask;

						NewList((struct MinList *)&Handle -> TimePort . mp_MsgList);

						Handle -> TimeRequest . tr_node . io_Message . mn_ReplyPort	= &Handle -> TimePort;
						Handle -> TimeRequest . tr_node . io_Message . mn_Length	= sizeof(struct timerequest);

						OpenDevice(TIMERNAME,UNIT_VBLANK,&Handle -> TimeRequest,NULL);

						return(Handle);
					}
				}
			}
		}

		LT_DeleteHandle(Handle);
	}
	else
		UnlockPubScreen(NULL,PubScreen);

	return(NULL);
}


/*****************************************************************************/


STATIC VOID __regargs
LTK_AddGadgets(LayoutHandle *handle)
{
	if(handle)
	{
		AddGList(handle -> Window,handle -> List,(UWORD)-1,(UWORD)-1,NULL);
		RefreshGList(handle -> List,handle -> Window,NULL,(UWORD)-1);

		GT_RefreshWindow(handle -> Window,NULL);

		LTK_DrawGroup(handle,handle -> TopGroup);
	}
}


/*****************************************************************************/


BOOL LIBENT
LT_Rebuild(REG(a0) LayoutHandle *handle,REG(a1) struct IBox *bounds,REG(a2) LONG extraWidth,REG(d0) LONG extraHeight,REG(d1) BOOL clear)
{
	if(handle)
	{
		struct Gadget	*gadget;
		ObjectNode	*node;
		LONG		 left,top;
		struct IBox	 newBounds;
		LONG		 i;

		RemoveGList(handle -> Window,handle -> List,(UWORD)-1);

		gadget = handle -> List;

		while(gadget)
		{
			if(GETOBJECT(gadget,node))
			{
				node -> Host = NULL;

				switch(node -> Type)
				{
					case PICKER_KIND:

						if(node -> Special . Picker . Image)
						{
							DisposeObject(node -> Special . Picker . Image);

							node -> Special . Picker . Image = NULL;
						}

						break;

					case TAPEDECK_KIND:

						if(node -> Special . TapeDeck . ButtonImage)
						{
							DisposeObject(node -> Special . TapeDeck . ButtonImage);

							node -> Special . TapeDeck . ButtonImage = NULL;
						}

						break;

					case INCREMENTER_KIND:

						if(node -> Special . Incrementer . Image)
						{
							DisposeObject(node -> Special . Incrementer . Image);

							node -> Special . Incrementer . Image = NULL;
						}

						break;

					case STRING_KIND:

						node -> Special . String . Picker = NULL;

						break;

					case TEXT_KIND:

						node -> Special . Text . Picker = NULL;

						break;

					case INTEGER_KIND:

						node -> Special . Integer . LeftIncrementer = NULL;
						node -> Special . Integer . RightIncrementer = NULL;

						break;
				}
			}

			gadget = gadget -> NextGadget;
		}

		FreeGadgets(handle -> List);

		handle -> List = NULL;

		LTK_Free(handle,handle -> GadgetArray,sizeof(struct Gadget *) * handle -> Count);

		handle -> GadgetArray = NULL;

		handle -> Count = handle -> Index = 0;

		LTK_ResetGroups(handle -> TopGroup);

		if(!bounds)
		{
			LTK_GetDisplayClip(handle -> Screen,&newBounds . Left,&newBounds . Top,&newBounds . Width,&newBounds . Height);

			bounds = &newBounds;
		}

		left	= handle -> Window -> BorderLeft + handle -> InterWidth;
		top	= handle -> Window -> BorderTop + handle -> InterHeight;

		LTK_CreateGadgets(handle,bounds,left,top,left + handle -> InterWidth + handle -> Screen -> WBorRight,top + handle -> InterHeight + handle -> Screen -> WBorBottom);

		if(handle -> Failed)
			return(FALSE);

		if(clear)
		{
			LTK_SetPens(&handle -> RPort,handle -> BackgroundPen,0,JAM1);

			RectFill(&handle -> RPort,handle -> Window -> BorderLeft,handle -> Window -> BorderTop,handle -> Window -> Width - (handle -> Window -> BorderRight + 1),handle -> Window -> Height - (handle -> Window -> BorderBottom + 1));
		}

		LTK_AddGadgets(handle);

		handle -> Previous = NULL;

		for(i = 0 ; i < handle -> Count ; i++)
		{
			if(handle -> GadgetArray[i])
			{
				if(GETOBJECT(handle -> GadgetArray[i],node))
				{
					if(LIKE_STRING_KIND(node) || (node -> Type == INTEGER_KIND))
					{
						if(!handle -> Previous)
							handle -> Previous = handle -> GadgetArray[i];
					}
				}
			}
		}

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


/*****************************************************************************/


VOID LIBENT
LT_HandleInput(REG(a0) LayoutHandle *Handle,REG(d0) ULONG MsgQualifier,REG(a1) ULONG *MsgClass,REG(a2) UWORD *MsgCode,REG(a3) struct Gadget **MsgGadget)
{
	ObjectNode	*Node;
	BOOLEAN		 Activate = FALSE;

	if(!Handle)
		return;

	switch(*MsgClass)
	{
		case IDCMP_REFRESHWINDOW:

			if(Handle -> AutoRefresh)
			{
				LT_BeginRefresh(Handle);

				LT_EndRefresh(Handle,TRUE);

				*MsgClass = NULL;
			}

			break;

		case IDCMP_INTUITICKS:

			if(Handle -> ActiveIncrementer)
			{
				Handle -> IncrementerCountdown--;

				if(Handle -> IncrementerCountdown <= 0)
				{
					if(Handle -> ActiveIncrementer -> Host -> Flags & GFLG_SELECTED)
					{
						struct ObjectNode	*Parent;
						LONG			 Number;

						*MsgClass	= IDCMP_GADGETUP;
						*MsgCode	= 0;
						*MsgGadget	= Handle -> ActiveIncrementer -> Special . Incrementer . Parent;

						Parent = (*MsgGadget) -> UserData;

						if(Parent -> Special . Integer . IncrementerHook)
							Number = CallHookPkt(Parent -> Special . Integer . IncrementerHook,(APTR)LT_GetAttributes(Handle,Parent -> ID,TAG_DONE),Handle -> ActiveIncrementer -> Special . Incrementer . Amount < 0 ? (APTR)INCREMENTERMSG_DECREMENT : (APTR)INCREMENTERMSG_INCREMENT);
						else
							Number = ((LONG)LT_GetAttributes(Handle,Parent -> ID,TAG_DONE)) + Handle -> ActiveIncrementer -> Special . Incrementer . Amount;

						if(Number >= Parent -> Special . Integer . Min && Number <= Parent -> Special . Integer . Max)
							LT_SetAttributes(Handle,Parent -> ID,GTIN_Number,Number,TAG_DONE);
					}

					Handle -> IncrementerCountdown = 2;
				}
			}

			break;

		case IDCMP_RAWKEY:
		{
			UBYTE			Buffer[10],Key;
			struct InputEvent	event;
			BOOLEAN			KeyUp;

			if(*MsgCode == 95 && Handle -> HelpHook)
			{
				ObjectNode	*Item;
				struct HelpMsg	 Message;
				struct IBox	 Box;

				if(Item = LTK_FindNode_Position(Handle -> TopGroup,Handle -> Window -> MouseX,Handle -> Window -> MouseY))
				{
					Message . ObjectID = Item -> ID;

					Box . Left	= Item -> Left;
					Box . Top	= Item -> Top;
					Box . Width	= Item -> Width;
					Box . Height	= Item -> Height;
				}
				else
				{
					Message . ObjectID = -1;

					Box . Left	= 0;
					Box . Top	= 0;
					Box . Width	= Handle -> Window -> Width;
					Box . Height	= Handle -> Window -> Height;
				}

				Message . Handle = Handle;

				CallHookPkt(Handle -> HelpHook,&Message,&Box);

				*MsgClass = NULL;

				break;
			}

			if((*MsgCode >= 99 && *MsgCode <= 103) || *MsgCode == 96 || *MsgCode == 97)
			{
				*MsgClass = NULL;

				break;
			}

			event . ie_NextEvent		= NULL;
			event . ie_Code			= (*MsgCode) & ~IECODE_UP_PREFIX;
			event . ie_Qualifier		= MsgQualifier & ~QUALIFIER_SHIFT;
			event . ie_Class		= IECLASS_RAWKEY;
			event . ie_SubClass		= 0;
			event . ie_position . ie_addr	= (APTR)*MsgGadget;

			Buffer[0] = 0;

			if(MapRawKey(&event,Buffer,9,NULL) < 1)
			{
				*MsgClass = NULL;

				break;
			}

			if(!(Key = Buffer[0]))
			{
				*MsgClass = NULL;

				break;
			}

			if((*MsgCode) & IECODE_UP_PREFIX)
				KeyUp = TRUE;
			else
				KeyUp = FALSE;

			if(!KeyUp && Handle -> CursorKey && (*MsgCode == CURSORUP || *MsgCode == CURSORDOWN))
			{
				ObjectNode	*Node;
				struct Gadget	*Gadget;
				LONG		 NewState;

				Node		= Handle -> CursorKey;
				Gadget		= Node -> Host;
				NewState	= Node -> Current;

				if(*MsgCode == CURSORDOWN)
				{
					if(MsgQualifier & QUALIFIER_SHIFT)
						NewState += Node -> Lines;
					else
					{
						if(MsgQualifier & (QUALIFIER_ALT | QUALIFIER_CONTROL))
							NewState = Node -> Max;
						else
							NewState++;
					}
				}
				else
			        {
					if(MsgQualifier & QUALIFIER_SHIFT)
						NewState -= Node -> Lines;
					else
					{
						if(MsgQualifier & (QUALIFIER_ALT | QUALIFIER_CONTROL))
							NewState = Node -> Min;
						else
							NewState--;
					}
				}

				if(NewState < Node -> Min)
					NewState = Node -> Min;
				else
				{
					if(NewState > Node -> Max)
						NewState = Node -> Max;
				}

				if(NewState != Node -> Current && Node -> Max != Node -> Min)
				{
					Node -> Current = NewState;

					GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
						GTLV_Top,		Node -> Current,
						GTLV_MakeVisible,	Node -> Current,

						Node -> Special . List . LinkID != -1 ? GTLV_Selected : TAG_IGNORE,Node -> Current,
					TAG_DONE);

					LTK_PutStorage(Node);

					*MsgClass	= IDCMP_GADGETUP;
					*MsgCode	= Node -> Current;
					*MsgGadget	= Gadget;
		                }
		                else
					*MsgClass = NULL;

				return;
			}

			if(Key == '\33' && !KeyUp)
			{
				ObjectNode *Node;

				if(Node = Handle -> EscKey)
				{
					if(!Node -> Disabled)
					{
						LTK_BlinkButton(Handle,Node -> Host);

						*MsgCode	= 0;
						*MsgGadget	= Node -> Host;
						*MsgClass	= IDCMP_GADGETUP;
					}
					else
						*MsgClass = NULL;

					return;
				}

				if(Handle -> Window -> Flags & WFLG_CLOSEGADGET)
				{
					*MsgCode	= 0;
					*MsgGadget	= NULL;
					*MsgClass	= IDCMP_CLOSEWINDOW;
				}
				else
					*MsgClass = NULL;

				return;
			}
			else
			{
				struct Gadget	*Gadget;
				ObjectNode	*Node;
				LONG		 i;
				BOOLEAN		 FoundIt = FALSE,
						 Forward;

				if(MsgQualifier & QUALIFIER_SHIFT)
					Forward = FALSE;
				else
					Forward = TRUE;

				if(Handle -> ReturnKey && Key == '\r')
				{
					Node = Handle -> ReturnKey;

					if(!KeyUp && !Node -> Disabled)
					{
						LTK_BlinkButton(Handle,Node -> Host);

						*MsgClass	= IDCMP_GADGETUP;
						*MsgCode	= 0;
						*MsgGadget	= Node -> Host;
					}
					else
						*MsgClass = NULL;

					return;
				}

				for(i = 0 ; !FoundIt && i < Handle -> Count ; i++)
				{
					if(Gadget = Handle -> GadgetArray[i])
					{
						if(GETOBJECT(Gadget,Node))
						{
							if(Key == Node -> Key)
							{
								if(Node -> Disabled)
									break;
								else
								{
									switch(Node -> Type)
									{
#ifdef DO_TAPEDECK_KIND
										case TAPEDECK_KIND:

											if(!(MsgQualifier & IEQUALIFIER_REPEAT))
											{
												if(Node -> Special . TapeDeck . Toggle)
												{
													LT_SetAttributes(Handle,Node -> ID,
														LATD_Pressed,!Node -> Current,
													TAG_DONE);

													LTK_PutStorage(Node);
												}

												if(Gadget -> Flags & GFLG_SELECTED)
													*MsgCode = TRUE;
												else
													*MsgCode = FALSE;

												*MsgClass	= IDCMP_GADGETUP;
												*MsgGadget	= Gadget;
											}
											else
												*MsgClass = NULL;

											break;
#endif	/* DO_TAPEDECK_KIND */
										case CHECKBOX_KIND:

											if(!KeyUp && !(MsgQualifier & IEQUALIFIER_REPEAT))
											{
												Node -> Current = !Node -> Current;

												LTK_PutStorage(Node);

												GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
													GTCB_Checked,Node -> Current,
												TAG_DONE);

												*MsgClass	= IDCMP_GADGETUP;
												*MsgCode	= Node -> Current;
												*MsgGadget	= Gadget;
											}
											else
												*MsgClass = NULL;

											FoundIt = TRUE;

											break;

										case LISTVIEW_KIND:

											if(!KeyUp)
											{
												BOOLEAN setAttrs = FALSE;

												if(Forward)
												{
													if(Node -> Current < Node -> Max)
													{
														Node -> Current++;

														setAttrs = TRUE;
													}
												}
												else
												{
													if(Node -> Current > Node -> Min)
													{
														Node -> Current--;

														setAttrs = TRUE;
													}
												}

												if(setAttrs)
												{
													if(Node -> Current < 0)
													{
														GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
															GTLV_Selected,Node -> Current,
														TAG_DONE);
													}
													else
													{
							                                                        GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
															GTLV_Top,		Node -> Current,
															GTLV_MakeVisible,	Node -> Current,
															Node -> Special . List . LinkID != -1 ? GTLV_Selected : TAG_IGNORE,Node -> Current,
														TAG_DONE);
													}

													LTK_PutStorage(Node);

													*MsgClass	= IDCMP_GADGETUP;
													*MsgCode	= Node -> Current;
													*MsgGadget	= Gadget;
												}
												else
													*MsgClass = NULL;
											}
											else
												*MsgClass = NULL;

											FoundIt = TRUE;

											break;

										case MX_KIND:

											if(!KeyUp)
											{
												if(Forward)
												{
													if(Node -> Current < Node -> Max)
													{
														Node -> Current++;

														GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
															GTMX_Active,Node -> Current,
														TAG_DONE);

														LTK_PutStorage(Node);

														*MsgClass	= IDCMP_GADGETDOWN;
														*MsgCode	= Node -> Current;
														*MsgGadget	= Gadget;
													}
													else
														*MsgClass = NULL;
												}
												else
												{
													if(Node -> Current > Node -> Min)
													{
														Node -> Current--;

														GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
															GTMX_Active,Node -> Current,
														TAG_DONE);

														LTK_PutStorage(Node);

														*MsgClass	= IDCMP_GADGETDOWN;
														*MsgCode	= Node -> Current;
														*MsgGadget	= Gadget;
													}
													else
														*MsgClass = NULL;
												}
											}
											else
												*MsgClass = NULL;

											FoundIt = TRUE;

											break;

										case TEXT_KIND:

											if(!KeyUp && !(MsgQualifier & IEQUALIFIER_REPEAT))
											{
												if(Node -> Special . Text . Picker)
												{
													LTK_BlinkButton(Handle,Node -> Special . Text . Picker);

													*MsgClass	= IDCMP_IDCMPUPDATE;
													*MsgCode	= 0;
													*MsgGadget	= Gadget;
												}
												else
													*MsgClass = NULL;
											}
											else
												*MsgClass = NULL;

											FoundIt = TRUE;

											break;

										case NUMBER_KIND:

											*MsgClass = NULL;

											FoundIt = TRUE;

											break;

										case CYCLE_KIND:

											if(!KeyUp && !(MsgQualifier & IEQUALIFIER_REPEAT))
											{
												LTK_BlinkButton(Handle,Gadget);

												if(Forward)
												{
													if(Node -> Current < Node -> Max)
														Node -> Current++;
													else
														Node -> Current = Node -> Min;
												}
												else
												{
													if(Node -> Current > Node -> Min)
														Node -> Current--;
													else
														Node -> Current = Node -> Max;
												}

												LTK_PutStorage(Node);

												GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
													GTCY_Active,Node -> Current,
												TAG_DONE);

												*MsgClass	= IDCMP_GADGETUP;
												*MsgCode	= Node -> Current;
												*MsgGadget	= Gadget;
											}
											else
												*MsgClass = NULL;

											FoundIt = TRUE;

											break;

										case PALETTE_KIND:

											if(!KeyUp)
											{
												if(Forward)
												{
													if(Node -> Current < Node -> Max)
														Node -> Current++;
													else
														Node -> Current = Node -> Min;
												}
												else
												{
													if(Node -> Current > Node -> Min)
														Node -> Current--;
													else
														Node -> Current = Node -> Max;
												}

												LTK_PutStorage(Node);

												GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
													GTPA_Color,Node -> Current,
												TAG_DONE);

												*MsgClass	= IDCMP_GADGETUP;
												*MsgCode	= Node -> Current;
												*MsgGadget	= Gadget;
											}
											else
												*MsgClass = NULL;

											FoundIt = TRUE;

											break;

										case SLIDER_KIND:

											if(KeyUp)
											{
												*MsgClass	= IDCMP_GADGETUP;
												*MsgCode	= Node -> Current;
												*MsgGadget	= Gadget;
											}
											else
											{
												if(Forward)
												{
													if(Node -> Current < Node -> Max)
													{
														Node -> Current++;

														GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
															GTSL_Level,Node -> Current,
														TAG_DONE);

														LTK_PutStorage(Node);

														*MsgClass	= IDCMP_MOUSEMOVE;
														*MsgCode	= Node -> Current;
														*MsgGadget	= Gadget;
													}
													else
														*MsgClass = NULL;
												}
												else
												{
													if(Node -> Current > Node -> Min)
													{
														Node -> Current--;

														GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
															GTSL_Level,Node -> Current,
														TAG_DONE);

														LTK_PutStorage(Node);

														*MsgClass	= IDCMP_MOUSEMOVE;
														*MsgCode	= Node -> Current;
														*MsgGadget	= Gadget;
													}
													else
														*MsgClass = NULL;
												}
											}

											FoundIt = TRUE;

											break;

										case SCROLLER_KIND:

											if(KeyUp)
											{
												*MsgClass	= IDCMP_GADGETUP;
												*MsgCode	= Node -> Current;
												*MsgGadget	= Gadget;
											}
											else
											{
												if(Forward)
												{
													if(Node -> Current < Node -> Max)
													{
														Node -> Current++;

														GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
															GTSC_Top,Node -> Current,
														TAG_DONE);

														LTK_PutStorage(Node);

														*MsgClass	= IDCMP_MOUSEMOVE;
														*MsgCode	= Node -> Current;
														*MsgGadget	= Gadget;
													}
													else
														*MsgClass = NULL;
												}
												else
												{
													if(Node -> Current > Node -> Min)
													{
														Node -> Current--;

														GT_SetGadgetAttrs(Gadget,Handle -> Window,NULL,
															GTSC_Top,Node -> Current,
														TAG_DONE);

														LTK_PutStorage(Node);

														*MsgClass	= IDCMP_MOUSEMOVE;
														*MsgCode	= Node -> Current;
														*MsgGadget	= Gadget;
													}
													else
														*MsgClass = NULL;
												}
											}

											FoundIt = TRUE;

											break;

										case STRING_KIND:
										case FRACTION_KIND:
										case PASSWORD_KIND:

											if(!KeyUp)
											{
												if(Node -> Special . String . Picker && !Forward)
												{
													LTK_BlinkButton(Handle,Node -> Special . String . Picker);

													*MsgClass	= IDCMP_IDCMPUPDATE;
													*MsgCode	= 0;
													*MsgGadget	= Gadget;
												}
												else
												{
													ActivateGadget(Gadget,Handle -> Window,NULL);

													*MsgClass	= IDCMP_GADGETDOWN;
													*MsgCode	= 0;
													*MsgGadget	= Gadget;

													Handle -> Previous = Gadget;
												}
											}
											else
												*MsgClass = NULL;

											FoundIt = TRUE;

											break;

										case INTEGER_KIND :

											if(!KeyUp)
											{
												if(Node -> Special . Integer . UseIncrementers)
												{
													ObjectNode	*incNode,*Parent = (ObjectNode *)Gadget -> UserData;
													LONG		 Number;
													struct Gadget	*Incrementer;

													if(Forward)
													{
														incNode = Node -> Special . Integer . RightIncrementer -> UserData;

														Incrementer = Node -> Special . Integer . RightIncrementer;
													}
													else
													{
														incNode = Node -> Special . Integer . LeftIncrementer -> UserData;

														Incrementer = Node -> Special . Integer . LeftIncrementer;
													}

													if(Parent -> Special . Integer . IncrementerHook)
														Number = CallHookPkt(Parent -> Special . Integer . IncrementerHook,(APTR)LT_GetAttributes(Handle,Parent -> ID,TAG_DONE),incNode -> Special . Incrementer . Amount < 0 ? (APTR)INCREMENTERMSG_DECREMENT : (APTR)INCREMENTERMSG_INCREMENT);
													else
														Number = LT_GetAttributes(Handle,Parent -> ID,TAG_DONE) + incNode -> Special . Incrementer . Amount;

													if(Number >= Parent -> Special . Integer . Min && Number <= Parent -> Special . Integer . Max)
													{
														LTK_BlinkButton(Handle,Incrementer);

														LT_SetAttributes(Handle,Parent -> ID,GTIN_Number,Number,TAG_DONE);

														*MsgClass = IDCMP_GADGETUP;

														LTK_PutStorage(Node);
													}
												}
												else
												{
													ActivateGadget(Gadget,Handle -> Window,NULL);

													*MsgClass = IDCMP_GADGETDOWN;
												}

												*MsgCode	= 0;
												*MsgGadget	= Gadget;

												Handle -> Previous = Gadget;
											}
											else
												*MsgClass = NULL;

											FoundIt = TRUE;

											break;

										case BUTTON_KIND:

											if(!KeyUp && !(MsgQualifier & IEQUALIFIER_REPEAT))
											{
												LTK_BlinkButton(Handle,Gadget);

												*MsgClass	= IDCMP_GADGETUP;
												*MsgCode	= 0;
												*MsgGadget	= Gadget;
											}
											else
												*MsgClass = NULL;

											FoundIt = TRUE;

											break;
									}
								}
							}
						}
					}
				}
			}

			break;
		}

		case IDCMP_GADGETDOWN:

			if(GETOBJECT((*MsgGadget),Node))
			{
				switch(Node -> Type)
				{
					case STRING_KIND:
					case INTEGER_KIND:
					case FRACTION_KIND:
					case PASSWORD_KIND:

						Handle -> Previous = *MsgGadget;
						break;
#ifdef DO_TAPEDECK_KIND
					case TAPEDECK_KIND:

						if(Node -> Special . TapeDeck . Toggle)
						{
							if((*MsgGadget) -> Flags & GFLG_SELECTED)
								*MsgCode = TRUE;
							else
								*MsgCode = FALSE;

							Node -> Current = *MsgCode;

							LTK_PutStorage(Node);

							if(Handle -> Previous && !Node -> PageSelector)
								Activate = TRUE;

							*MsgClass = IDCMP_GADGETUP;
						}
						else
							*MsgClass = NULL;

						break;
#endif	/* DO_TAPEDECK_KIND */
					case MX_KIND:

						if(!V39 && Node -> Disabled)
						{
							GT_SetGadgetAttrs(*MsgGadget,Handle -> Window,NULL,
								GTMX_Active,Node -> Current,
							TAG_DONE);

							*MsgClass = NULL;
						}
						else
						{
							Node -> Current = *MsgCode;

							LTK_PutStorage(Node);

							if(Handle -> Previous && !Node -> PageSelector)
								Activate = TRUE;
						}

						break;

					case SLIDER_KIND:

						if(Node -> Min < 0)
							Node -> Current = (WORD)*MsgCode;
						else
							Node -> Current = *MsgCode;

						LTK_PutStorage(Node);

						if(Handle -> Previous && !Node -> PageSelector)
							Activate = TRUE;

						break;

					case SCROLLER_KIND:

						Node -> Current = *MsgCode;

						LTK_PutStorage(Node);

						if(Handle -> Previous && !Node -> PageSelector)
							Activate = TRUE;

						break;

					case INCREMENTER_KIND:

						Handle -> ActiveIncrementer	= Node;
						Handle -> IncrementerCountdown	= 6;

						*MsgClass	= IDCMP_GADGETUP;
						*MsgCode	= 0;
						*MsgGadget	= Node -> Special . Incrementer . Parent;

						/* ALWAYS */
						{
							struct ObjectNode	*Parent = (ObjectNode *)(*MsgGadget) -> UserData;
							LONG			 Number;

							if(Parent -> Special . Integer . IncrementerHook)
								Number = CallHookPkt(Parent -> Special . Integer . IncrementerHook,(APTR)LT_GetAttributes(Handle,Parent -> ID,TAG_DONE),Node -> Special . Incrementer . Amount < 0 ? (APTR)INCREMENTERMSG_DECREMENT : (APTR)INCREMENTERMSG_INCREMENT);
							else
								Number = LT_GetAttributes(Handle,Parent -> ID,TAG_DONE) + Node -> Special . Incrementer . Amount;

							if(Number >= Parent -> Special . Integer . Min && Number <= Parent -> Special . Integer . Max)
								LT_SetAttributes(Handle,Parent -> ID,GTIN_Number,Number,TAG_DONE);
							else
								*MsgClass = NULL;
						}

						break;
				}
			}

			break;

		case IDCMP_MOUSEBUTTONS:

			if(((*MsgCode) & IECODE_UP_PREFIX) && Handle -> Previous)
				Activate = TRUE;

			if((*MsgCode == SELECTUP) && (Handle -> ActiveIncrementer))
				Handle -> ActiveIncrementer = NULL;

			break;

		case IDCMP_GADGETUP:

			if(GETOBJECT((*MsgGadget),Node))
			{
				switch(Node -> Type)
				{
					case FRACTION_KIND:

						LT_SetAttributes(Handle,(*MsgGadget) -> GadgetID,LAFC_Number,LT_GetAttributes(Handle,(*MsgGadget) -> GadgetID,TAG_DONE),TAG_DONE);

					/* FALLS THROUGH */

					case PASSWORD_KIND:
					case STRING_KIND:

						Handle -> Previous = NULL;

						LTK_PutStorage(Node);

						break;

					case INTEGER_KIND:
					{
						struct StringInfo *StringInfo = (struct StringInfo *)(*MsgGadget) -> SpecialInfo;
						LONG Contents = StringInfo -> LongInt;

						if(Contents < Node -> Special . Integer . Min)
							Contents = Node -> Special . Integer . Min;
						else
						{
							if(Contents > Node -> Special . Integer . Max)
								Contents = Node -> Special . Integer . Max;
						}

						LT_SetAttributes(Handle,(*MsgGadget) -> GadgetID,GTIN_Number,Contents,TAG_DONE);

						Handle -> Previous = NULL;

						LTK_PutStorage(Node);

						break;
					}

					case CHECKBOX_KIND:

						if(!V39)
						{
							if((*MsgGadget) -> Flags & GFLG_SELECTED)
								*MsgCode = TRUE;
							else
								*MsgCode = FALSE;
						}

						Node -> Current = *MsgCode;

						LTK_PutStorage(Node);

						if(Handle -> Previous && !Node -> PageSelector)
							Activate = TRUE;

						break;
#ifdef DO_TAPEDECK_KIND
					case TAPEDECK_KIND:

						if(Node -> Special . TapeDeck . Toggle)
						{
							if((*MsgGadget) -> Flags & GFLG_SELECTED)
								*MsgCode = TRUE;
							else
								*MsgCode = FALSE;

							Node -> Current = *MsgCode;

							LTK_PutStorage(Node);
						}

						if(Handle -> Previous && !Node -> PageSelector)
							Activate = TRUE;

						break;
#endif	/* DO_TAPEDECK_KIND */
					case CYCLE_KIND:
					case PALETTE_KIND:

						Node -> Current = *MsgCode;

						LTK_PutStorage(Node);

						if(Handle -> Previous && !Node -> PageSelector)
							Activate = TRUE;

						break;

					case MX_KIND:

						if(!V39 && Node -> Disabled)
						{
							GT_SetGadgetAttrs(*MsgGadget,Handle -> Window,NULL,
								GTMX_Active,Node -> Current,
							TAG_DONE);

							*MsgClass = NULL;
						}
						else
						{
							Node -> Current = *MsgCode;

							LTK_PutStorage(Node);

							if(Handle -> Previous && !Node -> PageSelector)
								Activate = TRUE;
						}

						break;

					case SLIDER_KIND:

						if(Node -> Min < 0)
							Node -> Current = (WORD)*MsgCode;
						else
							Node -> Current = *MsgCode;

						LTK_PutStorage(Node);

						if(Handle -> Previous && !Node -> PageSelector)
							Activate = TRUE;

						break;

					case SCROLLER_KIND:

						Node -> Current = *MsgCode;

						LTK_PutStorage(Node);

						if(Handle -> Previous && !Node -> PageSelector)
							Activate = TRUE;

						break;

					case PICKER_KIND:

						*MsgClass	= IDCMP_IDCMPUPDATE;
						*MsgCode	= 0;
						*MsgGadget	= Node -> Special . Picker . Parent;

						if(Handle -> Previous && !Node -> PageSelector)
							Activate = TRUE;

						break;

					case INCREMENTER_KIND:

						Handle -> ActiveIncrementer = NULL;
						break;

					case LISTVIEW_KIND:

						if(Node -> Current != *MsgCode)
							CurrentTime(&Node -> Special . List . ClickSeconds,&Node -> Special . List . ClickMicros);
						else
						{
							ULONG Seconds,Micros;

							CurrentTime(&Seconds,&Micros);

							if(DoubleClick(Node -> Special . List . ClickSeconds,Node -> Special . List . ClickMicros,Seconds,Micros))
								*MsgClass = IDCMP_IDCMPUPDATE;

							Node -> Special . List . ClickSeconds	= Seconds;
							Node -> Special . List . ClickMicros	= Micros;
						}

						Node -> Current = *MsgCode;

						LTK_PutStorage(Node);

						if(Node -> Special . List . Link)
						{
							Handle -> Previous = Node -> Special . List . Link;

							if(!Node -> PageSelector)
								Activate = TRUE;
						}
						else
						{
							if(Handle -> Previous && !Node -> PageSelector)
								Activate = TRUE;
						}

						break;

					default:

						if(Handle -> Previous && !Node -> PageSelector)
							Activate = TRUE;

						break;
				}
			}

			break;

		case IDCMP_MOUSEMOVE:

			if(GETOBJECT((*MsgGadget),Node))
			{
				if(Node -> Type == SLIDER_KIND)
				{
					if(Node -> Min < 0)
						Node -> Current = (WORD)*MsgCode;
					else
						Node -> Current = *MsgCode;

					LTK_PutStorage(Node);
				}
				else
				{
					if(Node -> Type == SCROLLER_KIND)
					{
						Node -> Current = *MsgCode;

						LTK_PutStorage(Node);
					}
				}
			}

			break;

		case IDCMP_ACTIVEWINDOW:

			if(Handle -> Previous)
			{
				Activate = TRUE;

				*MsgClass	= IDCMP_GADGETDOWN;
				*MsgCode	= 0;
				*MsgGadget	= Handle -> Previous;
			}

			break;

		case IDCMP_INACTIVEWINDOW:

			Handle -> ActiveIncrementer = NULL;
			break;
	}

	if(Handle -> AutoActivate && Activate && Handle -> Previous)
	{
		if(GETOBJECT(Handle -> Previous,Node))
		{
			if(!Node -> Disabled)
				ActivateGadget(Handle -> Previous,Handle -> Window,NULL);
		}
	}
}


/*****************************************************************************/


VOID LIBENT
LT_BeginRefresh(REG(a0) LayoutHandle *handle)
{
	if(handle)
	{
		GT_BeginRefresh(handle -> Window);

		LTK_DrawGroup(handle,handle -> TopGroup);
	}
}


/*****************************************************************************/


VOID LIBENT
LT_EndRefresh(REG(a0) LayoutHandle *handle,REG(d0) BOOL complete)
{
	if(handle)
		GT_EndRefresh(handle -> Window,complete);
}


/*****************************************************************************/


LONG __stdargs
LT_GetAttributes(LayoutHandle *Handle,LONG ID,...)
{
	va_list	VarArgs;
	LONG	Result;

	va_start(VarArgs,ID);
	Result = LT_GetAttributesA(Handle,ID,(struct TagItem *)VarArgs);
	va_end(VarArgs);

	return(Result);
}

LONG LIBENT
LT_GetAttributesA(REG(a0) LayoutHandle *Handle,REG(d0) LONG ID,REG(a1) struct TagItem *TagList)
{
	if(Handle)
	{
		ObjectNode *Node;

		if(Node = LTK_FindNode(Handle -> TopGroup,ID))
		{
			struct StringInfo *StringInfo;

			if(TagList)
			{
				struct TagItem	*TagItem,
						*TempPtr = TagList;
				LONG		*Value;

				while(TagItem = NextTagItem(&TempPtr))
				{
					Value = (LONG *)TagItem -> ti_Data;

					switch(TagItem -> ti_Tag)
					{
						case LA_Left:

							if(Node -> Type == FRAME_KIND && Node -> Special . Frame . DrawBox)
								*Value = Node -> Left + 4;
							else
								*Value = Node -> Left;

							break;

						case LA_Top:

							if(Node -> Type == FRAME_KIND && Node -> Special . Frame . DrawBox)
								*Value = Node -> Top + 2;
							else
								*Value = Node -> Top;

							break;

						case LA_Width:

							if(Node -> Type == FRAME_KIND && Node -> Special . Frame . DrawBox)
								*Value = Node -> Width - 8;
							else
								*Value = Node -> Width;

							break;

						case LA_Height:

							if(Node -> Type == FRAME_KIND && Node -> Special . Frame . DrawBox)
								*Value = Node -> Height - 4;
							else
								*Value = Node -> Height;

							break;
					}
				}
			}

			if(Node -> Host)
			{
				struct Gadget *Gadget = Node -> Host;

				switch(Node -> Type)
				{
					case CHECKBOX_KIND:
					case LISTVIEW_KIND:
					case MX_KIND:
					case CYCLE_KIND:
					case PALETTE_KIND:
					case SLIDER_KIND:
					case SCROLLER_KIND:
#ifdef DO_GAUGE_KIND
					case GAUGE_KIND:
#endif
#ifdef DO_TAPEDECK_KIND
					case TAPEDECK_KIND:
#endif	/* DO_TAPEDECK_KIND */
						return(Node -> Current);

#ifdef DO_FRACTION_KIND
					case FRACTION_KIND:
					{
						FIXED Contents;

						StringInfo = (struct StringInfo *)Gadget -> SpecialInfo;

						Contents = LT_String2Fixed(StringInfo -> Buffer);

						if(Contents < Node -> Special . String . Min)
							Contents = Node -> Special . String . Min;
						else
						{
							if(Contents > Node -> Special . String . Max)
								Contents = Node -> Special . String . Max;
						}

						LT_SetAttributes(Handle,Gadget -> GadgetID,LAFC_Number,Contents,TAG_DONE);

						return((LONG)Contents);
					}
#endif
					case STRING_KIND:

						StringInfo = (struct StringInfo *)Gadget -> SpecialInfo;

						return((LONG)StringInfo -> Buffer);
#ifdef DO_PASSWORD_KIND
					case PASSWORD_KIND:

						return((LONG)Node -> Special . String . Original);
#endif
					case INTEGER_KIND:
					{
						LONG Contents;

						StringInfo = (struct StringInfo *)Gadget -> SpecialInfo;

						Contents = StringInfo -> LongInt;

						if(Contents < Node -> Special . Integer . Min)
							LT_SetAttributes(Handle,Gadget -> GadgetID,GTIN_Number,Contents = Node -> Special . Integer . Min,TAG_DONE);
						else
						{
							if(Contents > Node -> Special . Integer . Max)
								LT_SetAttributes(Handle,Gadget -> GadgetID,GTIN_Number,Contents = Node -> Special . Integer . Max,TAG_DONE);
							else
								Node -> Special . Integer . Number = Contents;
						}

						return(Contents);
					}

					case GROUP_KIND:

						return((LONG)Node -> Special . Group . ActivePage);
				}
			}
		}
	}

	return(NULL);
}


/*****************************************************************************/


VOID __stdargs
LT_SetAttributes(LayoutHandle *handle,LONG id,...)
{
	va_list VarArgs;

	va_start(VarArgs,id);
	LT_SetAttributesA(handle,id,(struct TagItem *)VarArgs);
	va_end(VarArgs);
}

VOID LIBENT
LT_SetAttributesA(REG(a0) LayoutHandle *handle,REG(d0) LONG id,REG(a1) struct TagItem *TagList)
{
	if(handle && TagList)
	{
		struct Gadget	*Gadget;
		struct TagItem	*ThisTag;
		ObjectNode	*Node;

		if(ThisTag = FindTagItem(LH_AutoActivate,TagList))
			handle -> AutoActivate = ThisTag -> ti_Data;

		if(ThisTag = FindTagItem(LH_LocaleHook,TagList))
			handle -> LocaleHook = (struct Hook *)ThisTag -> ti_Data;

		if(Gadget = LTK_FindGadget(handle,id))
		{
			if(Node = (ObjectNode *)Gadget -> UserData)
			{
				if(Node -> Host != Gadget || Node -> PointBack != Node)
					Node = NULL;
			}
		}
		else
			Node = LTK_FindNode(handle -> TopGroup,id);

		if(Node)
		{
			STATIC Tag Filter[] = { GA_Disabled,TAG_DONE };

			struct TagItem	*NewTags = NULL;
			ULONG		 Exclude = NULL;

			switch(Node -> Type)
			{
				case CHECKBOX_KIND:

					if(!Gadget)
						return;

					if(ThisTag = FindTagItem(GTCB_Checked,TagList))
					{
						if((Node -> Current && ThisTag -> ti_Data) || (!Node -> Current && !ThisTag -> ti_Data))
							Exclude = GTCB_Checked;
						else
						{
							Node -> Current = ThisTag -> ti_Data;

							LTK_PutStorage(Node);
						}
					}

					break;

#ifdef DO_TAPEDECK_KIND
				case TAPEDECK_KIND:

					if(Gadget)
					{
						if(ThisTag = FindTagItem(LATD_Pressed,TagList))
						{
							if(Node -> Current != ThisTag -> ti_Data && Node -> Special . TapeDeck . Toggle)
							{
								RemoveGList(handle -> Window,handle -> List,(UWORD)-1);

								if(Node -> Current = ThisTag -> ti_Data)
									Gadget -> Flags |= GFLG_SELECTED;
								else
									Gadget -> Flags &= ~GFLG_SELECTED;

								AddGList(handle -> Window,handle -> List,(UWORD)-1,(UWORD)-1,NULL);
								RefreshGList(Gadget,handle -> Window,NULL,1);
							}
						}
					}

					break;
#endif	/* DO_TAPEDECK_KIND */

#ifdef DO_GAUGE_KIND
				case GAUGE_KIND:

					if(Gadget)
					{
						LONG	Percent		= (LONG)Node -> Current;
						BOOLEAN	NeedRefresh	= FALSE;

						if(ThisTag = FindTagItem(LAGA_Percent,TagList))
						{
							Percent = (LONG)ThisTag -> ti_Data;

							if(Percent < 0)
								Percent = 0;
							else
							{
								if(Percent > 100)
									Percent = 100;
							}

							if(Percent != (LONG)Node -> Current)
								NeedRefresh = TRUE;
						}

						if(ThisTag = FindTagItem(LAGA_InfoText,TagList))
						{
							STRPTR SomeText = (STRPTR)ThisTag -> ti_Data;

							if(Node -> Special . Gauge . InfoLength)
							{
								LONG Len = strlen(SomeText);

								if(Len > Node -> Special . Gauge . InfoLength)
									Len = Node -> Special . Gauge . InfoLength;

								CopyMem(SomeText,Node -> Special . Gauge . InfoText,Len);

								Node -> Special . Gauge . InfoText[Len] = 0;

								NeedRefresh = TRUE;
							}
						}

						if(NeedRefresh)
							LTK_DrawGauge(handle,Node,Percent,FALSE);
					}

					return;
#endif
				case LISTVIEW_KIND:

					if(!Gadget)
						return;

					if(ThisTag = FindTagItem(GTLV_Labels,TagList))
					{
						if(ThisTag -> ti_Data == (ULONG)~0)
							Node -> Min = Node -> Max = -1;
						else
						{
							struct List	*List;
							LONG		 Count = 0;

							if(List = (struct List *)ThisTag -> ti_Data)
							{
								struct Node *Item;

								SCANLIST(List,Item)
								{
									Count++;
								}
							}

							Node -> Min = 0;

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

					if(ThisTag = FindTagItem(GA_Disabled,TagList))
					{
						if(!V39)
						{
							if(!(NewTags = CloneTagItems(TagList)))
								return;
							else
								FilterTagItems(NewTags,Filter,TAGFILTER_NOT);
						}
					}

					if(ThisTag = FindTagItem(GTLV_Selected,TagList))
					{
						struct List *List;

						Node -> Current = (LONG)ThisTag -> ti_Data;

						if(ThisTag = FindTagItem(GTLV_Labels,TagList))
							List = (struct List *)ThisTag -> ti_Data;
						else
							List = NULL;

						if(Node -> Current < 0)
						{
							GT_SetGadgetAttrs(Gadget,handle -> Window,NULL,
								GTLV_Selected,			Node -> Current,
								List ? GTLV_Labels : TAG_IGNORE,List,
							TAG_DONE);
						}
						else
						{
							GT_SetGadgetAttrs(Gadget,handle -> Window,NULL,
								GTLV_Selected,			Node -> Current,
								GTLV_Top,			Node -> Current,
								GTLV_MakeVisible,		Node -> Current,

								List ? GTLV_Labels : TAG_IGNORE,List,
							TAG_DONE);
						}

						LTK_PutStorage(Node);
					}

					break;

				case MX_KIND:

					if(!Gadget)
						return;

					if(!V39)
					{
						if(FindTagItem(GA_Disabled,TagList))
						{
							if(!(NewTags = CloneTagItems(TagList)))
								return;
							else
								FilterTagItems(NewTags,Filter,TAGFILTER_NOT);
						}
					}

					if(ThisTag = FindTagItem(GTMX_Active,TagList))
					{
						if(Node -> Current == ThisTag -> ti_Data)
							Exclude = GTMX_Active;
						else
						{
							Node -> Current = ThisTag -> ti_Data;

							LTK_PutStorage(Node);
						}
					}

					break;

				case CYCLE_KIND:

					if(!Gadget)
						return;

					if(ThisTag = FindTagItem(GTCY_Active,TagList))
					{
						if(Node -> Current == ThisTag -> ti_Data)
							Exclude = GTCY_Active;
						else
						{
							Node -> Current = ThisTag -> ti_Data;

							LTK_PutStorage(Node);
						}
					}

					if(ThisTag = FindTagItem(GTCY_Labels,TagList))
					{
						STRPTR	*Strings;
						LONG	 Count = 0;

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

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

					break;

				case PALETTE_KIND:

					if(!Gadget)
						return;

					if(ThisTag = FindTagItem(GTPA_Color,TagList))
					{
						if(Node -> Current == ThisTag -> ti_Data)
							Exclude = GTPA_Color;
						else
						{
							Node -> Current = ThisTag -> ti_Data;

							LTK_PutStorage(Node);
						}
					}

					break;

				case INTEGER_KIND:

					if(!Gadget)
						return;

					if(ThisTag = FindTagItem(GTIN_Number,TagList))
					{
						LONG num;

						num = ThisTag -> ti_Data;

						if(num < Node -> Special . Integer . Min)
							num = Node -> Special . Integer . Min;
						else
						{
							if(num > Node -> Special . Integer . Max)
								num = Node -> Special . Integer . Max;
						}
#ifdef DO_HEXHOOK
						if(!Node -> Special . Integer . EditHook)
						{
							UBYTE			 buffer[20];
							struct StringInfo	*stringInfo;

							sprintf(buffer,"%ld",num);

							Exclude = GTIN_Number;

							GT_SetGadgetAttrs(Gadget,handle -> Window,NULL,
								GTST_String,buffer,
							TAG_DONE);

							stringInfo = (struct StringInfo *)Gadget -> SpecialInfo;

							stringInfo -> LongInt = num;
						}
						else
						{
							UBYTE			 buffer[40];
							struct StringInfo	*stringInfo;
							STRPTR			 Index;
							LONG			 Value,Number = num,
										 Scale,Sign;

							stringInfo = (struct StringInfo *)Gadget -> SpecialInfo;

							Index = stringInfo -> Buffer;

							while(*Index && *Index == ' ')
								Index++;

							switch(Index[0])
							{
								case '$':

									sprintf(buffer,"$%lx",num);
									break;

								case '&':

									if(Number < 0)
									{
										Sign = -1;
										Number = -Number;
									}
									else
										Sign = 1;

									for(Value = 0, Scale = 1 ; Number ; Number /= 8, Scale *= 10)
										Value += (Number & 7) * Scale;

									sprintf(buffer,"&%ld",Sign * Value);
									break;

								case '%':

									if(Number < 0)
									{
										Sign = -1;
										Number = -Number;
									}
									else
										Sign = 1;

									for(Value = 0, Scale = 1 ; Number ; Number /= 2, Scale *= 10)
										Value += (Number & 1) * Scale;

									sprintf(buffer,"%%%ld",Sign * Value);
									break;

								case '0':

									if(Index[1] == 'x')
									{
										sprintf(buffer,"0x%lx",num);
										break;
									}

									// Fall through to...

								default:

									sprintf(buffer,"%ld",num);
									break;
							}

							Exclude = GTIN_Number;

							GT_SetGadgetAttrs(Gadget,handle -> Window,NULL,
								GTST_String,buffer,
							TAG_DONE);

							stringInfo -> LongInt = num;
						}
#else
						GT_SetGadgetAttrs(Gadget,handle -> Window,NULL,
							GTIN_Number,num,
						TAG_DONE);
#endif
						Node -> Special . Integer . Number = num;

						LTK_PutStorage(Node);
					}

					break;

				case SLIDER_KIND:

					if(!Gadget)
						return;

					if(ThisTag = FindTagItem(GTSL_Level,TagList))
					{
						if(Node -> Current == ThisTag -> ti_Data)
							Exclude = GTSL_Level;
						else
						{
							Node -> Current = ThisTag -> ti_Data;

							LTK_PutStorage(Node);
						}
					}

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

						if(Node -> Current < Node -> Min)
						{
							Node -> Current = Node -> Min;

							LTK_PutStorage(Node);
						}
					}

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

						if(Node -> Current > Node -> Max)
						{
							Node -> Current = Node -> Max;

							LTK_PutStorage(Node);
						}
					}

					break;

				case SCROLLER_KIND:

					if(!Gadget)
						return;

					if(ThisTag = FindTagItem(GTSC_Top,TagList))
					{
						if(Node -> Current == ThisTag -> ti_Data)
							Exclude = GTSC_Top;
						else
						{
							Node -> Current = ThisTag -> ti_Data;

							LTK_PutStorage(Node);
						}
					}

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

						if(Node -> Current > Node -> Max)
						{
							Node -> Current = Node -> Max;

							LTK_PutStorage(Node);
						}
					}

					if(ThisTag = FindTagItem(GTSC_Visible,TagList))
						Node -> Special . Scroller . Visible = ThisTag -> ti_Data;

					break;

				case BOX_KIND:

					if(ThisTag = FindTagItem(LABX_Index,TagList))
					{
						LONG Index = ThisTag -> ti_Data;

						if(Index >= 0 && Index < Node -> Lines)
						{
							if(ThisTag = FindTagItem(LABX_Text,TagList))
							{
								if(Node -> Special . Box . ReserveSpace)
								{
									STRPTR	Text = (STRPTR)ThisTag -> ti_Data;
									WORD	Len = strlen(Text);

									if(Len > Node -> Special . Box . MaxSize)
										Len = Node -> Special . Box . MaxSize;

									CopyMem(Text,Node -> Special . Box . Lines[Index],Len);

									Node -> Special . Box . Lines[Index][Len] = 0;
								}
								else
									Node -> Special . Box . Lines[Index] = (STRPTR)ThisTag -> ti_Data;

								LTK_PrintBoxLine(handle,Node,Index);
							}
						}
					}

					if(ThisTag = FindTagItem(LABX_Lines,TagList))
					{
						STRPTR	*Lines = (STRPTR *)ThisTag -> ti_Data;
						LONG	 i;

						if(Node -> Special . Box . ReserveSpace)
						{
							WORD Len;

							for(i = 0 ; i < Node -> Lines ; i++)
							{
								if(Lines[i])
								{
									Len = strlen(Lines[i]);

									if(Len > Node -> Special . Box . MaxSize)
										Len = Node -> Special . Box . MaxSize;

									CopyMem(Lines[i],Node -> Special . Box . Lines[i],Len);

									Node -> Special . Box . Lines[i][Len] = 0;

									LTK_PrintBoxLine(handle,Node,i);
								}
								else
									break;
							}
						}
						else
						{
							for(i = 0 ; i < Node -> Lines ; i++)
							{
								if(Lines[i])
								{
									Node -> Special . Box . Lines[i] = Lines[i];

									LTK_PrintBoxLine(handle,Node,i);
								}
								else
									break;
							}
						}
					}

					break;

				case GROUP_KIND:

					if(Node -> Special . Group . Paging)
					{
						if(ThisTag = FindTagItem(LAGR_ActivePage,TagList))
						{
							ObjectNode *node;

							if(node = LTK_FindNode(handle -> TopGroup,id))
							{
								if(node -> Type == GROUP_KIND)
								{
									if(node -> Special . Group . ActivePage != ThisTag -> ti_Data)
									{
										node -> Special . Group . ActivePage = ThisTag -> ti_Data;

										LTK_SetPens(&handle -> RPort,handle -> BackgroundPen,0,JAM1);
										RectFill(&handle -> RPort,node -> Left,node -> Top,node -> Left+node -> Width - 1,node -> Top + node -> Height - 1);

										LT_Rebuild(handle,NULL,0,0,FALSE);
									}
								}
							}
						}
					}

					break;
			}

			if(ThisTag = FindTagItem(LA_LabelText,TagList))
				Node -> Label = (STRPTR)ThisTag -> ti_Data;

			if(ThisTag = FindTagItem(LA_LabelID,TagList))
			{
				if(handle -> LocaleHook)
					Node -> Label = (STRPTR)CallHookPkt(handle -> LocaleHook,handle,(APTR)ThisTag -> ti_Data);
			}

			if(ThisTag = FindTagItem(GA_Disabled,TagList))
			{
				if((Node -> Disabled && ThisTag -> ti_Data) || (!Node -> Disabled && !ThisTag -> ti_Data))
				{
					if(NewTags = CloneTagItems(TagList))
						FilterTagItems(NewTags,Filter,TAGFILTER_NOT);
				}
				else
				{
					struct Gadget *gad;

					Node -> Disabled = ThisTag -> ti_Data;

					switch(Node -> Type)
					{
						case TEXT_KIND:

							gad = Node -> Special . Text . Picker;
							break;

						case STRING_KIND:

							gad = Node -> Special . String . Picker;
							break;

						case INTEGER_KIND:

							if(gad = Node -> Special . Integer . LeftIncrementer)
							{
								RemoveGList(handle -> Window,handle -> List,(UWORD)-1);

								if(Node -> Disabled)
									gad -> Flags |= GFLG_DISABLED;
								else
									gad -> Flags &= ~GFLG_DISABLED;

								AddGList(handle -> Window,handle -> List,(UWORD)-1,(UWORD)-1,NULL);
								RefreshGList(gad,handle -> Window,NULL,1);
							}

							gad = Node -> Special . Integer . RightIncrementer;
							break;

						case TAPEDECK_KIND:

							gad = Node -> Host;

							Gadget = NULL;

							break;

						default:

							gad = NULL;
							break;
					}

					if(gad)
					{
						RemoveGList(handle -> Window,handle -> List,(UWORD)-1);

						if(Node -> Disabled)
							gad -> Flags |= GFLG_DISABLED;
						else
							gad -> Flags &= ~GFLG_DISABLED;

						AddGList(handle -> Window,handle -> List,(UWORD)-1,(UWORD)-1,NULL);
						RefreshGList(gad,handle -> Window,NULL,1);
					}
				}
			}

			if(Exclude)
			{
				ULONG Filter[2];

				Filter[0] = Exclude;
				Filter[1] = TAG_DONE;

				if(!NewTags)
					NewTags = CloneTagItems(TagList);

				if(NewTags)
					FilterTagItems(NewTags,Filter,TAGFILTER_NOT);
			}

			if(Gadget)
			{
				struct TagItem *tags = TagList;

				if(NewTags)
					tags = NewTags;

				if(!V39 && Node -> Disabled && Node -> Type == SLIDER_KIND)
				{
					GT_SetGadgetAttrs(Gadget,handle -> Window,NULL,
						GA_Disabled,	FALSE,
					TAG_MORE,tags);

					GT_SetGadgetAttrs(Gadget,handle -> Window,NULL,
						GA_Disabled,	TRUE,
					TAG_DONE);
				}
				else
					GT_SetGadgetAttrsA(Gadget,handle -> Window,NULL,tags);

				if(Node -> Type == STRING_KIND || Node -> Type == FRACTION_KIND || Node -> Type == PASSWORD_KIND)
					LTK_PutStorage(Node);
			}

			FreeTagItems(NewTags);
		}
	}
}


/*****************************************************************************/

VOID __stdargs
LT_Add(LayoutHandle *Handle,UBYTE Type,STRPTR Label,LONG ID,...)
{
	va_list VarArgs;

	va_start(VarArgs,ID);
	LT_AddA(Handle,Type,Label,ID,(struct TagItem *)VarArgs);
	va_end(VarArgs);
}

VOID LIBENT
LT_AddA(REG(a0) LayoutHandle *Handle,REG(d0) UBYTE Type,REG(d1) STRPTR Label,REG(d2) LONG ID,REG(a1) struct TagItem *TagList)
{
	struct TagItem *TagItem;

	if(!Handle || Handle -> Failed)
		return;

/*	if(ID > Handle -> MaxID)*/
/*		Handle -> MaxID = ID + 1;*/

	if(Type == VERTICAL_KIND || Type == HORIZONTAL_KIND || Handle -> TopGroup)
	{
		ObjectNode *Node;

		switch(Type)
		{
			case BUTTON_KIND:

				Handle -> IDCMP |= BUTTONIDCMP;
				break;

			case CHECKBOX_KIND:

				Handle -> IDCMP |= CHECKBOXIDCMP;
				break;

			case LISTVIEW_KIND:

				Handle -> IDCMP |= LISTVIEWIDCMP;
				break;

			case MX_KIND:

				Handle -> IDCMP |= MXIDCMP;
				break;

			case CYCLE_KIND:

				Handle -> IDCMP |= CYCLEIDCMP;
				break;

			case PALETTE_KIND:

				Handle -> IDCMP |= PALETTEIDCMP;
				break;

			case SLIDER_KIND:

				Handle -> IDCMP |= SLIDERIDCMP;
				break;

			case SCROLLER_KIND:

				Handle -> IDCMP |= SCROLLERIDCMP | ARROWIDCMP;
				break;

			case INTEGER_KIND:

				Handle -> IDCMP |= STRINGIDCMP | IDCMP_GADGETDOWN | IDCMP_INTUITICKS;
				break;

			case STRING_KIND:
			case FRACTION_KIND:
			case PASSWORD_KIND:

				Handle -> IDCMP |= STRINGIDCMP | IDCMP_GADGETDOWN;
				break;

			case END_KIND:

				Handle -> CurrentGroup = Handle -> CurrentGroup -> Special . Group . ParentGroup;
				return;
		}

		if(Node = LTK_CreateObjectNode(Handle,Type,ID,Label))
		{
			struct MinList	*ParentList;
			LONG		 FirstLabel	= -1;
			LONG		 LastLabel	= -1;

			Type = Node -> Type;

			if(Type == GROUP_KIND)
			{
				Node -> LabelPlace			= PLACE_IN;
				Node -> Special . Group . ParentGroup	= Handle -> CurrentGroup;

				if(!Handle -> TopGroup)
				{
					Handle -> TopGroup = Node;

					ParentList = NULL;
				}
				else
					ParentList = &Handle -> CurrentGroup -> Special . Group . ObjectList;

				Handle -> CurrentGroup = Node;

				NewList(&Node -> Special . Group . ObjectList);
			}
			else
				ParentList = &Handle -> CurrentGroup -> Special . Group . ObjectList;

			if(Label && !Node -> NoKey)
			{
				ULONG Len;

				Len = 0;

				while(Label[Len])
				{
					if(Label[Len] == '_')
					{
						Node -> Key = ToLower(Label[Len + 1]);

						break;
					}

					Len++;
				}
			}

			if(TagList)
			{
				struct TagItem *TempList = TagList;

				while(!Handle -> Failed && (TagItem = NextTagItem(&TempList)))
				{
					switch(TagItem -> ti_Tag)
					{
						case LA_BYTE:

							Node -> Storage		= (APTR)TagItem -> ti_Data;
							Node -> StorageType	= STORAGE_BYTE;

							LTK_GetStorage(Node);

							break;

						case LA_UBYTE:

							Node -> Storage		= (APTR)TagItem -> ti_Data;
							Node -> StorageType	= STORAGE_UBYTE;

							LTK_GetStorage(Node);

							break;

						case LA_WORD:

							Node -> Storage		= (APTR)TagItem -> ti_Data;
							Node -> StorageType	= STORAGE_WORD;

							LTK_GetStorage(Node);

							break;

						case LA_UWORD:

							Node -> Storage		= (APTR)TagItem -> ti_Data;
							Node -> StorageType	= STORAGE_UWORD;

							LTK_GetStorage(Node);

							break;

						case LA_LONG:

							Node -> Storage		= (APTR)TagItem -> ti_Data;
							Node -> StorageType	= STORAGE_LONG;

							LTK_GetStorage(Node);

							break;

						case LA_ULONG:

							Node -> Storage		= (APTR)TagItem -> ti_Data;
							Node -> StorageType	= STORAGE_ULONG;

							LTK_GetStorage(Node);

							break;

						case LA_STRPTR:

							Node -> Storage		= (APTR)TagItem -> ti_Data;
							Node -> StorageType	= STORAGE_STRPTR;

							LTK_GetStorage(Node);

							break;

						case LAXB_FullSize:

							if(Type == XBAR_KIND || Type == YBAR_KIND)
								Node -> Special . Bar . FullSize = TagItem -> ti_Data;

							break;

						case LA_FirstLabel:

							FirstLabel = TagItem -> ti_Data;
							break;

						case LA_LastLabel:

							LastLabel = TagItem -> ti_Data;
							break;

						case LA_LastGadget:

							if(Type == INTEGER_KIND)
								Node -> Special . Integer . LastGadget = TagItem -> ti_Data;
							else
							{
								if(Type == FRACTION_KIND || Type == STRING_KIND || Type == PASSWORD_KIND)
									Node -> Special . String . LastGadget = TagItem -> ti_Data;
							}

							break;

						case LA_HistoryLines:

							if(Type == INTEGER_KIND)
								Node -> Special . Integer . MaxHistoryLines = TagItem -> ti_Data;
							else
							{
								if(Type == FRACTION_KIND || Type == STRING_KIND || Type == PASSWORD_KIND)
									Node -> Special . String . MaxHistoryLines = TagItem -> ti_Data;
							}

							break;

						case LA_HistoryHook:

							if(Type == INTEGER_KIND)
								Node -> Special . Integer . HistoryHook = (struct Hook *)TagItem -> ti_Data;
							else
							{
								if(Type == FRACTION_KIND || Type == STRING_KIND)
									Node -> Special . String . HistoryHook = (struct Hook *)TagItem -> ti_Data;
							}

							break;

						case LAIN_IncrementerHook:

							if(Type == INTEGER_KIND)
								Node -> Special . Integer . IncrementerHook = (struct Hook *)TagItem -> ti_Data;

							break;

						case LA_Min:

							if(Type == INTEGER_KIND)
								Node -> Special . Integer . Min = TagItem -> ti_Data;
#ifdef DO_FRACTION_KIND
							else
							{
								if(Type == FRACTION_KIND)
									Node -> Special . String . Min = TagItem -> ti_Data;
							}
#endif
							break;

						case LA_Max:

							if(Type == INTEGER_KIND)
								Node -> Special . Integer . Max = TagItem -> ti_Data;
#ifdef DO_FRACTION_KIND
							else
							{
								if(Type == FRACTION_KIND)
									Node -> Special . String . Max = TagItem -> ti_Data;
							}
#endif
							break;

						case LABX_ReserveSpace:

							if(Type == BOX_KIND)
								Node -> Special . Box . ReserveSpace = TagItem -> ti_Data;

							break;

						case LA_Chars:

							Node -> Chars = TagItem -> ti_Data;

							break;

						case LA_LabelPlace:

							Node -> LabelPlace = TagItem -> ti_Data;

							if(Type == MX_KIND)
							{
								if(TagItem -> ti_Data == PLACE_RIGHT)
									Node -> LabelPlace = PLACE_RIGHT;
								else
									Node -> LabelPlace = PLACE_LEFT;
							}

							break;

						case LA_ExtraSpace:

							Node -> ExtraSpace = (WORD)TagItem -> ti_Data;

							if(Node -> ExtraSpace == 1)
								Node -> ExtraSpace = 3;

							break;

						case LA_Center:

							Node -> Centered = TagItem -> ti_Data;
							break;

						case LA_PageSelector:

							Node -> PageSelector = TagItem -> ti_Data;
							break;

						case LABT_ExtraFat:

							if(Node -> Type == BUTTON_KIND)
								Node -> Special . Button . ExtraFat = TagItem -> ti_Data;

							break;
#ifdef DO_PICKSHORTCUTS
						case LA_NoKey:

							Node -> NoKey = TagItem -> ti_Data;
							break;
#endif
						case LA_HighLabel:

							Node -> HighLabel = TagItem -> ti_Data;
							break;

						case LAPA_SmallPalette:

							if(Type == PALETTE_KIND)
								Node -> Special . Palette . SmallPalette = TagItem -> ti_Data;

							break;

						case LA_Lines:

							if(Type == BOX_KIND)
							{
								LONG Count = TagItem -> ti_Data;

								if(Node -> Lines != Count)
								{
									if(Node -> Special . Box . Lines)
									{
										LTK_Free(Handle,Node -> Special . Box . Lines,sizeof(STRPTR) * Node -> Lines);

										Node -> Special . Box . Lines = NULL;
									}
								}

								if(!Node -> Special . Box . Lines)
								{
									if(Node -> Special . Box . Lines = (STRPTR *)LTK_Alloc(Handle,sizeof(STRPTR) * Count))
									{
										LONG i;

										for(i = 0 ; i < Count ; i++)
											Node -> Special . Box . Lines[i] = "";
									}
									else
										Handle -> Failed = TRUE;
								}
							}

							Node -> Lines = TagItem -> ti_Data;

							break;

						case LA_Picker:

							if(Type == TEXT_KIND)
								Node -> Special . Text . UsePicker = TagItem -> ti_Data;

							if(Type == STRING_KIND)
								Node -> Special . String . UsePicker = TagItem -> ti_Data;

							break;

						case LAGR_Spread:

							if(Type == GROUP_KIND)
								Node -> Special . Group . Spread = TagItem -> ti_Data;

							break;

						case LAGR_SameSize:

							if(Type == GROUP_KIND)
								Node -> Special . Group . SameSize = TagItem -> ti_Data;

							break;

						case LAGR_LastAttributes:

							if(Type == GROUP_KIND)
								Node -> Special . Group . LastAttributes = TagItem -> ti_Data;

							break;

						case LAGR_ActivePage:

							if(Type == GROUP_KIND)
							{
								Node -> Special . Group . ActivePage	= TagItem -> ti_Data;
								Node -> Special . Group . Paging	= TRUE;
							}

							break;

						case LABT_ReturnKey:

							if(Type == BUTTON_KIND)
								Node -> Special . Button . ReturnKey = TagItem -> ti_Data;

							break;

						case LABT_EscKey:

							if(Type == BUTTON_KIND)
								Node -> Special . Button . EscKey = TagItem -> ti_Data;

							break;

						case LALV_CursorKey:

							if(Type == LISTVIEW_KIND)
								Node -> Special . List . CursorKey = TagItem -> ti_Data;

							break;

						case LALV_MaxGrowX:

							if(Type == LISTVIEW_KIND)
							{
								Handle -> GrowView = Node;

								Node -> Special . List . MaxGrowX = TagItem -> ti_Data;
							}

							break;

						case LALV_MaxGrowY:

							if(Type == LISTVIEW_KIND)
							{
								Handle -> GrowView = Node;

								Node -> Special . List . MaxGrowY = TagItem -> ti_Data;
							}

							break;

						case LALV_Labels:

							if(Type == LISTVIEW_KIND)
							{
								STRPTR *Labels = (STRPTR *)TagItem -> ti_Data;

								if(*Labels)
								{
									struct List *SomeList;

									if(SomeList = LTK_Alloc(Handle,sizeof(struct List)))
									{
										struct Node *SomeNode;

										NewList(SomeList);

										Node -> Special . List . Labels = SomeList;

										while(*Labels)
										{
											if(SomeNode = LTK_Alloc(Handle,sizeof(struct Node) + strlen(*Labels) + 1))
											{
												SomeNode -> ln_Name = (STRPTR)(SomeNode + 1);

												strcpy(SomeNode -> ln_Name,*Labels++);

												AddTail(SomeList,SomeNode);
											}
										}
									}
								}
							}

							break;

						case LA_Link:

							if(Type == LISTVIEW_KIND)
								Node -> Special . List . LinkID = TagItem -> ti_Data;

							if(Type == STRING_KIND || Type == FRACTION_KIND)
								Node -> Special . String . LinkID = TagItem -> ti_Data;

							break;

#ifdef DO_TAPEDECK_KIND
						case LATD_ButtonType:

							if(Type == TAPEDECK_KIND)
								Node -> Special . TapeDeck . ButtonType = TagItem -> ti_Data;

							break;

						case LATD_Toggle:

							if(Type == TAPEDECK_KIND)
								Node -> Special . TapeDeck . Toggle = TagItem -> ti_Data;

							break;

						case LATD_Pressed:

							if(Type == TAPEDECK_KIND)
								Node -> Current = TagItem -> ti_Data;

							break;

						case LATD_Smaller:

							if(Type == TAPEDECK_KIND)
								Node -> Special . TapeDeck . Smaller = TagItem -> ti_Data;

							break;
#endif	/* DO_TAPEDECK_KIND */

#ifdef DO_GAUGE_KIND
						case LAGA_Percent:

							if(Type == GAUGE_KIND)
							{
								LONG Percent = (LONG)TagItem -> ti_Data;

								if(Percent <= 0)
									Node -> Current = 0;
								else
								{
									if(Percent > 100)
										Node -> Current = 100;
									else
										Node -> Current = Percent;
								}
							}

							break;

						case LAGA_InfoLength:

							if(Type == GAUGE_KIND)
							{
								LONG Length = (LONG)TagItem -> ti_Data;

								if(Length > Node -> Special . Gauge . InfoLength)
								{
									STRPTR NewText;

									if(NewText = (STRPTR)AsmAllocPooled(Handle -> Pool,Length + 1,SysBase))
									{
										if(Node -> Special . Gauge . InfoText)
										{
											strcpy(NewText,Node -> Special . Gauge . InfoText);

											AsmFreePooled(Handle -> Pool,Node -> Special . Gauge . InfoText,Node -> Special . Gauge . InfoLength + 1,SysBase);
										}

										NewText[0] = 0;

										Node -> Special . Gauge . InfoText	= NewText;
										Node -> Special . Gauge . InfoLength	= Length;
									}
									else
										Handle -> Failed = TRUE;
								}
							}

							break;

						case LAGA_InfoText:

							if(Type == GAUGE_KIND)
							{
								STRPTR	SomeText	= (STRPTR)TagItem -> ti_Data;
								LONG	Len		= strlen(SomeText);

								if(!Node -> Special . Gauge . InfoLength)
								{
									if(Node -> Special . Gauge . InfoText = (STRPTR)AsmAllocPooled(Handle -> Pool,Len + 1,SysBase))
										Node -> Special . Gauge . InfoLength = Len;
									else
										Handle -> Failed = TRUE;
								}

								if(Node -> Special . Gauge . InfoLength)
								{
									if(Len > Node -> Special . Gauge . InfoLength)
										Len = Node -> Special . Gauge . InfoLength;

									CopyMem(SomeText,Node -> Special . Gauge . InfoText,Len);

									Node -> Special . Gauge . InfoText[Len] = 0;
								}
							}

							break;
#endif
						case LALV_ExtraLabels:

							if(Type == LISTVIEW_KIND)
								Node -> Special . List . ExtraLabels = (STRPTR *)TagItem -> ti_Data;

							break;

						case LAFR_InnerWidth:

							if(Type == FRAME_KIND)
								Node -> Special . Frame . InnerWidth = TagItem -> ti_Data;

							break;

						case LAFR_InnerHeight:

							if(Type == FRAME_KIND)
								Node -> Special . Frame . InnerHeight = TagItem -> ti_Data;

							break;

						case LA_DrawBox:

							if(Type == FRAME_KIND)
								Node -> Special . Frame . DrawBox = TagItem -> ti_Data;

							if(Type == BOX_KIND)
								Node -> Special . Box . DrawBox = TagItem -> ti_Data;

							break;

						case LABX_AlignText:

							if(Type == BOX_KIND)
								Node -> Special . Box . AlignText = TagItem -> ti_Data;

							break;

						case LABX_Labels:

							if(Type == BOX_KIND)
							{
								STRPTR	*Labels	= (STRPTR *)TagItem -> ti_Data;
								LONG	 Count	= 0;

								while(Labels[Count])
									Count++;

								if(Node -> Lines != Count)
								{
									if(Node -> Special . Box . Lines)
									{
										LTK_Free(Handle,Node -> Special . Box . Lines,sizeof(STRPTR) * Node -> Lines);

										Node -> Special . Box . Lines = NULL;
									}
								}

								if(Count)
								{
									if(!Node -> Special . Box . Lines)
									{
										if(Node -> Special . Box . Lines = LTK_Alloc(Handle,sizeof(STRPTR) * Count))
										{
											LONG i;

											for(i = 0 ; i < Count ; i++)
												Node -> Special . Box . Lines[i] = "";
										}
									}
								}

								Node -> Lines = Count;

								Node -> Special . Box . Labels = Labels;
							}

							break;

						case LABX_Lines:

							if(Type == BOX_KIND)
							{
								STRPTR	*Lines = (STRPTR *)TagItem -> ti_Data;
								LONG	 Count = 0;

								while(Lines[Count])
									Count++;

								if(Node -> Lines && Count > Node -> Lines)
									Count = Node -> Lines;

								if(Count)
								{
									if(!Node -> Special . Box . Lines)
										Node -> Special . Box . Lines = LTK_Alloc(Handle,sizeof(STRPTR) * Count);

									if(Node -> Special . Box . Lines)
									{
										LONG i;

										for(i = 0 ; i < Count ; i++)
											Node -> Special . Box . Lines[i] = Lines[i];
									}
								}

								Node -> Lines = Count;
							}

							break;

						case GA_Disabled:

							Node -> Disabled = TagItem -> ti_Data;
							break;

						case GTCB_Checked:

							if(Type == CHECKBOX_KIND)
								Node -> Current = TagItem -> ti_Data;

							break;

						case GTLV_Labels:

							if(Type == LISTVIEW_KIND)
								Node -> Special . List . Labels = (struct List *)TagItem -> ti_Data;

							break;

						case GTLV_ReadOnly:

							if(Type == LISTVIEW_KIND)
								Node -> Special . List . ReadOnly = TagItem -> ti_Data;

							break;

						case GTLV_MakeVisible:
						case GTLV_Selected:

							if(Type == LISTVIEW_KIND)
								Node -> Current = (LONG)TagItem -> ti_Data;

							break;

						case GTLV_CallBack:

							if(Type == LISTVIEW_KIND)
								Node -> Special . List . CallBack = (struct Hook *)TagItem -> ti_Data;

							break;

						case GTLV_MaxPen:

							if(Type == LISTVIEW_KIND)
								Node -> Special . List . MaxPen = TagItem -> ti_Data;

							break;

						case GTMX_Labels:

							if(Type == MX_KIND)
								Node -> Special . Radio . Choices = (STRPTR *)TagItem -> ti_Data;

							break;

						case GTMX_Active:

							if(Type == MX_KIND)
								Node -> Current = TagItem -> ti_Data;

							break;

						case GTMX_TitlePlace:

							if(Type == MX_KIND && Label)
							{
								if(TagItem -> ti_Data == PLACE_RIGHT)
									Node -> Special . Radio . TitlePlace = PLACETEXT_RIGHT;
								else
									Node -> Special . Radio . TitlePlace = PLACETEXT_LEFT;
							}

							break;

						case GTTX_Text:

							if(Type == TEXT_KIND)
								Node -> Special . Text . Text = (STRPTR)TagItem -> ti_Data;

							break;

						case GTTX_CopyText:

							if(Type == TEXT_KIND)
								Node -> Special . Text . CopyText = TagItem -> ti_Data;

							break;

						case GTTX_Border:

							if(Type == TEXT_KIND)
								Node -> Special . Text . Border = TagItem -> ti_Data;

							break;

						case GTNM_Number:

							if(Type == NUMBER_KIND)
								Node -> Special . Number . Number = TagItem -> ti_Data;

							break;

						case GTNM_Border:

							if(Type == NUMBER_KIND)
								Node -> Special . Number . Border = TagItem -> ti_Data;

							break;

						case GTTX_Justification:

							if(Type == NUMBER_KIND)
								Node -> Special . Number . Justification = TagItem -> ti_Data;

							if(Type == TEXT_KIND)
								Node -> Special . Text . Justification = TagItem -> ti_Data;

							break;

						case GTCY_Labels:

							if(Type == CYCLE_KIND)
								Node -> Special . Cycle . Choices = (STRPTR *)TagItem -> ti_Data;

							break;

						case GTCY_Active:

							if(Type == CYCLE_KIND)
								Node -> Current = TagItem -> ti_Data;

							break;

						case GTPA_Depth:

							if(Type == PALETTE_KIND)
							{
								Node -> Special . Palette . Depth = TagItem -> ti_Data;

								if(Handle -> MaxPen < (1L << Node -> Special . Palette . Depth) - 1)
									Handle -> MaxPen = (1L << Node -> Special . Palette . Depth) - 1;
							}

							break;

						case GTPA_Color:

							if(Type == PALETTE_KIND)
								Node -> Current = TagItem -> ti_Data;

							break;

						case GTPA_ColorOffset:

							if(Type == PALETTE_KIND)
								Node -> Min = TagItem -> ti_Data;

							break;

						case GTPA_NumColors:

							if(Type == PALETTE_KIND)
							{
								Node -> Special . Palette . NumColours = TagItem -> ti_Data;

								if(Node -> Special . Palette . ColourTable)
								{
									LONG i;

									for(i = 0 ; i < Node -> Special . Palette . NumColours ; i++)
									{
										if(Node -> Special . Palette . ColourTable[i] > Handle -> MaxPen)
											Handle -> MaxPen = Node -> Special . Palette . ColourTable[i];
									}
								}
							}

							break;

						case GTPA_ColorTable:

							if(Type == PALETTE_KIND)
							{
								Node -> Special . Palette . ColourTable = (UBYTE *)TagItem -> ti_Data;

								if(Node -> Special . Palette . NumColours)
								{
									LONG i;

									for(i = 0 ; i < Node -> Special . Palette . NumColours ; i++)
									{
										if(Node -> Special . Palette . ColourTable[i] > Handle -> MaxPen)
											Handle -> MaxPen = Node -> Special . Palette . ColourTable[i];
									}
								}
							}

							break;

						case GTSC_Top:

							if(Type == SCROLLER_KIND)
								Node -> Current = TagItem -> ti_Data;

							break;

						case GTSC_Total:

							if(Type == SCROLLER_KIND)
								Node -> Max = TagItem -> ti_Data;

							break;

						case GTSC_Visible:

							if(Type == SCROLLER_KIND)
								Node -> Special . Scroller . Visible = TagItem -> ti_Data;

							break;

						case GTSC_Arrows:

							if(Type == SCROLLER_KIND)
								Node -> Special . Scroller . Arrows = (TagItem -> ti_Data != 0);

							break;

						case LASC_Thin:

							if(Type == SCROLLER_KIND)
								Node -> Special . Scroller . Thin = TagItem -> ti_Data;

							break;

						case PGA_Freedom:

							if(Type == SCROLLER_KIND)
								Node -> Special . Scroller . Vertical = (TagItem -> ti_Data == LORIENT_VERT);

							break;

						case GTSL_Min:

							if(Type == SLIDER_KIND)
								Node -> Min = TagItem -> ti_Data;

							break;

						case GTSL_Max:

							if(Type == SLIDER_KIND)
								Node -> Max = TagItem -> ti_Data;

							break;

						case GTSL_Level:

							if(Type == SLIDER_KIND)
								Node -> Current = TagItem -> ti_Data;

							break;

						case GTSL_LevelFormat:

							if(Type == SLIDER_KIND)
								Node -> Special . Slider . LevelFormat = (STRPTR)TagItem -> ti_Data;

							break;

						case GTSL_LevelPlace:

							if(Type == SLIDER_KIND)
							{
								if(TagItem -> ti_Data == PLACE_RIGHT)
									Node -> Special . Slider . LevelPlace = PLACETEXT_RIGHT;
								else
									Node -> Special . Slider . LevelPlace = PLACETEXT_LEFT;
							}

							break;

						case GTSL_DispFunc:

							if(Type == SLIDER_KIND)
							{
								APTR p = (APTR)TagItem -> ti_Data;

								Node -> Special . Slider . DispFunc = (DISPFUNC)p;
							}

							break;

						case LASL_FullCheck:

							if(Type == SLIDER_KIND)
								Node -> Special . Slider . FullLevelCheck = TagItem -> ti_Data;

							break;

						case GTST_String:

							if(Type == STRING_KIND || Type == PASSWORD_KIND)
								Node -> Special . String . String = (STRPTR)TagItem -> ti_Data;

							break;

						case GTST_MaxChars:

							if(Type == STRING_KIND || Type == PASSWORD_KIND)
								Node -> Special . String . MaxChars = TagItem -> ti_Data;

							break;

						case GTST_EditHook:

							if(Type == STRING_KIND)
								Node -> Special . String . EditHook = (struct Hook *)TagItem -> ti_Data;
							else
							{
								if(Type == INTEGER_KIND)
									Node -> Special . Integer . EditHook = (struct Hook *)TagItem -> ti_Data;
							}

							break;

						case STRINGA_Justification:

							if(Type == STRING_KIND || Type == FRACTION_KIND || Type == PASSWORD_KIND)
								Node -> Special . String . Justification = TagItem -> ti_Data;
							else
							{
								if(Type == INTEGER_KIND)
									Node -> Special . Integer . Justification = TagItem -> ti_Data;
							}

							break;
#ifdef DO_FRACTION_KIND
						case LAFC_MaxChars:

							if(Type == FRACTION_KIND)
								Node -> Special . String . MaxChars = TagItem -> ti_Data;

							break;

						case LAFC_Number:

							if(Type == FRACTION_KIND)
								Node -> Current = TagItem -> ti_Data;

							break;
#endif
						case LAIN_UseIncrementers:

							if(Type == INTEGER_KIND)
							{
								if(Node -> Special . Integer . UseIncrementers = TagItem -> ti_Data)
									Handle -> IDCMP |= IDCMP_INTUITICKS;
							}

							break;

						case GTIN_Number:

							if(Type == INTEGER_KIND)
								Node -> Special . Integer . Number = TagItem -> ti_Data;

							break;

						case GTIN_MaxChars:

							if(Type == INTEGER_KIND)
								Node -> Special . Integer . MaxChars = TagItem -> ti_Data;

							break;
					}
				}
			}

			if(Handle -> Failed)
				return;

			if((Type == FRACTION_KIND || Type == STRING_KIND || Type == INTEGER_KIND) && Node -> Special . String . MaxHistoryLines)
			{
				if(!Node -> Special . Integer . HistoryHook)
				{
					struct Hook *Hook;

					if(Hook = (struct Hook *)LTK_Alloc(Handle,sizeof(struct Hook) + sizeof(struct MinList)))
					{
						struct MinList *List;

						List = Hook -> h_Data = (APTR)(Hook + 1);

						NewList((struct List *)List);

						Hook -> h_Entry = (HOOKFUNC)LTK_DefaultHistoryHook;

						Node -> Special . Integer . HistoryHook = Hook;
					}
				}

				if(Node -> Special . Integer . HistoryHook)
				{
					struct Node	*TextNode;
					struct MinList	*List = (struct List *)Node -> Special . Integer . HistoryHook -> h_Data;

					Node -> Special . Integer . LayoutHandle	= Handle;
					Node -> Special . Integer . NumHistoryLines	= 0;

					TextNode = (struct Node *)List -> mlh_Head;

					while(TextNode -> ln_Succ)
					{
						Node -> Special . Integer . NumHistoryLines++;

						TextNode = TextNode -> ln_Succ;
					}
				}
			}

			if(Type == TEXT_KIND && Node -> Special . Text . CopyText && Node -> Special . Text . Text)
			{
				LONG	len;
				STRPTR	text;

				len = strlen(Node -> Special . Text . Text);

				if(text = LTK_Alloc(Handle,len + 1))
				{
					strcpy(text,Node -> Special . Text . Text);

					Node -> Special . Text . Text = text;
				}
			}

			if(FirstLabel != -1 && LastLabel != -1)
			{
				if(Type == MX_KIND || Type == CYCLE_KIND || Type == LISTVIEW_KIND || Type == BOX_KIND)
				{
					if(!Handle -> LocaleHook)
						Handle -> Failed = TRUE;
					else
					{
						if(Type == LISTVIEW_KIND)
						{
							struct List *SomeList;

							if(SomeList = (struct List *)LTK_Alloc(Handle,sizeof(struct List)))
							{
								struct Node	*SomeNode;
								LONG		 Count = (LastLabel - FirstLabel + 1),i;

								NewList(SomeList);

								Node -> Special . List . Labels = SomeList;

								for(i = 0 ; i < Count ; i++)
								{
									if(SomeNode = LTK_Alloc(Handle,sizeof(struct Node)))
									{
										SomeNode -> ln_Name = (STRPTR)CallHookPkt(Handle -> LocaleHook,Handle,(APTR)(FirstLabel + i));

										AddTail(SomeList,SomeNode);
									}
								}
							}
						}
						else
						{
							STRPTR	*Labels;
							LONG	 Count = (LastLabel - FirstLabel + 1),i;

							if(Labels = LTK_Alloc(Handle,sizeof(STRPTR) * (Count + 1)))
							{
								for(i = 0 ; i < Count ; i++)
									Labels[i] = (STRPTR)CallHookPkt(Handle -> LocaleHook,Handle,(APTR)(FirstLabel + i));

								Labels[i] = NULL;

								if(Type == MX_KIND)
									Node -> Special . Radio . Choices = Labels;
								else
								{
									if(Type == CYCLE_KIND)
										Node -> Special . Cycle . Choices = Labels;
									else
									{
										if(Type == BOX_KIND)
										{
											if(!Node -> Special . Box . Lines)
											{
												if(Node -> Special . Box . Lines = LTK_Alloc(Handle,sizeof(STRPTR) * Count))
												{
													LONG i;

													for(i = 0 ; i < Count ; i++)
														Node -> Special . Box . Lines[i] = "";
												}
											}

											Node -> Lines = Count;

											Node -> Special . Box . Labels = Labels;
										}
									}
								}
							}
						}
					}
				}
			}

			if(Type == BOX_KIND && Node -> Special . Box . ReserveSpace)
			{
				LONG	Size;
				STRPTR	Buffer;

				if(!Node -> Special . Box . Lines)
				{
					if(Node -> Special . Box . Lines = LTK_Alloc(Handle,sizeof(STRPTR) * Node -> Lines))
					{
						LONG i;

						for(i = 0 ; i < Node -> Lines ; i++)
							Node -> Special . Box . Lines[i] = "";
					}
				}

				if(!Node -> Chars)
					Size = 10;
				else
					Size = Node -> Chars;

				Node -> Special . Box . MaxSize = Size;

				if(Buffer = (STRPTR)LTK_Alloc(Handle,(Size + 1) * Node -> Lines))
				{
					LONG i,Len;

					for(i = 0 ; i < Node -> Lines ; i++)
					{
						if(Node -> Special . Box . Lines[i])
						{
							Len = strlen(Node -> Special . Box . Lines[i]);

							if(Len > Size)
								Len = Size;

							CopyMem(Node -> Special . Box . Lines[i],Buffer,Len);

							Buffer[Len] = 0;
						}
						else
							Buffer[0] = 0;

						Node -> Special . Box . Lines[i] = Buffer;

						Buffer += Size + 1;
					}
				}
			}

			if(Handle -> Failed)
				return;

			if(ParentList)
			{
#ifdef DO_PASSWORD_KIND
				if(Type == PASSWORD_KIND)
				{
					STRPTR Buffer;

					if(!(Buffer = LTK_Alloc(Handle,2 * (Node -> Special . String . MaxChars + 1))))
						return;

					Node -> Special . String . RealString	= Buffer;
					Node -> Special . String . Original	= Buffer + Node -> Special . String . MaxChars + 1;

					if(Node -> Special . String . String)
					{
						strcpy(Node -> Special . String . RealString,	Node -> Special . String . String);
						strcpy(Node -> Special . String . Original,	Node -> Special . String . String);
					}
				}
#endif
				AddTail((struct List *)ParentList,(struct Node *)Node);

				if(Type == XBAR_KIND || Type == YBAR_KIND)
					Node -> Special . Bar . Parent = Handle -> CurrentGroup;
			}
		}
	}
}


/*****************************************************************************/


VOID __stdargs
LT_AddL(LayoutHandle *handle,UBYTE type,ULONG labelID,LONG id,...)
{
	if(handle)
	{
		struct TagItem	*tagList;
		va_list		 varArg;

		va_start(varArg,id);

		tagList = (struct TagItem *)varArg;

		if(handle -> LocaleHook)
		{
			STRPTR label;

			label = (STRPTR)CallHookPkt(handle -> LocaleHook,handle,(APTR)labelID);

			LT_AddA(handle,type,label,id,tagList);
		}
		else
			handle -> Failed = TRUE;

		va_end(varArgs);
	}
}


/*****************************************************************************/


VOID __stdargs
LT_New(LayoutHandle *handle,...)
{
	va_list VarArgs;

	va_start(VarArgs,handle);
	LT_NewA(handle,(struct TagItem *)VarArgs);
	va_end(VarArgs);
}

VOID LIBENT
LT_NewA(REG(a0) LayoutHandle *handle,REG(a1) struct TagItem *tagList)
{
	if(handle)
	{
		struct TagItem	*tag;
		UBYTE		 type;
		STRPTR		 label;
		ULONG		 id;

		if(tag = FindTagItem(LA_Type,tagList))
		{
			type = tag -> ti_Data;

			if(type == END_KIND)
			{
				handle -> CurrentGroup = handle -> CurrentGroup -> Special . Group . ParentGroup;

				return;
			}

			if(tag = FindTagItem(LA_ID,tagList))
				id = tag -> ti_Data;
/*			else*/
/*				id = handle -> MaxID;*/

			if(tag = FindTagItem(LA_LabelID,tagList))
			{
				if(handle -> LocaleHook)
					label = (STRPTR)CallHookPkt(handle -> LocaleHook,handle,(APTR)tag -> ti_Data);
				else
					handle -> Failed = TRUE;
			}
			else
				label = (STRPTR)GetTagData(LA_LabelText,NULL,tagList);

			LT_AddA(handle,type,label,id,tagList);
		}
		else
			handle -> Failed = TRUE;
	}
}


/*****************************************************************************/


VOID LIBENT
LT_EndGroup(REG(a0) LayoutHandle *handle)
{
	LT_AddA(handle,END_KIND,NULL,0,NULL);
}


/*****************************************************************************/


struct Window * __stdargs
LT_Layout(LayoutHandle *handle, STRPTR title,struct IBox *bounds,LONG extraWidth, LONG extraHeight,ULONG IDCMP, UBYTE align, ...)
{
	struct Window	*Result;
	va_list		 VarArgs;

	va_start(VarArgs,align);
	Result = LT_LayoutA(handle,title,bounds,extraWidth,extraHeight,IDCMP,align,(struct TagItem *)VarArgs);
	va_end(VarArgs);

	return(Result);
}

struct Window * LIBENT
LT_LayoutA(REG(a0) LayoutHandle *handle, REG(a1) STRPTR title,REG(a2) struct IBox *bounds,REG(d0) LONG extraWidth, REG(d1) LONG extraHeight,REG(d2) ULONG IDCMP, REG(d3) UBYTE align, REG(a3) struct TagItem *TagParams)
{
	LONG		 left, top, width, height;
	struct IBox	 newBounds;
	struct Menu	*menu;
	struct TagItem	*item;
	LONG		 placeLeft;
	LONG		 placeTop;
	struct IBox	 zoomBox;
	struct IBox	*zoom;
	LONG		 i;
	struct TextFont	*font;
	struct TagItem	*NewTags = NULL,*Tags;
	BOOLEAN		 FillBack = FALSE,BlockParent = handle -> BlockParent;
	struct Window	*Parent = handle -> Parent;

	if(!handle)
		return(NULL);

	if(!bounds)
	{
		LTK_GetDisplayClip(handle -> Screen,&newBounds . Left,&newBounds . Top,&newBounds . Width,&newBounds . Height);

		bounds = &newBounds;
	}

	if(title)
		top = handle -> Screen -> WBorTop + handle -> Screen -> Font -> ta_YSize + 1 + handle -> InterHeight;
	else
		top = handle -> Screen -> WBorTop + handle -> InterHeight;

	left = handle -> Screen -> WBorLeft + handle -> InterWidth;

	LTK_CreateGadgets(handle,bounds,left,top,left + handle -> InterWidth + handle -> Screen -> WBorRight,top + handle -> InterHeight + handle -> Screen -> WBorBottom);

	if(handle -> Failed)
		return(NULL);

	width	= left + handle -> TopGroup -> Width + handle -> InterWidth + handle -> Screen -> WBorRight;
	height	= top + handle -> TopGroup -> Height + handle -> InterHeight + handle -> Screen -> WBorBottom;

	if(align & ALIGNF_LEFT)
		placeLeft = 0;
	else
	{
		if(align & ALIGNF_RIGHT)
			placeLeft = bounds -> Width - (width + extraWidth);
		else
			placeLeft = (bounds -> Width - (width + extraWidth)) / 2;
	}

	if(align & ALIGNF_TOP)
		placeTop = 0;
	else
	{
		if(align & ALIGNF_BOTTOM)
			placeTop = bounds -> Height - (height + extraHeight);
		else
			placeTop = (bounds -> Height - (height + extraHeight)) / 2;
	}

	if(align & ALIGNF_EXTRA_LEFT)
		placeLeft += extraWidth;
	else
	{
		if(!(align & ALIGNF_EXTRA_RIGHT))
			placeLeft += extraWidth / 2;
	}

	if(align & ALIGNF_EXTRA_TOP)
		placeTop += extraHeight;
	else
	{
		if(!(align & ALIGNF_EXTRA_BOTTOM))
			placeTop += extraHeight / 2;
	}

	placeLeft	+= bounds -> Left;
	placeTop	+= bounds -> Top;

	if(placeLeft < 0)
		placeLeft = 0;

	if(placeTop < 0)
		placeTop = 0;

	menu = NULL;
	zoom = NULL;

	handle -> AutoRefresh = TRUE;
#ifdef DO_CLONING
	if(handle -> CloneExtra)
	{
		placeLeft	= 0;
		placeTop	= handle -> CloneExtra -> Screen -> BarHeight + 1;

		zoomBox . Left	= placeLeft;
		zoomBox . Top	= placeTop;
	}
#endif	/* DO_CLONING */

	/* ALWAYS */
	{
		struct TagItem *TagList = (struct TagItem *)TagParams;

		while(item = NextTagItem(&TagList))
		{
			switch((ULONG)item -> ti_Tag)
			{
				case LA_Menu:

					handle -> IDCMP |= IDCMP_MENUPICK;

					menu = (struct Menu *)item -> ti_Data;

					break;

				case LAWN_UserPort:

					handle -> MsgPort = (struct MsgPort *)item -> ti_Data;

					break;

				case LAWN_HelpHook:

					handle -> HelpHook = (struct Hook *)item -> ti_Data;

					break;

				case LAWN_Left:

					placeLeft = item -> ti_Data;

					break;

				case LAWN_Top:

					placeTop = item -> ti_Data;

					break;

				case LAWN_Parent:

					Parent = (struct Window *)item -> ti_Data;
					break;

				case LAWN_BlockParent:

					BlockParent = item -> ti_Data;
					break;

				case LAWN_BelowMouse:

					if((placeLeft = handle -> Screen -> MouseX - (width / 2)) < 0)
						placeLeft = 0;

					if((placeTop = handle -> Screen -> MouseY - (height / 2)) < 0)
						placeTop = 0;

					break;

				case LAWN_Zoom:

					if(item -> ti_Data)
					{
						if(V39)
						{
							zoomBox . Left	= -1;
							zoomBox . Top	= -1;
						}
						else
						{
							zoomBox . Left	= placeLeft;
							zoomBox . Top	= placeTop;
						}

						zoomBox . Width		= width;
						zoomBox . Height	= handle -> Screen -> WBorTop + handle -> Screen -> Font -> ta_YSize + 1;

						zoom = &zoomBox;
					}

					break;

				case LAWN_MaxPen:

					handle -> MaxPen = (LONG)item -> ti_Data;
					break;

				case LAWN_MoveToWindow:

					handle -> MoveToWindow = (LONG)item -> ti_Data;
					break;

				case LAWN_AutoRefresh:

					handle -> AutoRefresh = (LONG)item -> ti_Data;
					break;
			}
		}
	}

	Tags = (struct TagItem *)TagParams;

	if(handle -> BackgroundPen && !V39)
	{
		if(NewTags = CloneTagItems(Tags))
		{
			STATIC Tag Filter[] = { WA_SimpleRefresh,TAG_DONE };

			FilterTagItems(NewTags,Filter,TAGFILTER_NOT);

			Tags = NewTags;

			FillBack = TRUE;
		}
		else
			return(NULL);
	}

	if(Parent)
	{
		WORD	Left,Top,Width,Height,MoveLeft,MoveTop,
			WindowLeft,WindowTop,WindowWidth,WindowHeight;

		WindowLeft	= Parent -> LeftEdge + Parent -> BorderLeft;
		WindowTop	= Parent -> TopEdge + Parent -> BorderTop;
		WindowWidth	= Parent -> Width - (Parent -> BorderLeft + Parent -> BorderRight);
		WindowHeight	= Parent -> Height - (Parent ->BorderTop + Parent -> BorderBottom);

		LTK_GetDisplayClip(Parent -> WScreen,&Left,&Top,&Width,&Height);

		if((MoveLeft = WindowLeft + (WindowWidth - width) / 2) < 0)
			MoveLeft = 0;

		if((MoveTop = WindowTop + (WindowHeight - height) / 2) < 0)
			MoveTop = 0;

		if(MoveLeft < Left || MoveLeft + width > Left + Width)
			MoveLeft = -1;

		if(MoveTop < Top || MoveTop + height > Top + Height)
			MoveTop = -1;

		if(MoveTop != -1 && MoveLeft != -1)
		{
			placeLeft	= MoveLeft;
			placeTop	= MoveTop;
		}
	}

	if(BlockParent && Parent)
	{
		LT_LockWindow(Parent);

		handle -> Parent = Parent;
	}

	if(handle -> Window = OpenWindowTags(NULL,
		WA_Left,		placeLeft,
		WA_Top,			placeTop,
		WA_Width,		width,
		WA_Height,		height,
		WA_NewLookMenus,	TRUE,
		WA_CustomScreen,	handle -> Screen,
		WA_BackFill,		&handle -> BackfillHook,

		!FillBack	? WA_Gadgets :		TAG_IGNORE,	handle -> List,
		zoom	? WA_Zoom :			TAG_IGNORE,	zoom,
		!handle -> MsgPort ? WA_IDCMP :		TAG_IGNORE,	IDCMP_REFRESHWINDOW | IDCMP_RAWKEY | IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW | IDCMP_MOUSEBUTTONS | IDCMP | handle -> IDCMP,
		title	? WA_Title :			TAG_IGNORE,	title,
		handle -> AmigaGlyph ? WA_AmigaKey :	TAG_IGNORE,	handle -> AmigaGlyph,
		handle -> CheckGlyph ? WA_Checkmark :	TAG_IGNORE,	handle -> CheckGlyph,
	TAG_MORE,Tags))
	{
		font = handle -> RPort . Font;

		handle -> RPort = *handle -> Window -> RPort;

		LTK_SetFont(handle,font);

		if(NewTags)
		{
			FreeTagItems(NewTags);

			NewTags = NULL;
		}

		if(V39)
		{
			if(handle -> MaxPen > 0)
			{
				SetMaxPen(&handle -> RPort,handle -> MaxPen);
				SetMaxPen(handle -> Window -> RPort,handle -> MaxPen);
			}
		}

		if(FillBack)
	        {
			LTK_SetAPen(handle -> Window -> RPort,handle -> BackgroundPen);
			RectFill(handle -> Window -> RPort,handle -> Window -> BorderLeft,handle -> Window -> BorderTop,handle -> Window -> Width - (handle -> Window -> BorderRight + 1),handle -> Window -> Height - (handle -> Window -> BorderBottom + 1));

			AddGList(handle -> Window,handle -> List,(UWORD)-1,(UWORD)-1,NULL);
			RefreshGList(handle -> List,handle -> Window,NULL,(UWORD)-1);
		}

		GT_RefreshWindow(handle -> Window,NULL);
		LTK_DrawGroup(handle,handle -> TopGroup);

		LTK_MoveToWindow(handle);

		if(handle -> MsgPort)
		{
			handle -> Window -> UserPort = handle -> MsgPort;

			if(!ModifyIDCMP(handle -> Window,IDCMP_REFRESHWINDOW | IDCMP_RAWKEY | IDCMP_INACTIVEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_MOUSEBUTTONS | IDCMP | handle -> IDCMP))
			{
				handle -> Window -> UserPort = NULL;

				return (NULL);
			}
		}

		if(menu)
			SetMenuStrip(handle -> Window,menu);

		handle -> Previous = NULL;

		for(i = 0 ; i < handle -> Count ; i++)
		{
			if(handle -> GadgetArray[i])
			{
				ObjectNode *node;

				if(GETOBJECT(handle -> GadgetArray[i],node))
				{
					if(LIKE_STRING_KIND(node) || (node -> Type == INTEGER_KIND))
					{
						if(!handle -> Previous)
							handle -> Previous = handle -> GadgetArray[i];
					}
				}
			}
		}
#ifdef DO_CLONING
		if(handle -> CloneExtra)
			ScreenToFront(handle -> Window -> WScreen);
#endif
	}
	else
	{
		if(handle -> Parent)
		{
			LT_UnlockWindow(handle -> Parent);

			handle -> Parent = NULL;
		}
	}

	FreeTagItems(NewTags);

	return(handle -> Window);
}


/*****************************************************************************/


struct Menu * __stdargs
LT_LayoutMenus(LayoutHandle *handle,struct NewMenu *menuTemplate,...)
{
	struct Menu	*Result;
	va_list		 VarArgs;

	va_start(VarArgs,menuTemplate);
	Result = LT_LayoutMenusA(handle,menuTemplate,(struct TagItem *)VarArgs);
	va_end(VarArgs);

	return(Result);
}

struct Menu * LIBENT
LT_LayoutMenusA(REG(a0) LayoutHandle *handle,REG(a1) struct NewMenu *menuTemplate,REG(a2) struct TagItem *TagParams)
{
	struct Menu *menu;

	menu = NULL;

	if(handle)
	{
		struct TagItem	*tag;
		LONG		 label;
		BOOLEAN		 foundFirst;
		BOOLEAN		 foundLast;

		foundFirst	= FALSE;
		foundLast	= FALSE;

		if(tag = FindTagItem(LA_FirstLabel,(struct TagItem *)TagParams))
		{
			foundFirst = TRUE;

			label = tag -> ti_Data;
		}

		if(tag = FindTagItem(LA_LastLabel,(struct TagItem *)TagParams))
			foundLast = TRUE;

		if(foundFirst == foundLast)
		{
			struct NewMenu *localTemplate;

			localTemplate = NULL;

			if(foundFirst)
			{
				if(handle -> LocaleHook)
				{
					LONG count;

					count = 0;

					while(menuTemplate[count] . nm_Type != NM_END)
						count++;

					count++;

					if(localTemplate = AllocVec(sizeof(struct NewMenu) * count,MEMF_ANY | MEMF_PUBLIC))
					{
						LONG i;

						CopyMem(menuTemplate,localTemplate,sizeof(struct NewMenu) * count);

						for(i = 0 ; i < count ; i++)
						{
							if(localTemplate[i] . nm_Label != NM_BARLABEL && (localTemplate[i] . nm_Type == NM_TITLE || localTemplate[i] . nm_Type == NM_ITEM || localTemplate[i] . nm_Type == NM_SUB))
							{
								localTemplate[i] . nm_Label = (STRPTR)CallHookPkt(handle -> LocaleHook,handle,(APTR)label);

								label++;
							}
						}
					}
				}

				menuTemplate = localTemplate;
			}

			if(menuTemplate)
			{
				if(menu = CreateMenusA(menuTemplate,NULL))
				{
					if(!LayoutMenus(menu,handle -> VisualInfo,
						GTMN_NewLookMenus,	TRUE,

						handle -> AmigaGlyph ? GTMN_AmigaKey :	TAG_IGNORE,handle -> AmigaGlyph,
						handle -> CheckGlyph ? GTMN_Checkmark :	TAG_IGNORE,handle -> CheckGlyph,
					TAG_DONE))
					{
						FreeMenus(menu);

						menu = NULL;
					}
				}

				if(localTemplate)
					FreeVec(localTemplate);
			}
		}
	}

	return(menu);
}


/*****************************************************************************/


#ifdef DO_FRACTION_KIND
VOID LIBENT
LT_Fixed2String(REG(d0) FIXED fixed,REG(d1) STRPTR buffer)
{
	sprintf(buffer,"%ld%lc%ld",fixed / FIXED_UNITY,LTK_Locale ? LTK_Locale -> loc_DecimalPoint[0] : ' . ',fixed % FIXED_UNITY);
}
#endif


/*****************************************************************************/


#ifdef DO_FRACTION_KIND

FIXED LIBENT
LT_String2Fixed(REG(a0) STRPTR buffer)
{
	UBYTE	localBuffer[20];
	UBYTE	decimalPoint;
	LONG	i,left,right;

	if(LTK_Locale)
		decimalPoint = LTK_Locale -> loc_DecimalPoint[0];
	else
		decimalPoint = '.';

	strcpy(localBuffer,buffer);

	i = 0;

	while(buffer[i])
	{
		if(localBuffer[i] == decimalPoint)
		{
			localBuffer[i] = 0;

			if(i)
				left = Atol(localBuffer);
			else
				left = 0;

			if(localBuffer[i + 1])
			{
				UBYTE rest[4];

				CopyMem(&localBuffer[i + 1],rest,3);

				rest[3] = 0;

				right = Atol(rest);
			}
			else
				right = 0;

			return((FIXED)(left * FIXED_UNITY + right));
		}

		i++;
	}

	left = Atol(localBuffer);

	return((FIXED)(left * FIXED_UNITY));
}
#endif


/*****************************************************************************/


#ifdef DO_FRACTION_KIND
ULONG LIBENT
LT_FixedMult(REG(d0) FIXED fixed,REG(d1) ULONG factor)
{
	return((fixed * factor) / FIXED_UNITY);
}
#endif


/*****************************************************************************/


BOOL
LT_Init()
{
	if(SysBase)
		return(TRUE);
	else
	{
		SysBase = *(struct ExecBase **)4;

		if(SysBase -> LibNode . lib_Version < 37)
		{
			SysBase = NULL;

			return(FALSE);
		}
		else
		{
			V39 = (SysBase -> LibNode . lib_Version >= 39);
			V40 = (SysBase -> LibNode . lib_Version >= 40);

			IntuitionBase	= OpenLibrary("intuition.library",37);
			GfxBase		= (struct GfxBase *)OpenLibrary("graphics.library",37);
			UtilityBase	= OpenLibrary("utility.library",37);
			GadToolsBase	= OpenLibrary("gadtools.library",37);
			KeymapBase	= OpenLibrary("keymap.library",37);

			if(LocaleBase = OpenLibrary("locale.library",38))
				LTK_Locale = OpenLocale(NULL);

			if(IntuitionBase && GfxBase && UtilityBase && GadToolsBase && KeymapBase)
			{
				if(LTK_ImageClass = MakeClass(NULL,IMAGECLASS,NULL,sizeof(ImageInfo),0))
				{
#ifdef DO_PICKSHORTCUTS
					InitSemaphore(&LTK_KeySemaphore);
#endif	/* DO_PICKSHORTCUTS */
					LTK_ImageClass -> cl_Dispatcher . h_Entry = (HOOKFUNC)LTK_ImageDispatch;

					InitSemaphore(&LTK_LockSemaphore);

					NewList(&LTK_LockList);

					return(TRUE);
				}
			}

			LT_Exit();

			return(FALSE);
		}
	}
}


/*****************************************************************************/


VOID
LT_Exit()
{
	if(SysBase)
	{
		if(LTK_ImageClass)
		{
			FreeClass(LTK_ImageClass);

			LTK_ImageClass = NULL;
		}

		if(LTK_Locale)
		{
			CloseLocale(LTK_Locale);

			LTK_Locale = NULL;
		}

		CloseLibrary(LocaleBase);

		LocaleBase = NULL;

		CloseLibrary(KeymapBase);

		KeymapBase = NULL;

		CloseLibrary(GadToolsBase);

		GadToolsBase = NULL;

		CloseLibrary(UtilityBase);

		UtilityBase = NULL;

		CloseLibrary(GfxBase);

		GfxBase = NULL;

		CloseLibrary(IntuitionBase);

		IntuitionBase = NULL;

		SysBase = NULL;
	}
}


/*****************************************************************************/


LONG LIBENT
LT_LabelWidth(REG(a0) LayoutHandle *handle,REG(a1) STRPTR label)
{
	if(handle)
	{
		LONG len;
		LONG cnt;

		cnt = 0;
		len = 0;

		while(label[len])
		{
			if(label[len] == '_')
				cnt += TextLength(&handle -> RPort,"_",1);

			len++;
		}

		return(TextLength(&handle -> RPort,label,len) - cnt);
	}
	else
		return(0);
}


/*****************************************************************************/


LONG LIBENT
LT_LabelChars(REG(a0) LayoutHandle *handle,REG(a1) STRPTR label)
{
	if(handle)
		return((LT_LabelWidth(handle,label) + handle -> GlyphWidth - 1) / handle -> GlyphWidth);
	else
		return(0);
}


/****************************************************************************/


VOID LIBENT
LT_LockWindow(REG(a0) struct Window *window)
{
	if(window)
	{
		LockNode	*lockNode;
		BOOLEAN		 found;

		found = FALSE;

		ObtainSemaphore(&LTK_LockSemaphore);

		SCANLIST(&LTK_LockList,lockNode)
		{
			if(lockNode -> Window == window)
			{
				lockNode -> Count++;

				found = TRUE;

				break;
			}
		}

		if(!found)
		{
			if(lockNode = AllocMem(sizeof(LockNode),MEMF_PUBLIC | MEMF_ANY | MEMF_CLEAR))
			{
				AddTail((struct List *)&LTK_LockList,(struct Node *)lockNode);

				lockNode -> Window		= window;
				lockNode -> OldIDCMPFlags	= window -> IDCMPFlags;
				lockNode -> Count		= 1;
				lockNode -> MinWidth		= window -> MinWidth;
				lockNode -> MaxWidth		= window -> MaxWidth;
				lockNode -> MinHeight		= window -> MinHeight;
				lockNode -> MaxHeight		= window -> MaxHeight;

				WindowLimits(window,window -> Width,window -> Height,window -> Width,window -> Height);

				ModifyIDCMP(window,window -> IDCMPFlags | IDCMP_SIZEVERIFY);

				if(V39)
				{
					SetWindowPointer(window,
						WA_BusyPointer,		TRUE,
						WA_PointerDelay,	TRUE,
					TAG_DONE);
				}
				else
				{
					STATIC UWORD __chip Stopwatch[(2 + 16) * 2] =
					{
						0x0000,0x0000,

						0x0400,0x07C0,
						0x0000,0x07C0,
						0x0100,0x0380,
						0x0000,0x07E0,
						0x07C0,0x1FF8,
						0x1FF0,0x3FEC,
						0x3FF8,0x7FDE,
						0x3FF8,0x7FBE,
						0x7FFC,0xFF7F,
						0x7EFC,0xFFFF,
						0x7FFC,0xFFFF,
						0x3FF8,0x7FFE,
						0x3FF8,0x7FFE,
						0x1FF0,0x3FFC,
						0x07C0,0x1FF8,
						0x0000,0x07E0,

						0x0000,0x0000
					};

					SetPointer(window,Stopwatch,16,16,-6,0);
				}

				Request(&lockNode -> Requester,window);
			}
		}

		ReleaseSemaphore(&LTK_LockSemaphore);
	}
}


/****************************************************************************/


VOID LIBENT
LT_UnlockWindow(REG(a0) struct Window *window)
{
	if(window)
	{
		LockNode *lockNode;

		ObtainSemaphore(&LTK_LockSemaphore);

		SCANLIST(&LTK_LockList,lockNode)
		{
			if(lockNode -> Window == window)
			{
				if(--lockNode -> Count < 1)
				{
					Remove((struct Node *)lockNode);

					WindowLimits(window,lockNode -> MinWidth,lockNode -> MinHeight,lockNode -> MaxWidth,lockNode -> MaxHeight);

					EndRequest(&lockNode -> Requester,window);

					if(V39)
						SetWindowPointerA(window,NULL);
					else
						ClearPointer(window);

					ModifyIDCMP(window,lockNode -> OldIDCMPFlags);

					FreeMem(lockNode,sizeof(LockNode));
				}

				break;
			}
		}

		ReleaseSemaphore(&LTK_LockSemaphore);
	}
}


/*****************************************************************************/


VOID LIBENT
LT_DeleteWindowLock(REG(a0) struct Window *window)
{
	if(window)
	{
		LockNode *lockNode;

		ObtainSemaphore(&LTK_LockSemaphore);

		SCANLIST(&LTK_LockList,lockNode)
		{
			if(lockNode -> Window == window)
			{
				Remove((struct Node *)lockNode);

				EndRequest(&lockNode -> Requester,window);

				if(V39)
					SetWindowPointerA(window,NULL);
				else
					ClearPointer(window);

				ModifyIDCMP(window,lockNode -> OldIDCMPFlags);

				FreeMem(lockNode,sizeof(LockNode));

				break;
			}
		}

		ReleaseSemaphore(&LTK_LockSemaphore);
	}
}


/****************************************************************************/


VOID LIBENT
LT_ShowWindow(REG(a0) LayoutHandle *handle,REG(a1) BOOL activate)
{
	if(handle)
	{
		struct Window *window;

		window = handle -> Window;

		WindowToFront(window);

		if(activate)
			ActivateWindow(window);

		ScreenToFront(window -> WScreen);

		if((handle -> Window -> Flags & (WFLG_HASZOOM | WFLG_ZOOMED)) == (WFLG_HASZOOM | WFLG_ZOOMED))
		{
			WORD i;

			ZipWindow(window);

				// wait for the window to unzoom before calling LTK_MoveToWindow()

			for(i = 0 ; i < 300 ; i++)
			{
				if(!handle -> Window -> Flags & WFLG_ZOOMED)
					break;

				WaitTOF();
			}
		}

		LTK_MoveToWindow(handle);
	}
}


/****************************************************************************/


VOID LIBENT
LT_Activate(REG(a0) LayoutHandle *handle,REG(d0) LONG id)
{
	struct Gadget *gadget;

	if(gadget = LTK_FindGadget(handle,id))
	{
		ObjectNode *node;

		if(GETOBJECT(gadget,node))
		{
			if(!node -> Disabled)
			{
				if(LIKE_STRING_KIND(node) || (node -> Type == INTEGER_KIND))
				{
					handle -> Previous = gadget;

					ActivateGadget(handle -> Previous,handle -> Window,NULL);
				}
			}
		}
	}
}


/****************************************************************************/


VOID LIBENT
LT_PressButton(REG(a0) LayoutHandle *handle,REG(d0) LONG id)
{
	struct Gadget *gadget;

	if(gadget = LTK_FindGadget(handle,id))
	{
		ObjectNode *node;

		if(GETOBJECT(gadget,node))
		{
			if(!node -> Disabled)
				LTK_BlinkButton(handle,gadget);
		}
	}
}


/****************************************************************************/


WORD LIBENT
LT_GetCode(REG(d0) ULONG MsgQualifier,REG(d1) ULONG MsgClass,REG(d2) UWORD MsgCode,REG(a0) struct Gadget *MsgGadget)
{
	if(MsgClass == IDCMP_RAWKEY && !(MsgCode & IECODE_UP_PREFIX) && KeymapBase)
	{
		UBYTE			Buffer[10];
		struct InputEvent	Event;
		LONG			Len;

		Event . ie_NextEvent		= NULL;
		Event . ie_Code			= MsgCode & ~IECODE_UP_PREFIX;
		Event . ie_Qualifier		= MsgQualifier;
		Event . ie_Class		= IECLASS_RAWKEY;
		Event . ie_SubClass		= 0;
		Event . ie_position . ie_addr	= (APTR)MsgGadget;

		Buffer[0] = 0;

		if((Len = MapRawKey(&Event,Buffer,9,NULL)) > 0)
		{
			if(Buffer[0] != 0x9B || Len == 1)
				return((WORD)Buffer[0]);
		}
	}

	return(-1);
}


/****************************************************************************/

#define MESSAGE_COOKIE 0x47138AEB

struct IntuiMessage * LIBENT
LT_GetIMsg(REG(a0) struct LayoutHandle *Handle)
{
	if(Handle)
	{
		struct IntuiMessage *Msg = GT_GetIMsg(Handle -> Window -> UserPort);

		if(Msg)
		{
			struct IntuiMessage *Clone;

			if(Clone = (struct IntuiMessage *)AllocMem(sizeof(struct ExtIntuiMessage),MEMF_ANY))
			{
				CopyMem(Msg,Clone,sizeof(struct ExtIntuiMessage));

				Clone -> ExecMessage . mn_Node . ln_Name	= (char *)MESSAGE_COOKIE;
				Clone -> ExecMessage . mn_Node . ln_Pri		= -114;
				Clone -> ExecMessage . mn_ReplyPort		= (struct MsgPort *)Clone;
				Clone -> SpecialLink				= (struct IntuiMessage *)Clone;

				GT_ReplyIMsg(Msg);

				LT_HandleInput(Handle,Clone -> Qualifier,&Clone -> Class,&Clone -> Code,(struct Gadget **)&Clone -> IAddress);

				return(Clone);
			}
			else
				GT_ReplyIMsg(Msg);
		}
	}

	return(NULL);
}

VOID LIBENT
LT_ReplyIMsg(REG(a0) struct IntuiMessage *Msg)
{
	if(Msg)
	{
		if(Msg -> SpecialLink == (struct IntuiMessage *)Msg && Msg -> ExecMessage . mn_Node . ln_Name == (char *)MESSAGE_COOKIE && Msg -> ExecMessage . mn_Node . ln_Pri == -114 && Msg -> ExecMessage . mn_ReplyPort == (struct MsgPort *)Msg)
			FreeMem(Msg,sizeof(struct ExtIntuiMessage));
		else
			GT_ReplyIMsg(Msg);
	}
}
