/*
**	$Id: GadgetSupport.c,v 1.3 92/05/15 22:24:44 olsen Sta Locker: olsen $
**	$Revision: 1.3 $
**	$Date: 92/05/15 22:24:44 $
**
**	GadTools object management support routines
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

	/* Some includes for starters. */

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

#include <clib/intuition_protos.h>
#include <clib/exec_protos.h>

#include <stdarg.h>

	/* External data. */

extern struct Library	*GadToolsBase;
extern struct Gadget	*ActiveGadget;

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

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

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

	/* Local routines. */

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

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

	/* The original gadtools.library routines. */

struct Gadget *	OldCreateGadgetA(ULONG,struct Gadget *,struct NewGadget *,struct TagItem *);
VOID		OldFreeGadget(struct Gadget *);
VOID		OldGT_SetGadgetAttrsA(struct Gadget *,struct Window *,struct Requester *,struct TagItem *);
VOID		OldGT_ReplyIMsg(struct IntuiMessage *);

	/* Pragma calls: the easy way. */

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

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

struct Gadget *
CreateGadget(ULONG Kind,struct Gadget *Previous,struct NewGadget *New,...)
{
	va_list				 VarArgs;
	struct TagItem			*TagList;
	struct GadgetSupportInfo	*Info = NULL;
	WORD				 i;
	UBYTE				 Underscore = 0;
	struct Gadget			*Result;

	va_start(VarArgs,New);

	TagList = (struct TagItem *)VarArgs;

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

			for(i = 0 ; TagList[i] . ti_Tag != TAG_DONE ; i++)
			{
				if(TagList[i] . ti_Tag == GT_Underscore)
				{
					Underscore = TagList[i] . ti_Data;

					break;
				}
			}

			if(Underscore)
			{
				if(New -> ng_GadgetText)
				{
					for(i = 0 ; i < strlen(New -> ng_GadgetText) ; i++)
					{
						if(New -> ng_GadgetText[i] == Underscore)
						{
							if(New -> ng_GadgetText[i + 1] > 32)
							{
								if(Info = (struct GadgetSupportInfo *)AllocMem(sizeof(struct GadgetSupportInfo),MEMF_ANY | MEMF_CLEAR))
								{
									Info -> PointBack	= Info;
									Info -> Kind		= Kind;
									Info -> Key		= New -> ng_GadgetText[i + 1];

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

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

									break;
								}
							}
						}
					}
				}
			}

			break;

		default:

			break;
	}

	New -> ng_UserData = Info;

	if(Info)
	{
		struct Node	*Node;
		struct List	*List;
		LONG		 Count;
		STRPTR		*Strings;
		BYTE		 ShowSelected = FALSE;

		for(i = 0 ; TagList[i] . ti_Tag != TAG_DONE ; i++)
		{
			switch(TagList[i] . ti_Tag)
			{
				case GTCB_Checked:
				case GTCY_Active:
				case GTLV_Selected:
				case GTMX_Active:
				case GTSC_Top:
				case GTSL_Level:
				case GTPA_Color:

					Info -> Current = TagList[i] . ti_Data;
					break;

				case GTSL_Min:

					Info -> Min = TagList[i] . ti_Data;
					break;

				case GTSC_Total:
				case GTSL_Max:

					Info -> Max = TagList[i] . ti_Data;
					break;

				case GTPA_Depth:

					Info -> Max = (1 << ((LONG)TagList[i] . ti_Data)) - 1;
					break;

				case GTLV_ReadOnly:

					if(!ShowSelected)
						Info -> ReadOnly = TRUE;

					break;

				case GTLV_ShowSelected:

					ShowSelected = TRUE;

					Info -> ReadOnly = FALSE;

					break;

				case GTCY_Labels:
				case GTMX_Labels:

					Count = 0;

					if(Strings = (STRPTR *)TagList[i] . ti_Data)
					{
						while(Strings[Count])
							Count++;
					}

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

					break;

				case GTLV_Labels:

					Count = 0;

					if(TagList[i] . ti_Data != NULL && TagList[i] . ti_Data != ~0)
					{
						List = (struct List *)TagList[i] . ti_Data;

						Node = List -> lh_Head;

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

							Node = Node -> ln_Succ;
						}
					}

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

					break;

				default:

					break;
			}
		}

		if(Result = OldCreateGadgetA(Kind,Previous,New,TagList))
			Info -> Gadget = Result;
		else
			FreeMem(Info,sizeof(struct GadgetSupportInfo));
	}
	else
		Result = OldCreateGadgetA(Kind,Previous,New,TagList);

	va_end(VarArgs);

	return(Result);
}

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

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

	va_start(VarArgs,Requester);

	TagList = (struct TagItem *)VarArgs;

	if(Info = (struct GadgetSupportInfo *)Gadget -> UserData)
	{
		if(Info -> PointBack == Info)
		{
			struct Node	*Node;
			struct List	*List;
			LONG		 Count;
			STRPTR		*Strings;
			WORD		 i;

			for(i = 0 ; TagList[i] . ti_Tag != TAG_DONE ; i++)
			{
				switch(TagList[i] . ti_Tag)
				{
					case GTCB_Checked:
					case GTCY_Active:
					case GTLV_Selected:
					case GTMX_Active:
					case GTSC_Top:
					case GTSL_Level:
					case GTPA_Color:

						Info -> Current = TagList[i] . ti_Data;
						break;

					case GTSL_Min:

						Info -> Min = TagList[i] . ti_Data;
						break;

					case GTSC_Total:
					case GTSL_Max:

						Info -> Max = TagList[i] . ti_Data;
						break;

					case GTCY_Labels:

						Count = 0;

						if(Strings = (STRPTR *)TagList[i] . ti_Data)
						{
							while(Strings[Count])
								Count++;
						}

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

						break;

					case GTLV_Labels:

						Count = 0;

						if(TagList[i] . ti_Data != NULL && TagList[i] . ti_Data != ~0)
						{
							List = (struct List *)TagList[i] . ti_Data;

							Node = List -> lh_Head;

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

								Node = Node -> ln_Succ;
							}
						}

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

						break;

					default:

						break;
				}
			}
		}
	}

	OldGT_SetGadgetAttrsA(Gadget,Window,Requester,TagList);

	va_end(VarArgs);
}

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

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

	while(Gadget)
	{
		if(Info = (struct GadgetSupportInfo *)Gadget -> UserData)
		{
			if(Info -> PointBack == Info && Info -> Gadget == Gadget)
				FreeMem(Info,sizeof(struct GadgetSupportInfo));
		}

		Gadget = Gadget -> NextGadget;
	}

	OldFreeGadgets(GadgetList);
}

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

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

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

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

						switch(Info -> Kind)
						{
							case CHECKBOX_KIND:

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

								break;

							case CYCLE_KIND:

								Info -> Current = Message -> Code;

								break;

							case LISTVIEW_KIND:

								Info -> Current = Message -> Code;

								break;

							case MX_KIND:

								Info -> Current = Message -> Code;

								break;

							case SCROLLER_KIND:

								Info -> Current = Message -> Code;

								break;

							case SLIDER_KIND:

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

								break;

							case PALETTE_KIND:

								Info -> Current = Message -> Code;

								break;

							default:

								break;
						}
					}
				}
			}
		}
	}

	OldGT_ReplyIMsg(Message);
}

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

VOID
KeySelect(struct Gadget **GadgetList,WORD Last,UBYTE Key,struct Window *Window,struct Gadget **Gadget,ULONG *Class,LONG *Code)
{
	if(Key == '\033' && (Window -> Flags & WFLG_CLOSEGADGET))
		*Class = IDCMP_CLOSEWINDOW;
	else
	{
		struct GadgetSupportInfo	*Info;
		WORD				 i;
		BYTE				 Forward,
						 GotIt = FALSE;

		for(i = 0 ; !GotIt && i <= Last ; i++)
		{
			if(!(GadgetList[i] -> Flags & GFLG_DISABLED))
			{
				if(Info = GadgetList[i] -> UserData)
				{
					if(Info -> PointBack == Info)
					{
						if(ToUpper(Info -> Key) == ToUpper(Key))
						{
							if(ToUpper(Info -> Key) == Key)
								Forward = FALSE;
							else
								Forward = TRUE;

							switch(Info -> Kind)
							{
								case STRING_KIND:
								case INTEGER_KIND:	ActivateGadget(ActiveGadget = GadgetList[i],Window,NULL);

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

											GotIt = TRUE;

											break;

								case BUTTON_KIND:	*Class	= IDCMP_GADGETUP;
											*Code	= 0;
											*Gadget	= GadgetList[i];

											GotIt = TRUE;

											break;

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

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

											GotIt = TRUE;

											break;

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

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

											GotIt = TRUE;

											break;

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

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

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

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

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

											GotIt = TRUE;

											break;

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

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

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

											GotIt = TRUE;

											break;

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

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

											GotIt = TRUE;

											break;

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

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

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

											GotIt = TRUE;

											break;

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

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

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

											GotIt = TRUE;

											break;

								default:		break;
							}
						}
					}
				}
			}
		}

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

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

					break;
				}
			}
		}
	}
}
