/*
**	$Filename: gadlayout/gadlayout.c $
**	$Release: 1.6 $
**	$Revision: 36.22 $
**	$Date: 93/07/04 $
**
**	GadLayout functions, a dynamic gadget layout system.
**
**	(C) Copyright 1992, 1993, Timothy J. Aston
**	All Rights Reserved
**
**  GadLayout is copyright, so you can't go ahead and just use it however you
**  want, there are some restrictions.  GadLayout consists of the C source
**  files, all header files, and all documentation included.  You may not
**  modify GadLayout in any way.  GadLayout exists for you, the software
**  developer, to use it; however, certain conditions must be complied with
**  before you do:
**
**  1. Your documentation must clearly state that it is making use of the
**     GadLayout dynamic gadget layout system by Timothy Aston.
**  2. You must provide me with, free of charge, a copy of the software you use
**     GadLayout in, and any subsequent updates to it that use GadLayout, also
**     free of charge.  This means that if the software you use it in is
**     shareware, I must be considered a full-registered user of the software.
**     Similarly for commercial software, you must provide me with a
**     complimentary copy.  And in all cases, as long as your software continues
**     to use GadLayout, you must provide with all publically released updates.
**     See the end of this document to find out how to get the stuff to me. 
**     I hope this isn't being too unreasonable, I basically just want to see
**     all the software that uses GadLayout.
**  3. If you modify the GadLayout source at all, you must send your
**     modifications to me.  You may not under any circumstances use the
**     GadLayout sources to create any kind of runtime-linking module, such
**     as a standard Amiga shared-library.
**  4. You may not distribute modified GadLayout source, or include GadLayout
**     source in a distribution without permission from the author.
**  5. Any modified versions of GadLayout will fall under this same licensing
**     agreement.
**
**
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <utility/tagitem.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <graphics/clip.h>
#include <graphics/layers.h>
#include <libraries/gadtools.h>
#include <libraries/locale.h>
#include <clib/exec_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/layers_protos.h>
#include <clib/locale_protos.h>
#include <clib/utility_protos.h>
#include "gadlayout.h"


/* #define GL_DEBUG 1 */

#define MAX(a, b)	(a > b) ? a : b
#define MIN(a, b)	(a < b) ? a : b

#define GADLAYOUT_KIND 0xF001
#define GLFLG_READONLY 0x002

/* Local structure used to hold the definitions for the gadgets once they
 * have been laid out.
 */
struct LaidOutGadget
{
	BOOL log_IsEvaluated;
	ULONG log_GadgetKind;
	struct NewGadget log_NewGadget;
};

/* Structure used to hold the application strings, used incase we couldn't
 * get a string from the locale catalog.
 */
struct AppString
{
    LONG as_ID;
    STRPTR as_Str;
};

/* Private Local structure used to hold all gadget information that needs to
 * be freed.
 */
struct PrivateInfo
{
	struct Screen *pi_Screen;
	APTR pi_VisualInfo;
	struct DrawInfo *pi_DrawInfo;
	struct Gadget *pi_GadList;
	struct Catalog *pi_Catalog;
	struct AppString *pi_AppStrings;
	struct Remember *pi_Remember;
};

/* Local function prototypes.
 */
VOID evaluate_gadget(struct PrivateInfo *, WORD, struct LayoutGadget *, struct NewGadget *, struct LaidOutGadget *);
LONG text_width(char *, struct TextAttr *);
STRPTR get_locale_string(struct Catalog *, struct AppString *, LONG);
struct Gadget * create_layout_gad(struct PrivateInfo *, WORD, struct Gadget *, struct NewGadget *, struct TagItem *);
struct Image * create_gad_images(WORD, UWORD, UWORD, struct PrivateInfo *, struct Image *, BOOL);

/* Public function prototypes.  These function definitions may differ slightly
 * from what appears in gadlayout_protos.h (the public definitions) since we
 * must hide private structures by using APTRs.
 */
struct PrivateInfo * LayoutGadgets(struct Gadget **, struct LayoutGadget *, struct Screen *, Tag *, ...);
struct PrivateInfo * LayoutGadgetsA(struct Gadget **gad_list, struct LayoutGadget *g, struct Screen *, struct TagItem *);
VOID GL_SetGadgetAttrs(struct PrivateInfo *, struct Gadget *, struct Window *, struct Requester *, Tag *, ...);
VOID GL_SetGadgetAttrsA(struct PrivateInfo *, struct Gadget *, struct Window *, struct Requester *, struct TagItem *taglist);
WORD GadgetArrayIndex(WORD, struct LayoutGadget *);
VOID FreeLayoutGadgets(struct PrivateInfo *);

extern struct Library *LocaleBase;

/* Data for the drawer image.
 */
__chip static UWORD drawer_data[10] =
{
	0x03C0,
	0x0420,
	0xF810,
	0xFC10,
	0xC3F0,
	0xC010,
	0xC010,
	0xC010,
	0xC010,
	0xFFF0
};

/* Data for the file image.
 */
__chip static UWORD file_data[10] =
{
	0xFF00,
	0xC180,
	0xC140,
	0xC120,
	0xC1F0,
	0xC030,
	0xC030,
	0xC030,
	0xC030,
	0xFFF0
};

struct Image file_image =
{
	0, 0,
	12, 10,
	2,
	file_data,
	0x01, 0x00,
	NULL,
};

/* Data for the drawer image.
 */
struct Image drawer_image =
{
	0, 0,
	12, 10,
	2,
	drawer_data,
	0x01, 0x00,
	NULL,
};


/****** gadlayout/LayoutGadgetsA ********************************************
*
*   NAME
*       LayoutGadgetsA -- Formats an array of GadTools gadgets.
*       LayoutGadgets -- Varargs stub for LayoutGadgetsA().
*
*   SYNOPSIS
*       gad_info = LayoutGadgetsA(gad_list, gadgets, screen, taglist)
*       APTR LayoutGadgetsA(struct Gadget **, struct LayoutGadget *,
*                           struct Screen *, struct TagItem *)
*
*       gad_info = LayoutGadgets(gad_list, gadgets, screen, firsttag, ...)
*       APTR LayoutGadgets(struct Gadget **, struct LayoutGadget *,
*                          struct Screen *, Tag *, ...)
*
*   FUNCTION
*       Creates a laid-out gadget list from a LayoutGadget array, which
*       describes each gadget you want to create.  Gadgets you create
*       can be any of the gadget kinds supported by GadTools, as well
*       as any of the extended gadget kinds provided by GadLayout.
*       Gadgets can easily be defined so that the automatically adjust
*       their sizes and positions to accomodate fonts of any size
*       (including proportional fonts) and also to adapt to different
*       locale strings.  The real power of GadLayout is that allows you
*       to create a gadget layout that dynamically adjusts to different
*       user's environments.
*
*   INPUTS
*       gad_list - Pointer to the gadget list pointer, this will be
*           ready to pass to OpenWindowTags() or AddGList().
*
*       gadgets - An array of LayoutGadget structures.  Each element
*           in the array describes one of the gadgets that you will
*           be creating.  Each LayoutGadget structure in the array
*           should be initialized as follows:
*
*           lg_GadgetID - The ID for this gadget.
*           lg_LayoutTags - A taglist consisting of the following tags:
*               GL_GadgetKind (ULONG) - Which gadget kind to use.  This
*                   may be any of the GadTools gadget kinds (defined in
*                   libraries/gadtools.h), or one of the additional kinds
*                   provided by GadLayout, which are:
*                   IMAGEBUTTON_KIND : A button gadget with that uses an
*                                      Intuition Image structure for its
*                                      contents.  The image will be centred
*                                      automatically.
*                   DRAWER_KIND : A drawer button gadget.  Use this to
*                                 allow the user to use the ASL file
*                                 requester to select a path.
*                   FILE_KIND : A file button gadget.  Use this to allow
*                               the user to use the ASL file requester
*                               to select a file.
*                   Additional kinds may be added in the future.
*               GL_Width (WORD) - Absolute gadget width, in pixels.
*               GL_DupeWidth (UWORD) - Duplicate the width of another
*                   gadget.
*               GL_AutoWidth (WORD) - Set width according to length of
*                   text label + ti_Data.  Note that this function
*                   does not take into account the amount of space any
*                   gadget imagery might take within the gadgets area.
*               GL_Columns (UWORD) - Set width of gadget so that
*                   approximately ti_Data columns of text with the
*                   gadget's font will fit.  This will only be an
*                   approximation, because with proportional fonts the
*                   width of character varies.  Note that this function
*                   does not take into account the amount of space any
*                   gadget imagery might take within the gadgets area.
*               GL_AddWidth (WORD) - Add some value to the total width
*                   calculation.
*               GL_MinWidth (WORD) - Make sure that the final width of
*                   the gadget is at least this.
*               GL_MaxWidth (WORD) - Make sure that the final width of
*                   the gadget is at most this.
*               GL_Height (WORD) - Absolute gadget width.
*               GL_HeightFactor (UWORD) - Make the gadget height a multiple
*                   of the font height (useful for LISTVIEW_KIND gadgets).
*               GL_AutoHeight (WORD) - Set height according to height of
*                   text font + ti_Data.
*               GL_AddHeight (WORD) - Add some value to the total height
*                   calculation.
*               GL_MinHeight (WORD) - Make sure that the final height of
*                   the gadget is at least this.
*               GL_MaxHeight (WORD) - Make sure that the final height of
*                   the gadget is at most this.
*               GL_Top (WORD) - Absolute top edge.
*               GL_TopRel (UWORD)  - Top edge relative to bottom edge of
*                   another gadget (specified by its gadget ID).
*               GL_AdjustTop (WORD) - ADD the height of the text font +
*                   ti_Data to the top edge (often used to to properrly
*                   position gadgets that have their label above).
*               GL_AddTop (WORD) -  Add some value to the final top edge
*                   calculation.
*               GL_Bottom (WORD) - Absolute bottom edge.
*               GL_BottomRel (UWORD) - Bottom edge relative to top edge of
*                   another gadget (specified by its gadget ID).
*               GL_AddBottom (WORD) - Add some value to the final bottom edge
*                   calculation.
*               GL_Left (WORD) - Absolute left edge.
*               GL_LeftRel (UWORD) - Left edge relative to right edge of
*                   another gadget (specified by its gadget ID).
*               GL_AdjustLeft (WORD) - ADD the width of the text label +
*                   ti_Data to the left edge.
*               GL_AlignLeft (UWORD) - Align the left edge of the gadget
*                   with the left edge of another gadget (specified by its
*                   gadget ID).
*               GL_AddLeft (WORD) - Add some value to the final left edge
*                   calculation.
*               GL_Right (WORD) - Absolute right edge.
*               GL_RightRel (UWORD) - Right edge relative to left edge of
*                   another gadget (specified by its gadget ID).
*               GL_AlignRight (UWORD) - Align the right edge of the gadget
*                   with the right edge of another gadget (specified by its
*                   gadget ID).
*               GL_AddRight (WORD) - Add some value to the final right edge
*                   calculation.
*               GL_GadgetText (STRPTR) - Gadget text label.
*               GL_TextAttr (struct TextAttr *) - Desired font for gadget
*                   label, will override the GL_DefTextAttr if used.
*               GL_Flags - (ULONG) Gadget flags.
*               GL_UserData (VOID *)- Gadget UserData.
*               GL_LocaleText - Gadget label taken from a locale catalog,
*                   you supply the locale string ID.  If you use this tag
*                   you MUST have used GL_AppStrings in your call to
*                   LayoutGadgets().
*
*               If you've specified one of GadLayout's own gadget kinds
*               with GL_GadgetKind, the following tags are available for
*               defining attributes of those gadgets:
*
*               GLIM_Image (struct Image *) - Provide a pointer to the
*                   Image structure to to be used in an IMAGEBUTTON_KIND
*                   structure.  This pointer only need be valid when
*                   LayoutGadgets() is called.
*               GLIM_ReadOnly (BOOL) - Specifies that the gadget is read-
*                   only.  It will get a recessed border and will not be
*                   highlighted when clicked on.
*
*               Generally you need only specify the tags when the data
*               has changed from the previously gadget.  This gets a
*               little tricky when you use the relation tags like
*               GL_TopRel, as this means that gadgets will not be
*               processed in sequential order necessarily.
*
*           lg_GadToolsTags - When defining a GadTools gadgets, you
*               can pass a GadTools taglist to set options for that
*               gadget.  This would be the same set of tags that you
*               might pass to CreateGadgetA() if you were using GadTools
*               directly.
*           lg_Gadget - The pointer to the Gadget structure created for
*               this gadget will be placed here.  You should initialize
*               this field to NULL.  WARNING: The gadget structure
*               created READ-ONLY!
*
*       screen - A pointer to the screen that the gadgets will be
*           created for.  The is required so that the layout routines
*           can get display info about the screen, no rendering will
*           be done.
*
*       taglist - Pointer to a TagItem list (see below for allowed tags)
*
*   TAGS
*       GL_RightExtreme (LONG *) - A pointer to a LONG where GadLayout
*           will put the co-ordinate of the rightmost point where any
*           imagery of the laid-out gadgets will be drawn.  Use this to
*           open a window exactly big enough to hold all your gadgets.
*           Use this value alone with the WA_InnerWidth window tag and
*           NOT WA_Width, since you do not know how big the window
*           border will be.
*       GL_LowerExtreme (LONG *) - A pointer to a LONG where GadLayout
*           will put the co-ordinate of the lowermost point where any
*           imagery of the laid-out gadget will be drawn.  Use this to open
*           a window exactly big enough to hold all your gadgets.
*           Use this value alone with the WA_InnerHeight window tag and
*           NOT WA_Height, since you do not know how big the window
*           border will be.
*       GL_DefTextAttr (struct TextAttr *) - Instead of having to indicate
*           a TextAttr for each gadget, you can specify a font to be used
*           by default for all your gadgets.
*       GL_Catalog (struct Catalog *) - Specify the locale catalog to use
*           to get your strings from.  If you wish to localize your gadget
*           string via GL_LocaleText you MUST use this tag as well as
*           GL_AppStrings.  You must also make certain that locale.library
*           has been opened successfully with LocaleBase pointing to the
*           library base.
*       GL_AppStrings (struct AppString **) - If you wish to make your
*           gadgets localized, you you must pass a list of strings and
*           their IDs.  The format of these strings is an array of
*           structures, with a LONG that contains the ID and a STRPTR
*           pointing to the string, i.e.:
*               struct AppString
*               {
*                   LONG   as_ID;
*                   STRPTR as_Str;
*               };
*           These strings serve as the default language for the gadgets.
*           See locale.library documentation for more information on
*           localizing applications.  You MUST use this tag in addition to
*           GL_Catalog if you wish to use GL_LocaleText to localize your
*           gadgets.
*       GL_NoCreate (BOOL) - Set to TRUE if you don't want the layout
*           routine to actually create any gadgets.  This is used when
*           you want to use the GL_RightExtreme and GL_LowerExtreme tags
*           to find out how much space your gadgets will take, but don't
*           actually want to create the gadgets just yet.
*       GL_BorderTop (UWORD) - The size of the top border of your window.
*           If your window does not have the WFLG_GIMMEZEROZERO flag set,
*           it will be necessary to pass the size of the window borders.
*           This value can be gotten either from the Window structure of
*           your window (if it is already open), or from the Screen
*           structure of your screen (see intuition/screens.h for details
*           about this).  NOTE: This value is NOT added to the value
*           returned by GL_LowerExtreme!
*       GL_BorderLeft (UWORD) - The size of the left border of your window.
*           If your window does not have the WFLG_GIMMEZEROZERO flag set,
*           it will be necessary to pass the size of the window borders.
*           This value can be gotten either from the Window structure of
*           your window (if it is already open), or from the Screen
*           structure of your screen (see intuition/screens.h for details
*           about this).  NOTE: This value is NOT added to the value
*           returned by GL_RightExtreme.
*
*   RESULT
*       gad_info - A pointer to a private structure.  You must keep this
*           value and pass it to FreeLayoutGadgets() later on in order to
*           free up all resources used by your gadgets.
*
*   NOTES
*       You must be careful with the taglist in the lg_LayoutTags field.
*       Tags are processed sequentally in the order you give them in, and
*       if a tag references another gadget (eg. the GL_TopRel tag), then
*       processing of the current gadget halts while the referenced gadget
*       is processed (if it has not already been processed).  Problems can
*       arise if this gadget refers back to the original gadget that
*       referenced it, if it is referring to a field that has not yet been
*       processed in that gadget.  For example, gadget GAD_BUTTON1 may use
*       the GL_TopRel tag to refer to GAD_BUTTON2, which may subsequently
*       make use of GL_LeftRel to refer back to GAD_BUTTON1.  The gadgets
*       left edge must already be defined in GAD_BUTTON1 (i.e. a tag such
*       as GL_Left MUST appear before the GL_TopRel tag) if GAD_BUTTON2 is
*       to get the left edge desired.
*
*   BUGS
*       Doesn't do any checking to make sure gadgets don't overlap.
*       Essentially assumes you know what you're doing with the layout.
*
*       Bad things will happen if you provide an IMAGEBUTTON_KIND gadget
*       with an image too big to fit within the dimensions you've provided
*       for the gadget.
*
*   SEE ALSO
*       FreeLayoutGadgets(), gadlayout/gadlayout.h, libraries/gadtools.h,
*       GadTools documentation.
*
*****************************************************************************
*
*  Have you read the license info?  It tells you that you must send your
*  modifications of GadLayout to me, and that you may not make GadLayout
*  into a shared library.
*
*/
struct PrivateInfo * LayoutGadgets(struct Gadget **gad_list, struct LayoutGadget *gadgets,
				   struct Screen *screen, Tag *firsttag, ...)
{
	return( LayoutGadgetsA(gad_list, gadgets, screen, &firsttag) );
}

struct PrivateInfo * LayoutGadgetsA(struct Gadget **gad_list, struct LayoutGadget *gadgets,
					struct Screen *screen, struct TagItem *taglist)
{
	struct PrivateInfo *pi;
	struct Gadget *last_gad;
	struct NewGadget ng;
	struct LaidOutGadget gad_array[100];	/* Cop-out, should be dynamic */
	struct TagItem *tag=NULL;
	struct TextAttr *tattr=NULL;
	UWORD bordertop=0, borderleft=0;
	WORD *rightextreme=NULL, *lowerextreme=NULL;
	BOOL nocreate=FALSE;
	WORD num_gads=0, i;

	/* Allocate and initialize the PrivateInfo structure.
	 */
	if (!(pi = (struct PrivateInfo *)AllocVec(sizeof(struct PrivateInfo), MEMF_CLEAR)))
		return(NULL);

	pi->pi_Screen = screen;
	if (!(pi->pi_VisualInfo = GetVisualInfo(screen, TAG_DONE)))
		return(NULL);
	if (!(pi->pi_DrawInfo = GetScreenDrawInfo(pi->pi_Screen)))
		return(NULL);
	pi->pi_Remember = NULL;

	/* Count the number of gadgets that we have, and allocate space for
	 * NewGadget structures for all of them.
	 */
	while (gadgets[num_gads].lg_GadgetID != -1)
		num_gads++;

	/* if (!(gad_array = (struct LaidOutGadget *)AllocVec(num_gads * sizeof(struct LaidOutGadget), MEMF_ANY | MEMF_CLEAR)))
		return(NULL); */

	/* Evaluate the tag list.  Order does not make any difference with the
	 * tags supported at the moment.  In the case of duplicate tags, only
	 * the first will be recognized.
	 */

	/* ti_Data is a pointer to a LONG to store the right-most point that a
	 * gadget will exist in.
	 */
	if (tag = FindTagItem(GL_RightExtreme, taglist))
	{
#ifdef GL_DEBUG
		Printf("Tag: GL_RightExtreme  Data: %ld\n",tag->ti_Data);
#endif
		rightextreme = (WORD *)tag->ti_Data;
	}
	/* ti_Data is a pointer to a LONG to store the lower-most point that a
	 * a gadget will exist in.
	 */
	if (tag = FindTagItem(GL_LowerExtreme, taglist))
	{
#ifdef GL_DEBUG
		Printf("Tag: GL_LowerExtreme  Data: %ld\n",tag->ti_Data);
#endif
		lowerextreme = (WORD *)tag->ti_Data;
	}
	/* Indicates locale for the gadgets.
	 */
	if (tag = FindTagItem(GL_Catalog, taglist))
	{
#ifdef GL_DEBUG
		Printf("Tag: GL_Catalog  Data: %ld\n",tag->ti_Data);
#endif
		pi->pi_Catalog = (struct Catalog *)tag->ti_Data;
	}
	/* Indicates locale for the gadgets.
	 */
	if (tag = FindTagItem(GL_AppStrings, taglist))
	{
#ifdef GL_DEBUG
		Printf("Tag: GL_AppStrings  Data: %ld\n",tag->ti_Data);
#endif
		pi->pi_AppStrings = (struct AppString *)tag->ti_Data;
	}
	/* Specifies a default font for use with all gadgets, can still be
	 * over-ridden with GL_TextAttr.
	 */
	if (tag = FindTagItem(GL_DefTextAttr, taglist))
	{
#ifdef GL_DEBUG
		Printf("Tag: GL_DefTextAttr  Data: %ld\n",tag->ti_Data);
#endif
		tattr = (struct TextAttr *)tag->ti_Data;
	}
	/* Specifies the size of the top window border.
	 */
	if (tag = FindTagItem(GL_BorderTop, taglist))
	{
#ifdef GL_DEBUG
		Printf("Tag: GL_BorderTop  Data: %ld\n",tag->ti_Data);
#endif
		bordertop = (UWORD)tag->ti_Data;
	}
	/* Specifies the size of the left window border.
	 */
	if (tag = FindTagItem(GL_BorderLeft, taglist))
	{
#ifdef GL_DEBUG
		Printf("Tag: GL_BorderLeft  Data: %ld\n",tag->ti_Data);
#endif
		borderleft = (UWORD)tag->ti_Data;
	}
	/* Specifies that we don't actually want to create the gadgets.
	 */
	if (tag = FindTagItem(GL_NoCreate, taglist))
	{
#ifdef GL_DEBUG
		Printf("Tag: GL_NoCreate  Data: %ld\n",tag->ti_Data);
#endif
		nocreate = (BOOL)tag->ti_Data;
	}

	for (i = 0; i <= num_gads; i++)
		gad_array[i].log_IsEvaluated = FALSE;

	/* Initialize the gadget linked list.
	 */
#ifdef GL_DEBUG
	PutStr("Creating gadget context\n");
#endif
	if (nocreate == FALSE)
	{
		if (!(last_gad = CreateContext(gad_list)))
			return(NULL);
	}

	/* Initialize some suitable defaults for the gadgets.
	 */
	ng.ng_Height = ng.ng_Width = 0;
	ng.ng_LeftEdge = ng.ng_TopEdge = 0;
	ng.ng_GadgetText = NULL;
	ng.ng_TextAttr = tattr;
	ng.ng_GadgetID = 0;
	ng.ng_Flags = 0;
	ng.ng_VisualInfo = pi->pi_VisualInfo;            
	ng.ng_UserData = NULL;

	if (rightextreme)
		*rightextreme = 0;
	if (lowerextreme)
		*lowerextreme = 0;

	/* Read each gadget in the layout array, setting up its NewGadget structure.
	 */
#ifdef GL_DEBUG
	PutStr("Evaluating each gadget\n");
#endif
	for (i = 0; i < num_gads; i++)
		evaluate_gadget(pi, i, gadgets, &ng, gad_array);

	/* OK, the gagdets have all been laid out into NewGadget structures, we
	 * now go ahead and create them all.
	 */
#ifdef GL_DEBUG
	PutStr("Creating each gadget\n");
#endif
	for (i = 0; i < num_gads; i++)
	{
#ifdef GL_DEBUG
		Printf("Creating gadget %ld\n", (WORD)i);
#endif
		if (rightextreme)
			*rightextreme = MAX(*rightextreme, gad_array[i].log_NewGadget.ng_LeftEdge + gad_array[i].log_NewGadget.ng_Width);
		if (lowerextreme)
			*lowerextreme = MAX(*lowerextreme, gad_array[i].log_NewGadget.ng_TopEdge + gad_array[i].log_NewGadget.ng_Height);

		if (nocreate == FALSE)
		{
			gad_array[i].log_NewGadget.ng_LeftEdge += borderleft;
			gad_array[i].log_NewGadget.ng_TopEdge += bordertop;

			switch (gad_array[i].log_GadgetKind)
			{
				case IMAGEBUTTON_KIND:
				case FILE_KIND:
				case DRAWER_KIND:
					gadgets[i].lg_Gadget = last_gad = create_layout_gad(pi,
																		gad_array[i].log_GadgetKind,
																		last_gad,
																		&(gad_array[i].log_NewGadget),
																		gadgets[i].lg_LayoutTags);
					break;

				default:
					gadgets[i].lg_Gadget = last_gad = CreateGadgetA(gad_array[i].log_GadgetKind,
																	last_gad,
																	&(gad_array[i].log_NewGadget),
																	gadgets[i].lg_GadToolsTags);
					break;
			}
		}
#ifdef GL_DEBUG
		if (last_gad == NULL)
			PutStr("Couldn't create gadget\n");
#endif
	}
	/* FreeVec(gad_array); */

#ifdef GL_DEBUG
	PutStr("Done\n");
#endif
	if (nocreate == FALSE)
	{
		if (last_gad != NULL)
		{
			pi->pi_GadList = *gad_list;

			return(pi);
		}
	}
	else
		FreeVec(pi);

	return(NULL);
}


/****** gadlayout/GL_SetGadgetAttrsA ****************************************
*
*   NAME
*       GL_SetGadgetAttrsA -- Change attributes of a GadLayout gadget.
*       GL_SetGadgetAttrs -- Varargs stub for GL_SetGadgetAttrsA.
*
*   SYNOPSIS
*       GL_SetGadgetAttrsA(gad_info, gad, win, req, taglist)
*       VOID GL_SetGadgetAttrsA(APTR, struct Gadget *, struct Window *,
*                               struct Requester *, struct TagItem *)
*
*       GL_SetGadgetAttrs(gad_info, gad, win, req, firsttag, ...)
*       VOID GL_SetGadgetAttrs(APTR, struct Gadget *, struct Window *,
*                              struct Requester *, Tag *, ...)
*
*   FUNCTION
*       Changes attributes for one of the GadLayout gadget kinds according
*       according to the attributes chosen in the tag list.
*
*   INPUTS
*       gad_info - The value returned by LayoutGadgetsA().
*       gad - Pointer to the gadget in question.
*       win - Pointer to the window containing the gadget.
*       req - Pointer to the requester containing the gadget, or NULL if
*           not in a requester. (Not implemented yet, use NULL.)
*       taglist - Pointer to a TagItem list.
*
*   TAGS
*       IMAGEBUTTON_KIND:
*       GLIM_Image (struct Image *) - Changes the image displayed in the
*           gadget.
*
*   BUGS
*       This function is not compatable with itself in versions releases
*       1.5 and lower, because of the new pi parameter!  ALL OLD CODE
*       WILL HAVE TO BE CHANGED!!!
*
*       Attributes not pertaining to a specific gadget kind will not
*       always be ignored, so you will need to be careful that you only
*       try to change attributes that are valid for the gadget's kind.
*
*****************************************************************************
*
*  Have you read the license info?  It tells you that you must send your
*  modifications of GadLayout to me, and that you may not make GadLayout
*  into a shared library.
*
*/
VOID GL_SetGadgetAttrs(struct PrivateInfo *pi, struct Gadget *gad, struct Window *win, struct Requester *req,
						Tag *firsttag, ...)
{
	return( GL_SetGadgetAttrsA(pi, gad, win, req, &firsttag) );
}

VOID GL_SetGadgetAttrsA(struct PrivateInfo *pi, struct Gadget *gad,
						struct Window *win, struct Requester *req,
						struct TagItem *taglist)
{
	struct TagItem *tag;

	if (tag = FindTagItem(GLIM_Image, taglist))
	{
		if ((gad->MutualExclude & GADLAYOUT_KIND) == GADLAYOUT_KIND)
		{
			struct Image *image = (struct Image *)gad->GadgetRender;
			struct Image *new_image = (struct Image *)tag->ti_Data;

			/* Remove the gadget while we work on it.
			 */
			RemoveGadget(win, gad);

			/* Setup the new gadget imagery.
			 */
			gad->GadgetRender = create_gad_images(IMAGEBUTTON_KIND, gad->Width, gad->Height,
												  pi, new_image,
												  gad->MutualExclude & GLFLG_READONLY);

			/* Re-add the gadget and refresh it.
			 */
			AddGadget(win, gad, ~0);
			RefreshGList(gad, win, req, 1);
		}
	}
}


/****** gadlayout/GadgetArrayIndex ******************************************
*
*   NAME
*       GadgetArrayIndex -- Get a gadget's index in the LayoutGadget array.
*
*   SYNOPSIS
*       i = GadgetArrayIndex(gad_id, gadgets)
*       WORD GadgetArrayIndex(WORD, struct LayoutGadget *)
*
*   FUNCTION
*       Given a gadget ID, returns the index of that gadget's definition
*       in the LayoutGadget array.  For example, in cases where you need
*       to know a gadget's Gadget structure (eg. if you wanted to use
*       the Intuition function ActivateGadget() to make a string or an
*       integer gadget active), you would need to lookup the lg_Gadget
*       field in the LayoutGadget array.  You MUST NOT GIVE THE ARRAY
*       INDEX YOURSELF, THIS IS NOT GUARUNTEED TO REMAIN VALID!  Instead,
*       pass the id of the gadget that you want and this function will
*       return the array index for you.
*
*   INPUTS
*       gad_id - The ID of the gadget you want to find.
*       gadgets - The LayoutGadget array that this gadget is defined in.
*
*   RESULT
*       i - The index into the LayoutGadget array of the entry of the
*          gadget ID you asked for.
*
*****************************************************************************
*
*  Have you read the license info?  It tells you that you must send your
*  modifications of GadLayout to me, and that you may not make GadLayout
*  into a shared library.
*
*/
WORD GadgetArrayIndex(WORD gad_id, struct LayoutGadget *gadgets)
{
	WORD i=0;

	while ( (gadgets[i].lg_GadgetID != gad_id) && (gadgets[i].lg_GadgetID != -1) )
		i++;

	return(i);
}


/****** gadlayout/FreeLayoutGadgets *****************************************
*
*   NAME
*       FreeLayoutGadgets -- Frees gadgets laid out with LayoutGadgets().
*
*   SYNOPSIS
*       FreeLayoutGadgets(gad_info);
*       VOID FreeLayoutGadgets(APTR);
*
*   FUNCTION
*       Frees all resources used in creating and laying out gadgets with
*       LayoutGadgets().  This frees all gadgets as well as other
*       resources used.  Generally this will be called after a call to
*       CloseWindow() in Intuition.
*
*   INPUTS
*       gad_info - The pointer returned by LayoutGadgets().
*
*   SEE ALSO
*       LayoutGadgetsA()
*
*****************************************************************************
*
* PrivateInfo is a private data-type, and therefore this function is
* documented as taking APTR.
*
*  Have you read the license info?  It tells you that you must send your
*  modifications of GadLayout to me, and that you may not make GadLayout
*  into a shared library.
*
*/
VOID FreeLayoutGadgets(struct PrivateInfo *pi)
{
	struct Gadget *g;

	if (pi)
	{
		g = pi->pi_GadList;

		/* while (g)
		{
			if ((g->MutualExclude & GADLAYOUT_KIND) == GADLAYOUT_KIND)
			{
				struct Image *image = (struct Image *)g->GadgetRender;
				WORD i;

				for(i = 0 ; i < 2 ; i++)
					FreeVec(image[i].ImageData);

				FreeVec(image);
			}
			g = g->NextGadget;
		} */
		FreeRemember(&(pi->pi_Remember), TRUE);

		if (pi->pi_VisualInfo)
			FreeVisualInfo(pi->pi_VisualInfo);
		 if (pi->pi_DrawInfo)
			FreeScreenDrawInfo(pi->pi_Screen, pi->pi_DrawInfo);
		if (pi->pi_GadList)
			FreeGadgets(pi->pi_GadList);

		FreeVec(pi);
	}
}


/* Local routine that handles the layout tag list.  Each tag is evaluated
 * and the NewGadget structure for the gadget is setup.
 */
VOID evaluate_gadget(struct PrivateInfo *pi, WORD gad_num, struct LayoutGadget *gadgets,
					 struct NewGadget *ng, struct LaidOutGadget *gad_array)
{
	struct TagItem *tag=NULL, *tstate;
	struct NewGadget temp_ng;
	ULONG gad_kind=BUTTON_KIND;
	LONG textwidth;
	WORD found_num=0;
	BOOL extended_kind=FALSE;

	/* If we are already evaluating this gadgeting, then we won't risk a
	 * recursive state by trying to do it again.
	 */
	if (gad_array[gad_num].log_IsEvaluated)
		return;

	gad_array[gad_num].log_IsEvaluated = TRUE;

	ng->ng_GadgetID = gadgets[gad_num].lg_GadgetID;

	/* Look at each tag item sequentially.
	 */
	tstate = gadgets[gad_num].lg_LayoutTags;
	while (tag = NextTagItem(&tstate))
	{
		switch (tag->ti_Tag)
		{
			case GL_GadgetKind:
				/* Use a GadTools gadget, ti_Data indicates which kind (from
				 * libraries/gadtools.h).
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_GadgetKind  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID ,tag->ti_Data);
#endif
				gad_kind = tag->ti_Data;

				switch (tag->ti_Data)
				{
					case IMAGEBUTTON_KIND:
					case BORDERBUTTON_KIND:
					case DRAWER_KIND:
					case FILE_KIND:
						extended_kind = TRUE;
						break;
					default:
						extended_kind = FALSE;
						break;
				}
				break;

			case GL_GadgetText:
				/* Gadget label.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_GadgetText  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID ,tag->ti_Data);
#endif
				ng->ng_GadgetText = (STRPTR)tag->ti_Data;
				break;

			case GL_LocaleText:
				/* Gadget label.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_LocaleText  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID ,tag->ti_Data);
#endif
				ng->ng_GadgetText = get_locale_string(pi->pi_Catalog, pi->pi_AppStrings, tag->ti_Data);
				break;

			case GL_TextAttr:
				/* Desired font for gadget label.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_TextAttr  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_TextAttr = (struct TextAttr *)tag->ti_Data;
				break;

			case GL_Flags:
				/* Gadget flags.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_Flags  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_Flags = tag->ti_Data;
				break;

			case GL_UserData:
				/* Gadget UserData.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_UserData  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_UserData = (APTR)tag->ti_Data;
				break;

			case GL_Width:
				/* Absolute gadget width.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_Width  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_Width = tag->ti_Data;
				break;

			case GL_AutoWidth:
				/* Set width according to length of text label + (LONG)ti_Data.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AutoWidth  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				textwidth = text_width(ng->ng_GadgetText, ng->ng_TextAttr);
				ng->ng_Width = textwidth + (LONG)tag->ti_Data;
				break;

			case GL_Columns:
				/* Set width according to approximately fit ti_Data columns.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_Columns  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				{
					char tempstr[2] = "X";

					textwidth = text_width(tempstr, ng->ng_TextAttr) * (WORD)tag->ti_Data;
					ng->ng_Width = textwidth;
				}
				break;

			case GL_MinWidth:
				/* Make sure the width is at least a certain value.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_MinWidth  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				if (ng->ng_Width < (LONG)tag->ti_Data)
					ng->ng_Width = (LONG)tag->ti_Data;
				break;

			case GL_MaxWidth:
				/* Make sure the width is at most a certain value.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_MaxWidth  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				if (ng->ng_Width > (LONG)tag->ti_Data)
					ng->ng_Width = (LONG)tag->ti_Data;
				break;

			case GL_Height:
				/* Absolute gadget height.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_Height  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_Height = tag->ti_Data;
				break;

			case GL_HeightFactor:
				/* Make height a multiple of the font height.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_HeightFactor  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_Height = tag->ti_Data * ng->ng_TextAttr->ta_YSize;
				break;

			case GL_AutoHeight:
				/* Set height according to height of text font + (LONG)ti_Data.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AutoHeight  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_Height = ng->ng_TextAttr->ta_YSize + 2 + (LONG)tag->ti_Data;
				break;

			case GL_AddHeight:
				/* Add some value to the total height calculation.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AddHeight  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_Height += (LONG)tag->ti_Data;
				break;

			case GL_MinHeight:
				/* Make sure the height is at least a certain value.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_MinHeight  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				if (ng->ng_Height < (LONG)tag->ti_Data)
					ng->ng_Height = (LONG)tag->ti_Data;
				break;

			case GL_MaxHeight:
				/* Make sure the height is at most a certain value.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_MaxHeight  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				if (ng->ng_Height > (LONG)tag->ti_Data)
					ng->ng_Height = (LONG)tag->ti_Data;
				break;

			case GL_Top:
				/* Absolute top edge.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_Top  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_TopEdge = tag->ti_Data;
				break;

			case GL_Bottom:
				/* Absolute bottom edge.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_Bottom  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_TopEdge = tag->ti_Data - ng->ng_Height;
				break;

			case GL_Left:
				/* Absolute left edge.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_Left  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_LeftEdge = tag->ti_Data;
				break;

			case GL_Right:
				/* Absolute right edge.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_Right  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_LeftEdge = tag->ti_Data - ng->ng_Width;
				break;

			case GL_DupeWidth:
				/* Duplicate the width of another gadget.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_DupeWidth  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				found_num = GadgetArrayIndex(tag->ti_Data, gadgets);
				gad_array[gad_num].log_GadgetKind = gad_kind;
				CopyMem(ng, &(gad_array[gad_num].log_NewGadget),
					sizeof(struct NewGadget));
				CopyMem(ng, &temp_ng, sizeof(struct NewGadget));
				evaluate_gadget(pi, found_num, gadgets, &temp_ng, gad_array);
				ng->ng_Width = gad_array[found_num].log_NewGadget.ng_Width;
				break;

			case GL_AddWidth:
				/* Add some value to the total width calculation.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AddWidth  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_Width += (LONG)tag->ti_Data;
				break;

			case GL_LeftRel:
				/* Left edge relative to right edge of previous gadget.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_LeftRel  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				found_num = GadgetArrayIndex(tag->ti_Data, gadgets);
				gad_array[gad_num].log_GadgetKind = gad_kind;
				CopyMem(ng, &(gad_array[gad_num].log_NewGadget),
					sizeof(struct NewGadget));
				CopyMem(ng, &temp_ng, sizeof(struct NewGadget));
				evaluate_gadget(pi, found_num, gadgets, &temp_ng, gad_array);
				ng->ng_LeftEdge = gad_array[found_num].log_NewGadget.ng_LeftEdge + gad_array[tag->ti_Data].log_NewGadget.ng_Width;
				break;

			case GL_AlignLeft:
				/* Align left edge with the left edge of another gadget.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AlignLeft  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				found_num = GadgetArrayIndex(tag->ti_Data, gadgets);
				gad_array[gad_num].log_GadgetKind = gad_kind;
				CopyMem(ng, &(gad_array[gad_num].log_NewGadget),
					sizeof(struct NewGadget));
				CopyMem(ng, &temp_ng, sizeof(struct NewGadget));
				evaluate_gadget(pi, found_num, gadgets, &temp_ng, gad_array);
				ng->ng_LeftEdge = gad_array[found_num].log_NewGadget.ng_LeftEdge;
				break;

			case GL_AdjustLeft:
				/* ADD the width of the text label + ti_Data to the left edge.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AdjustLeft  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				textwidth = text_width(ng->ng_GadgetText, ng->ng_TextAttr);
				ng->ng_LeftEdge += textwidth + (LONG)tag->ti_Data;
				break;

			case GL_AddLeft:
				/* Add some value to the final left edge calculation.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AddLeft  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_LeftEdge += (LONG)tag->ti_Data;
				break;

			case GL_TopRel:
				/* Top edge relative to bottom edge of another gadget.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_TopRel  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				found_num = GadgetArrayIndex(tag->ti_Data, gadgets);
				gad_array[gad_num].log_GadgetKind = gad_kind;
				CopyMem(ng, &(gad_array[gad_num].log_NewGadget),
					sizeof(struct NewGadget));
				CopyMem(ng, &temp_ng, sizeof(struct NewGadget));
				evaluate_gadget(pi, found_num, gadgets, &temp_ng, gad_array);
				ng->ng_TopEdge = gad_array[found_num].log_NewGadget.ng_TopEdge
								 + gad_array[found_num].log_NewGadget.ng_Height;
				break;

			case GL_BottomRel:
				/* Bottom edge relative to top edge of another gadget.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_BottomRel  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				found_num = GadgetArrayIndex(tag->ti_Data, gadgets);
				gad_array[gad_num].log_GadgetKind = gad_kind;
				CopyMem(ng, &(gad_array[gad_num].log_NewGadget),
					sizeof(struct NewGadget));
				CopyMem(ng, &temp_ng, sizeof(struct NewGadget));
				evaluate_gadget(pi, found_num, gadgets, &temp_ng, gad_array);
				ng->ng_TopEdge = gad_array[found_num].log_NewGadget.ng_TopEdge - ng->ng_Height;
				break;

			case GL_AdjustTop:
				/* ADD the height of the text font + ti_Data to the top edge.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AdjustTop  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_TopEdge += ng->ng_TextAttr->ta_YSize + (LONG)tag->ti_Data;
				break;

			case GL_AddTop:
				/* Add some value to the final top edge calculation.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AddTop  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_TopEdge += (LONG)tag->ti_Data;
				break;

			case GL_AddBottom:
				/* Add some value to the final bottom edge calculation.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AddBottom  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_TopEdge += (LONG)tag->ti_Data;
				break;

			case GL_RightRel:
				/* Right edge relative to left edge of another gadget.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_RightRel  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				found_num = GadgetArrayIndex(tag->ti_Data, gadgets);
				gad_array[gad_num].log_GadgetKind = gad_kind;
				CopyMem(ng, &(gad_array[gad_num].log_NewGadget),
					sizeof(struct NewGadget));
				CopyMem(ng, &temp_ng, sizeof(struct NewGadget));
				evaluate_gadget(pi, found_num, gadgets, &temp_ng, gad_array);
				ng->ng_LeftEdge = gad_array[found_num].log_NewGadget.ng_LeftEdge
								  - ng->ng_Width;
				break;

			case GL_AlignRight:
				/* Align right edge with the right edge of another gadget.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AlignRight  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				found_num = GadgetArrayIndex(tag->ti_Data, gadgets);
				gad_array[gad_num].log_GadgetKind = gad_kind;
				CopyMem(ng, &(gad_array[gad_num].log_NewGadget),
					sizeof(struct NewGadget));
				CopyMem(ng, &temp_ng, sizeof(struct NewGadget));
				evaluate_gadget(pi, found_num, gadgets, &temp_ng, gad_array);
				ng->ng_LeftEdge = gad_array[found_num].log_NewGadget.ng_LeftEdge
								  + gad_array[found_num].log_NewGadget.ng_Width
								  - ng->ng_Width;
				break;

			case GL_AddRight:
				/* Add some value to the final right edge calculation.
				 */
#ifdef GL_DEBUG
				Printf("Gadget: %ld  Tag: GL_AddRight  Data: %ld\n", (LONG)gadgets[gad_num].lg_GadgetID, tag->ti_Data);
#endif
				ng->ng_LeftEdge += (LONG)tag->ti_Data;
				break;

			default:
				break;
		}
	}

	/* Now copy everything we know into the NewGadget structure.
	 */
	gad_array[gad_num].log_GadgetKind = gad_kind;
	CopyMem(ng, &(gad_array[gad_num].log_NewGadget), sizeof(struct NewGadget));
}


/* Local routine to determine how many pixels wide a string will be with a
 * given font.
 */
LONG text_width(char *str, struct TextAttr *font)
{
	struct IntuiText itext;

	itext.IText=str;
	itext.ITextFont=font;
	itext.FrontPen=1;
	itext.BackPen=0;
	itext.DrawMode=JAM1;
	itext.LeftEdge=0;
	itext.TopEdge=0;
	itext.NextText=NULL;

	return(IntuiTextLength(&itext));
}


/* Local routine to get a localized text string, given its ID.
 */
STRPTR get_locale_string(struct Catalog *catalog, struct AppString *appstrings,
						 LONG id)
{
	UWORD i=0;
	STRPTR local = NULL;

	if (appstrings)
	{
		while (!local)
		{
			if (appstrings[i].as_ID == id)
				local = appstrings[i].as_Str;
			i++;
		}

		if (LocaleBase)
			return(GetCatalogStr(catalog, id, local));
	}

    return(local);
}


/* Locale function to create one of GadLayout's extended gadgets.
 */
struct Gadget * create_layout_gad(struct PrivateInfo *pi, WORD gad_kind,
								  struct Gadget *last_gad, struct NewGadget *ng,
								  struct TagItem *taglist)
{
	struct Gadget *gad=NULL, *textgad=NULL;
	struct TagItem *tag;
	struct Image *image, *temp_image;
	struct NewGadget textng;
	BOOL readonly=FALSE;

	switch (gad_kind)
	{
		case IMAGEBUTTON_KIND:
		case FILE_KIND:
		case DRAWER_KIND:
			if (gad_kind == IMAGEBUTTON_KIND)
			{
				if (tag = FindTagItem(GLIM_Image, taglist))
					temp_image = (struct Image *)tag->ti_Data;
				if (tag = FindTagItem(GLIM_ReadOnly, taglist))
					readonly = tag->ti_Data;
			}
			else if (gad_kind == DRAWER_KIND)
				temp_image = &drawer_image;
			else if (gad_kind == FILE_KIND)
				temp_image = &file_image;

			/* Since GENERIC_KIND gadgets don't full support the ng_GadgetText
			 * field, we'll get around this by creating a TEXT_KIND gadget
			 * that displays the text for us.
			 */
			CopyMem(ng, &textng, sizeof(struct NewGadget));
			textng.ng_GadgetID = 1000;

			switch (textng.ng_Flags)
			{
				case PLACETEXT_LEFT:
					textng.ng_Width = 0;
					break;

				case PLACETEXT_RIGHT:
					textng.ng_LeftEdge += textng.ng_Width;
					textng.ng_Width = 0;
					break;

				case PLACETEXT_ABOVE:
					textng.ng_Height = 0;
					break;

				case PLACETEXT_BELOW:
					textng.ng_LeftEdge += textng.ng_Height;
					textng.ng_Height = 0;
					break;

				default:
					break;
			}
			textgad = last_gad = CreateGadget(TEXT_KIND, last_gad, &textng,
									GT_Underscore, '_',
									TAG_DONE);

			ng->ng_GadgetText = NULL;

			/* Create the image structure, then make the gadget.
			 */
			if (image = create_gad_images(gad_kind, ng->ng_Width, ng->ng_Height,
						pi, temp_image, readonly))
			{
				if (gad = last_gad = CreateGadget(GENERIC_KIND, last_gad, ng,
										TAG_DONE))
				{
					gad->GadgetType |= GTYP_BOOLGADGET;
					gad->GadgetRender = &image[0];
					gad->SelectRender = &image[1];
					gad->Flags |=  GFLG_GADGIMAGE | ((readonly)? GFLG_GADGHNONE : GFLG_GADGHIMAGE);
					gad->Activation |= GACT_RELVERIFY;
					gad->MutualExclude = GADLAYOUT_KIND | ((readonly)? GLFLG_READONLY : 0);
				}
			}
			ng->ng_GadgetText = textng.ng_GadgetText;

			break;

		default:
			break;
	}
	return(gad);
}


/* Local routine to return image structures for IMAGEBUTTON_KIND, DRAWER_KIND and
 * FILE_KIND gadgets.
 */
struct Image * create_gad_images(WORD gad_kind, UWORD width, UWORD height,
								 struct PrivateInfo *pi, struct Image *image,
								 BOOL readonly)
{
	struct Image *new_image = NULL;
	struct Remember *image_rem = NULL;
	UWORD image_width = 8, image_height = 8;

	/* Minimum size required.
	 */
	if (image)
	{
		image_width = image->Width;
		image_height = image->Height;
	}
	if ((width >= image_width + 4) && (height >= image_height + 2))
	{
		struct RastPort	*rport;

		/* Allocate local dummy rastport.
		 */
		if (rport = (struct RastPort *)AllocRemember(&image_rem, sizeof(struct RastPort), MEMF_ANY))
		{
			struct BitMap *bitmap[2] = { NULL, NULL };
			struct Layer_Info *layer_info[2] = { NULL, NULL };
			struct Layer *layer[2] = { NULL, NULL };
			BYTE success = FALSE;
			WORD depth, i,j;

			/* Determine screen depth.
			 */
			depth = pi->pi_Screen->RastPort.BitMap->Depth;

			/* Set up rastport.
			 */
			InitRastPort(rport);

			/* Allocate bitmaps and bitplane data.
			 */
			success = TRUE;

			for (i = 0 ; success && i < 2 ; i++)
			{
				if (bitmap[i] = (struct BitMap *)AllocRemember(&image_rem, sizeof(struct BitMap), MEMF_ANY))
				{
					InitBitMap(bitmap[i], depth, width, height);

					if (bitmap[i]->Planes[0] = (PLANEPTR)AllocRemember(&(pi->pi_Remember), bitmap[i]->BytesPerRow * bitmap[i]->Rows * bitmap[i]->Depth, MEMF_CHIP | MEMF_CLEAR))
					{
						for (j = 1 ; j < depth ; j++)
							bitmap[i]->Planes[j] = bitmap[i]->Planes[j - 1] + bitmap[i]->BytesPerRow * bitmap[i]->Rows;
					}
					else
						success = FALSE;
				}
				else
					success = FALSE;
			}

			/* Did we get what we wanted?
			 */
			if (success)
			{
				__aligned struct BitMap temp_bitmap;
				WORD left, top;

				/* Centre the image.
				 */
				left = (width - image_width) / 2;
				top	= (height - image_height) / 2;

				/* Set up the drawer bitmap.
				 */
				InitBitMap(&temp_bitmap, depth, image_width, image_height);

				/* Put the mask into all bitplanes.
				 */
				if (image)
					for (i = 0 ; i < depth ; i++)
						temp_bitmap.Planes[i] = (PLANEPTR)image->ImageData;

				/* Manipulate the first bitmap.
				 */
				rport->BitMap = bitmap[0];

				/* Clear the bitmap.
				 */
				SetRast(rport, pi->pi_DrawInfo->dri_Pens[BACKGROUNDPEN]);

				/* Draw the button box.
				 */
				DrawBevelBox(rport, 0, 0, width, height,
					((readonly)? GTBB_Recessed : TAG_IGNORE), TRUE,
					GT_VisualInfo, pi->pi_VisualInfo,            
					TAG_DONE);

				if ((gad_kind == DRAWER_KIND) || (gad_kind == FILE_KIND))
				{
					UBYTE minterm, mask;

					/* Clear the drawer mask.
					 */
					minterm = 0x20;
					mask = (1 << depth) - 1;

					BltBitMap(&temp_bitmap, 0, 0,
							  bitmap[0], left, top,
							  image_width, image_height,
							  minterm, mask, NULL);

					minterm = 0xe0;
					mask = pi->pi_DrawInfo->dri_Pens[TEXTPEN];

					BltBitMap(&temp_bitmap, 0, 0,
							  bitmap[0], left, top,
							  image_width, image_height,
							  minterm, mask, NULL);
				}
				else if (gad_kind == IMAGEBUTTON_KIND)
				{
					if (image)
						DrawImage(rport, image, left, top);
				}

				/* Manipulate the second bitmap.
				 */
				rport->BitMap = bitmap[1];

				/* Set the bitmap to the selected button colour.
				 */
				SetRast(rport, pi->pi_DrawInfo->dri_Pens[FILLPEN]);

				/* Create the selected imagery if needed.
				 */
				if ((pi->pi_DrawInfo->dri_Flags & DRIF_NEWLOOK) && depth > 1)
				{
					/* Draw the selected button box.
					 */
					DrawBevelBox(rport, 0, 0, width, height,
						((!readonly)? GTBB_Recessed : TAG_IGNORE), TRUE,
						GT_VisualInfo, pi->pi_VisualInfo,            
						TAG_DONE);

					if ((gad_kind == DRAWER_KIND) || (gad_kind == FILE_KIND))
					{
						/* Clear the drawer mask.
						 */
						BltBitMap(&temp_bitmap, 0, 0,
								  bitmap[1], left, top,
								  image_width, image_height,
								  0x20, (1 << depth) - 1, NULL);

						/* Draw the drawer mask.
						 */
						BltBitMap(&temp_bitmap, 0, 0,
								  bitmap[1], left, top,
								  image_width, image_height,
								  0xe0, pi->pi_DrawInfo->dri_Pens[FILLTEXTPEN], NULL);
					}
					else if (gad_kind == IMAGEBUTTON_KIND)
					{
						if (image)
							DrawImage(rport, image, left, top);
					}
				}
				/* Otherwise, just regular inverted imagery,
				 */
				else
				{
					/* Copy the image.
					 */
					BltBitMap(bitmap[0], 0, 0,
							  bitmap[1], 0, 0,
							  width, height,
							  0xc0, (1 << depth) - 1, NULL);

					/* Invert the inner area.
					 */
					BltBitMap(bitmap[0], 0, 0,
							  bitmap[1], 2, 1,
							  width - 4, height - 2,
							  0x50, (1 << depth) - 1, NULL);
				}
				/* Allocate space for standard image and selected image.
				 */
				if (new_image = (struct Image *)AllocRemember(&(pi->pi_Remember), 2 * sizeof(struct Image), MEMF_CHIP | MEMF_CLEAR))
				{
					/* Fill in the standard data.
					 */
					for (i = 0 ; i <= 1 ; i++)
					{
						new_image[i].Width = width;
						new_image[i].Height = height;
						new_image[i].Depth = depth;
						new_image[i].ImageData = (UWORD *)bitmap[i]->Planes[0];
						new_image[i].PlanePick = (1 << depth) - 1;
					}
				}
				/* for (i = 0; i < 2; i++)
					FreeVec(bitmap[i]->Planes[0]); */
			}
		}
	}
	/* Free all memory.
	 */
	FreeRemember(&image_rem, TRUE);

	return(new_image);
}
