/**	CreatHost.c
  *
  *	Implement the basic Create Host Port and dispatch window functions
  * for rexxarplib.library.
  *
  *   AUTHOR/DATE:  W.G.J. Langeveld, November 1987.
  *   ============
  *
  *	CURRENT VERSION:
  *
  *	This version has been converted to SAS C 6.5 format. It has been modified
  *	for modern definition sequences for ANSI compilation. This no longer works
  *	with OS versions prior to 2.04.e
  *
  *   AUTHOR/DATE:  Joanne Dow, jdow@bix.com, June 1998.
  *   ============
  *
  **/
#include <functions.h>
#include "ralprotos.h"
#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuitionbase.h>
#include <libraries/MyARP.h>
#include <proto/rexxsyslib.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "rexxarplib.h"
#include <simpmenu.h>
#include <simpreq.h>

/*
 *   The window host function table
 */
static struct WHfunc whfs[] = 
{
	{
		whf_openwindow,     "OPENWINDOW"     , 7 , 0       
	}
	,
	{
		whf_closewindow,    "CLOSEWINDOW"    , 1 , 0       
	}
	,
	{
		whf_closewindow,    "STOP"           , 0 , 0       
	}
	,
	{
		whf_closewindow,    "QUIT"           , 0 , 0       
	}
	,
	{
		whf_closewindow,    "EXIT"           , 0 , 0       
	}
	,
	{
		whf_draw,           "DRAW"           , 2 , 2       
	}
	,
	{
		whf_move,           "MOVE"           , 2 , 2       
	}
	,
	{
		whf_text,           "TEXT"           , 1 , 1       
	}
	,
	{
		whf_writepixel,     "WRITEPIXEL"     , 2 , 2       
	}
	,
	{
		whf_drawellipse,    "DRAWELLIPSE"    , 4 , 4       
	}
	,
	{
		whf_drawcircle,     "DRAWCIRCLE"     , 3 , 3       
	}
	,
	{
		whf_flood,          "FLOOD"          , 3 , 3       
	}
	,
	{
		whf_rectfill,       "RECTFILL"       , 4 , 4       
	}
	,
	{
		whf_setrgb4,        "SETRGB4"        , 4 , 4       
	}
	,
	{
		whf_setapen,        "SETAPEN"        , 1 , 1       
	}
	,
	{
		whf_setbpen,        "SETBPEN"        , 1 , 1       
	}
	,
	{
		whf_setopen,        "SETOPEN"        , 1 , 1       
	}
	,
	{
		whf_setdrmd,        "SETDRMD"        , 1 , 1       
	}
	,
	{
		whf_setdrpt,        "SETDRPT"        , 1 , 1       
	}
	,
	{
		whf_areamove,       "AREAMOVE"       , 2 , 2       
	}
	,
	{
		whf_areadraw,       "AREADRAW"       , 2 , 2       
	}
	,
	{
		whf_areaend,        "AREAEND"        , 1 , 0       
	}
	,
	{
		whf_areaellipse,    "AREAELLIPSE"    , 4 , 4       
	}
	,
	{
		whf_areacircle,     "AREACIRCLE"     , 3 , 3       
	}
	,
	{
		whf_addgadget,      "ADDGADGET"      , 7 , 5       
	}
	,
	{
		whf_removegadget,   "REMOVEGADGET"   , 1 , 1       
	}
	,
	{
		whf_refreshgadgets, "REFRESHGADGETS" , 0 , 0       
	}
	,
	{
		whf_windowtext,     "WINDOWTEXT"     , 1 , 1       
	}
	,
	{
		whf_backfill,       "BACKFILL"       , 0 , 0       
	}
	,
	{
		whf_activategadget, "ACTIVATEGADGET" , 1 , 1       
	}
	,
	{
		whf_activatewindow, "ACTIVATEWINDOW" , 0 , 0       
	}
	,
	{
		whf_windowtofront,  "WINDOWTOFRONT"  , 0 , 0       
	}
	,
	{
		whf_windowtoback,   "WINDOWTOBACK"   , 0 , 0       
	}
	,
	{
		whf_setfont,        "SETFONT"        , 5 , 2       
	}
	,
	{
		whf_modifyhost,     "MODIFYHOST"     , 2 , 2       
	}
	,
	{
		whf_readgadget,     "READGADGET"     , 1 , 1       
	}
	,
	{
		whf_setnotify,      "SETNOTIFY"      , 2 , 2       
	}
	,
	{
		whf_addmenu,        "ADDMENU"        , 1 , 1       
	}
	,
	{
		whf_additem,        "ADDITEM"        , 5 , 2       
	}
	,
	{
		whf_addsubitem,     "ADDSUBITEM"     , 5 , 2       
	}
	,
	{
		whf_remmenu,        "REMOVEMENU"     , 0 , 0       
	}
	,
	{
		whf_setreqcolor,    "SETREQCOLOR"    , 2 , 2       
	}
	,
	{
		whf_setitem,        "SETITEM"        , 4 , 4       
	}
	,
	{
		whf_readhost,       "READHOST"       , 2 , 2       
	}
	,
	{
		whf_setgadget,      "SETGADGET"      , 2 , 2       
	}
	,
	{
		whf_drawiff,        "IFFIMAGE"       , 6 , 1       
	}
	,
	{
		whf_null,            NULL            , 0 , 0       
	}
};

/**
 *
 *   This function finds a window host functions and checks the arguments
 *
**/
whf_find( long *err, int nargs, char *function )
{
	int ival;
	
	*err = 1L;
	/*
	 *   Get the function index
	 */
	ival = whhtable_index(function);
	if (ival < 0)
		return(-1);
	
	if (strcmpu(function, whfs[ival].rexxname) != 0)
		return(-1);
	
	*err = 17L;
	/*
	 *   Check number of arguments
	 */
	if (nargs > whfs[ival].nargs)
		return(-1);
	if (nargs < whfs[ival].nargs_req)
		return(-1);
	
	*err = 0L;
	return(ival);
}

/**
 *
 *   This function sets up a host that handles the function calls in the
 *   table above.
 *
**/
#define CHARWIDTH 8
#define CHARHEIGHT 9

char *rxf_createhost( long *err, int n, char *args[] )
{
	int ival = 0, nargs = 0, i, num = 0, na;
	int mouse_outstanding = 0;
	char *retval = NULL, *sg = NULL;
	struct Gadget *object = NULL;
	ULONG    class;
	USHORT   code, qual;
	long waitflags = 0L;
	struct IntuiMessage *message = NULL;
	struct RexxMsg *rexxmsg = NULL;
	struct HostParams hp;
	struct ReqPens reqp;
	struct GUData *gud;
	struct MenuItem *mi;
	
	*err = 3L;
	/*
	 *   Initialize the HostParams structure
	 */
	setmem(&hp, sizeof(struct HostParams), 0);
	
	/*
	 *   Under V36, defer the numbers till we open the window. This is because
	 *   we want to get the defaults from the Screen, there is one.
	 *   We can't assume the screen is open yet, and also the user may
	 *   already have set his colors by the time we do that though!
	 *   If he did, the number won't be -1, so use that now.
	 */
	reqp.reqp_BlockPen  = (unsigned char) -1;
	reqp.reqp_DetailPen = (unsigned char) -1;
	reqp.reqp_BackPen   = (unsigned char) -1;
	reqp.reqp_PromptPen = (unsigned char) -1;
	reqp.reqp_BoxPen    = (unsigned char) -1;
	reqp.reqp_ShadowPen = (unsigned char) -1;
	reqp.reqp_OkayPen   = (unsigned char) -1;
	reqp.reqp_CancelPen = (unsigned char) -1;

	hp.ReqPens      = &reqp;
	
	hp.Continue     = TRUE;
	/*
	 *   Check arguments
	 */
	*err = 18L;
	if (!args[0])
		goto cleanup;
	if (!args[1])
		goto cleanup;
	
	if (n >= 3)
		hp.PublicScreen = args[2];
	/*
	 *   Allocate some stuff
	 */
	for (i = 0; i < MAXCLASSES; i++) 
	{
		hp.ClassText[i] = mymalloc(3);
		if (!hp.ClassText[i])
			goto cleanup;
		strcpy(hp.ClassText[i], "%l");
		
		hp.NotifyPortName[i] = mymalloc(strlen(args[1]) + 1);
		if (!hp.NotifyPortName[i])
			goto cleanup;
		strcpy(hp.NotifyPortName[i], args[1]);
	}
	
	for (i = 0; i < 16; i++) 
	{
		hp.MsgPtr[i] = mymalloc(MAXIDCMPSIZE);
		if (!hp.MsgPtr[i])
			goto cleanup;
	}
	/*
	 *   Allocate a Vertex structure
	 */
	hp.Vertex = AddVertex(NULL);
	/*
	 *   Set up the Host's control master port.
	 */
	*err = 12L;
	hp.MasterPort = FindPort(args[0]);
	if (hp.MasterPort)
		goto cleanup;
	
	hp.MasterPort = CreatePort(args[0], 0L);
	if (hp.MasterPort == NULL)
		goto cleanup;
	hp.WaitMask = 1L << hp.MasterPort->mp_SigBit;
	/*
	 *   Main loop.
	 */
	while (hp.Continue || hp.MsgCount) 
	{
		waitflags = Wait(hp.WaitMask);
		/*
		 *   Handle messages from the MasterPort
		 */
		if (waitflags & 1L << hp.MasterPort->mp_SigBit) 
		{
			while(( rexxmsg = (struct RexxMsg *) GetMsg(hp.MasterPort)) != NULL) 
			{
				/*
				 *   See if this is a reply to a message we sent out.
				 *   reset the mouse flag when we get a reply.
				 */
				if (rexxmsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) 
				{
					HandlePortReply(rexxmsg);
					mouse_outstanding = 0;
					hp.MsgCount--;
				}
				else
				{
					if (hp.Continue) 
					{
						/*
						 *   Find the function
						 */
						nargs = rexxmsg->rm_Action & 0xFF;
						ival = whf_find(&(hp.Result), nargs, rexxmsg->rm_Args[0]);
						/*
						 *   Call the function with the arguments.
						 */
						if (ival >= 0) 
						{
							retval = (*whfs[ival].rexxfunc)(&hp, nargs,
							&(rexxmsg->rm_Args[1]) );
						}
						rexxmsg->rm_Result1 = hp.Result;
					}
					else
					{
						rexxmsg->rm_Result1 = 12L;
					}
					/*
					 *   Set the return values
					 */
					rexxmsg->rm_Result2 = 0L;
					ReplyMsg( (struct Message *) rexxmsg);
				}
			}
		}
		/*
		 *   Handle messages from the window if it exists
		 */
		if (hp.Window && hp.Window->UserPort) 
		{
			if (waitflags & 1L << hp.Window->UserPort->mp_SigBit) 
			{
				while((message = (struct IntuiMessage *)
				GetMsg(hp.Window->UserPort)) != NULL) 
				{
					code   = message->Code;
					qual   = message->Qualifier;
					object = (struct Gadget *) message->IAddress;
					class  = message->Class;
					
					ReplyMsg( (struct Message *) message);
					/*
					 *   If we've really quit, don't do anything
					 */
					if (hp.Continue == 0L)
						continue;
					/*
					 *   Don't do mousemoves if no reply to previous message yet.
					 */
					if (class == MOUSEMOVE) 
					{
						if (mouse_outstanding)
							continue;
						mouse_outstanding = 1;
					}
					/*
					 *   Handle gadgets and menus separately.
					 */
					num = ch_classtoindex(class);
					na = 0;
					
					if (class == GADGETUP || class == GADGETDOWN ) 
					{
						sg = NULL;
						if ((object->GadgetID & 0x0F) == 0x02)
							sg = (char *)
						((struct StringInfo*) (object->SpecialInfo))->Buffer;
						
						if (gud = (struct GUData *) object->UserData) 
						{
							na = ch_makestr(&hp, gud->MsgText, class, code, qual,
							gud->GadgetID, sg);
							if (na) 
							{
								if (SendPortMsg(hp.NotifyPortName[num], na, hp.MsgPtr,
									hp.MasterPort) == 0) hp.MsgCount++;
							}
						}
					}
					else
					if (class == MENUPICK) 
					{
						if (hp.Window->MenuStrip) 
						{
							while (code != MENUNULL) 
							{
								mi = ItemAddress(hp.Window->MenuStrip, (long) code);
								if (sg = ((struct XMenuItem *) mi)->UserData) 
								{
									na = ch_makestr(&hp, sg, class, code, qual,
									NULL, NULL);
									if (na) 
									{
										if (SendPortMsg(hp.NotifyPortName[num], na,
											hp.MsgPtr, hp.MasterPort) == 0) hp.MsgCount++;
									}
								}
								code = mi->NextSelect;
							}
						}
					}
					else
					{
						if (num >= 0) 
						{
							na = ch_makestr(&hp, hp.ClassText[num], class, code, qual,
							NULL, NULL);
							if (na) 
							{
								if (SendPortMsg(hp.NotifyPortName[num], na, hp.MsgPtr,
									hp.MasterPort) == 0) hp.MsgCount++;
							}
						}
					}
				}
			}
		}
	}
	
	*err = 0L;
	
	cleanup:
	if (hp.Vertex)
		hp.Vertex = KillVertex(hp.Vertex);
	
	if (hp.Font)
		CloseFont(hp.Font);
	
	if (hp.Window)
		whf_closewindow(&hp, 0, NULL);
	
	for (i = 0; i < MAXCLASSES; i++) 
	{
		if (hp.ClassText[i])
			myfree(hp.ClassText[i]);
		if (hp.NotifyPortName[i])
			myfree(hp.NotifyPortName[i]);
	}
	
	for (i = 0; i < 16; i++) 
	{
		if (hp.MsgPtr[i])
			myfree(hp.MsgPtr[i]);
	}
	
	if (hp.MasterPort && hp.WaitMask) 
	{
		while((rexxmsg = (struct RexxMsg *) GetMsg(hp.MasterPort)) != NULL) 
		{
			rexxmsg->rm_Result1 = 0L;
			rexxmsg->rm_Result2 = 0L;
			ReplyMsg( (struct Message *) rexxmsg);
		}
		DeletePort(hp.MasterPort);
	}
	
	return(CAS("0"));
}




/**
 *
 *   The NULL function
 *
**/
char *whf_null( struct HostParams *a, int b, char **c )
{
	return(NULL);
}


/**
 *
 *   Openwindow function
 *
**/
#define NWSIZE ((long) sizeof(struct NewWindow))
	
char *whf_openwindow( struct HostParams *hp, int n, char *args[] )
{
	struct NewWindow *nw = NULL;
	char *wt = NULL;
	struct DrawInfo *dri;
	int i;
	struct ReqPens *reqp;

	hp->ScreenPtr = NULL;

	hp->Result = 12L;
	if (hp->Window)
		return(NULL);

	hp->Result = 17L;
	if (args == NULL)
		goto cleanup;
	/*
	 *   If we can't find the public screen, open on the Workbench.
	 */
	hp->ScreenPtr = LockScreen(hp->PublicScreen);
	if (hp->ScreenPtr == NULL)
		hp->PublicScreen = NULL;
	
	reqp = hp->ReqPens;

	/*
	 *   If we have a screen, then get its DrawInfo to obtain the pens it uses.
	 *   Only use its pens if the program hasn't changed the pens yet (i.e.
	 *   the pen isn't set to -1).
	 *   If the screen won't open or there is no screen, use our usual defaults.
	 */
	if (hp->ScreenPtr) 
	{
		dri = GetScreenDrawInfo(hp->ScreenPtr);
		
		if (reqp->reqp_BlockPen  == -1)
			reqp->reqp_BlockPen  = dri->dri_Pens[BLOCKPEN];
		if (reqp->reqp_DetailPen == -1)
			reqp->reqp_DetailPen = dri->dri_Pens[DETAILPEN];
		if (reqp->reqp_BackPen   == -1)
			reqp->reqp_BackPen   = dri->dri_Pens[BACKGROUNDPEN];
		if (reqp->reqp_PromptPen == -1)
			reqp->reqp_PromptPen = dri->dri_Pens[TEXTPEN];
		if (reqp->reqp_BoxPen    == -1)
			reqp->reqp_BoxPen    = dri->dri_Pens[SHINEPEN];
		if (reqp->reqp_ShadowPen == -1)
			reqp->reqp_ShadowPen = dri->dri_Pens[SHADOWPEN];
		if (reqp->reqp_OkayPen   == -1)
			reqp->reqp_OkayPen   = dri->dri_Pens[TEXTPEN];
		if (reqp->reqp_CancelPen == -1)
			reqp->reqp_CancelPen = dri->dri_Pens[HIGHLIGHTTEXTPEN];
		
		FreeScreenDrawInfo(hp->ScreenPtr, dri);
	}
	else
	{
		if (reqp->reqp_BlockPen  == -1)
			reqp->reqp_BlockPen  = 3;
		if (reqp->reqp_DetailPen == -1)
			reqp->reqp_DetailPen = 2;
		if (reqp->reqp_BackPen   == -1)
			reqp->reqp_BackPen   = 0;
		if (reqp->reqp_PromptPen == -1)
			reqp->reqp_PromptPen = 1;
		if (reqp->reqp_BoxPen    == -1)
			reqp->reqp_BoxPen    = 2;
		if (reqp->reqp_ShadowPen == -1)
			reqp->reqp_ShadowPen = 1;
		if (reqp->reqp_OkayPen   == -1)
			reqp->reqp_OkayPen   = 1;
		if (reqp->reqp_CancelPen == -1)
			reqp->reqp_CancelPen = 3;
	}
	
	hp->Result = 3L;
	nw = (struct NewWindow *) AllocMem(NWSIZE, MEMF_CLEAR);
	if (nw == NULL)
		goto cleanup;
	
	if (n >= 1 && args[0])
		nw->LeftEdge = atoi(args[0]);
	if (n >= 2 && args[1])
		nw->TopEdge  = atoi(args[1]);
	
	if (hp->ScreenPtr) 
	{
		nw->Width  = hp->ScreenPtr->Width;
		nw->Height = hp->ScreenPtr->Height;
	}
	else
	{
		nw->Width  = GetWBCols();
		nw->Height = GetWBRows();
		if (GetWBLace() > 0)
			nw->Height <<= 1;
	}
	
	nw->MinWidth = 40;
	nw->MaxWidth = nw->Width;
	if (n >= 3 && args[2]) 
	{
		if (i = atoi(args[2])) 
		{
			nw->Width  = i;
			if (nw->Width < nw->MinWidth)
				nw->MinWidth = nw->Width;
		}
	}
	
	nw->MinHeight = 10;
	nw->MaxHeight = nw->Height;
	if (n >= 4 && args[3]) 
	{
		if (i = atoi(args[3])) 
		{
			nw->Height = i;
			if (nw->Height < nw->MinHeight)
				nw->MinHeight = nw->Height;
		}
	}
	
	if (n >= 5 && args[4]) 
	{
		for (i = 0; i < MAXCLASSES; i++) 
		{
			if ( strstr(args[4], ch_classtext(i)) )
				nw->IDCMPFlags |= ch_indextoclass(i);
		}
	}
	
	nw->Flags = SMART_REFRESH;
	if (n >= 6 && args[5]) 
	{
		if (strstr(args[5], "WINDOWCLOSE"))
			nw->Flags |= WINDOWCLOSE;
		if (strstr(args[5], "SIZEBRIGHT"))
			nw->Flags |= SIZEBRIGHT;
		if (strstr(args[5], "SIZEBBOTTOM"))
			nw->Flags |= SIZEBBOTTOM;
		if (strstr(args[5], "WINDOWSIZING"))
			nw->Flags |= WINDOWSIZING;
		if (strstr(args[5], "WINDOWDEPTH"))
			nw->Flags |= WINDOWDEPTH;
		if (strstr(args[5], "WINDOWDRAG"))
			nw->Flags |= WINDOWDRAG;
		if (strstr(args[5], "BORDERLESS"))
			nw->Flags |= BORDERLESS;
		if (strstr(args[5], "ACTIVATE"))
			nw->Flags |= ACTIVATE;
		if (strstr(args[5], "NOCAREREFRESH"))
			nw->Flags |= NOCAREREFRESH;
		if (strstr(args[5], "REPORTMOUSE"))
			nw->Flags |= REPORTMOUSE;
		if (strstr(args[5], "BACKDROP"))
			nw->Flags |= BACKDROP;
	}
	
	if (n >= 7 && args[6]) 
	{
		wt = mymalloc(strlen(args[6]) + 1);
		if (wt == NULL)
			goto cleanup;
		strcpy(wt, args[6]);
		nw->Title = (UBYTE *) wt;
	}
	
	nw->BlockPen  = reqp->reqp_BlockPen;
	nw->DetailPen = reqp->reqp_DetailPen;
	
	nw->Screen = hp->ScreenPtr;
	if (hp->ScreenPtr) 
	{
		nw->Type = hp->ScreenPtr->Flags & SCREENTYPE;
	}
	else
	{
		nw->Type = WBENCHSCREEN;
	}
	
	hp->Result = 18L;
	hp->Window = OpenWindow(nw);
	if (hp->Window == NULL)
		goto cleanup;
	
	if (hp->Window->UserPort)
		hp->WaitMask |= 1L << hp->Window->UserPort->mp_SigBit;
	
	hp->Result = 0L;
	
	FreeMem(nw, NWSIZE);
	nw = NULL;
	
	if (strstr(args[5], "BACKFILL"))
		BlankSReqWin(hp->Window, (ULONG) reqp->reqp_BackPen);
	
	if (hp->ScreenPtr)
		UnlockScreen(hp->PublicScreen, hp->ScreenPtr);
	
	return(NULL);
	
	cleanup:
	if (hp->Window) 
	{
		CloseWindow(hp->Window);
		hp->Window = NULL;
	}
	if (nw)
		FreeMem(nw, NWSIZE);
	if (wt)
		myfree(wt);

	if (hp->ScreenPtr)
		UnlockScreen(hp->PublicScreen, hp->ScreenPtr);
	
	return(NULL);
}


/**
 *
 *   Closewindow function
 *
**/
char *whf_closewindow( struct HostParams *hp, int n, char *args[] )
{
	struct IntuiMessage *msg = NULL;
	STRPTR wt = NULL;
	struct Gadget *gadget = NULL, *nextgadget = NULL;
	struct Menu *menu = NULL;
	
	if (hp->Window) 
	{
		if (hp->Window->UserPort) 
		{
			while (msg = (struct IntuiMessage *) GetMsg(hp->Window->UserPort))
				ReplyMsg( (struct Message *) msg);
			hp->WaitMask &= ~(1L << hp->Window->UserPort->mp_SigBit);
		}
		
		gadget = hp->Window->FirstGadget;
		while (gadget) 
		{
			nextgadget = gadget->NextGadget;
			if ((gadget->GadgetID & 0xF0) == 0x70) 
			{
				RemoveGadget(hp->Window, gadget);
				if (gadget->UserData)
					gadget->UserData = FreeGUData(gadget->UserData);
				FreeSimpGad(gadget);
			}
			gadget = nextgadget;
		}
		
		if (menu = hp->Window->MenuStrip)
			freesmenu(menu);
		
		wt = hp->Window->Title;
		CloseWindow(hp->Window);
		hp->Window = NULL;
		if (wt)
			myfree(wt);
	}
	
	hp->Result = 0L;
	/*
	 *   This implements the single argument of CLOSEWINDOW which allows the
	 *   host to stay around.
	 */
	if (args[0]) 
	{
		if (strcmpu(args[0], "CONTINUE") == 0)
			return(NULL);
	}
	
	hp->Continue = FALSE;
	
	return(NULL);
}

/**
 *
 *   Parse a string and assemble another. Return highest argument number.
 *
**/
int ch_makestr( struct HostParams *hp, char *t, ULONG class, USHORT code, USHORT qualifier, char *id, char *sg)
{
	int c, d, i, n = 0, m = 0, mn = 1;
	long time();
	char *s, bf[11];
	
	if (t == NULL)
		return(0);
	
	for (i = 0; i < 16; i++)
		hp->MsgPtr[i][0] = '\0';
	
	s = hp->MsgPtr[0];
	
	i = 0;
	while (c = *t++) 
	{
		if (c != '%') 
		{
			s[i++] = c;
			s[i] = '\0';
		}
		else
		{
			c = *t++;
			switch (c) 
			{
			case '\0' :
				return(0);
			case '%' :
				s[i++] = '%';
				s[i] = '\0';
				break;
			case 'a' :
				s[i++] = code;
				s[i] = '\0';
				break;
			case 'b' :
				switch (code) 
				{
				case SELECTDOWN :
					strcat(s, "SELECTDOWN");
					break;
				case SELECTUP   :
					strcat(s, "SELECTUP");
					break;
				case MENUDOWN   :
					strcat(s, "MENUDOWN");
					break;
				case MENUUP     :
					strcat(s, "MENUUP");
					break;
				}
				break;
			case 'c' :
				strcat(s, ltoa(bf, (long) code));
				break;
			case 'd' :
				if (id)
					strcat(s, id);
				break;
			case 'e' :
				strcat(s, ltoa(bf, (long) hp->Window->TopEdge));
				break;
			case 'f' :
				strcat(s, ltoa(bf, (long) hp->Window->LeftEdge));
				break;
			case 'g' :
				if (sg)
					strcat(s, sg);
				break;
			case 'h' :
				strcat(s, ltoa(bf, (long) hp->Window->Height));
				break;
			case 'i' :
				strcat(s, ltoa(bf, (long) ITEMNUM(code)));
				break;
			case 'l' :
				strcat(s, ch_classtoascii(class));
				break;
			case 'm' :
				strcat(s, ltoa(bf, (long) MENUNUM(code)));
				break;
			case 'q' :
				strcat(s, ltoa(bf, (long) qualifier));
				break;
			case 's' :
				strcat(s, ltoa(bf, (long) SUBNUM(code)));
				break;
			case 't' :
				strcat(s, ltoa(bf, time(NULL)));
				break;
			case 'w' :
				strcat(s, ltoa(bf, (long) hp->Window->Width));
				break;
			case 'x' :
				strcat(s, ltoa(bf, (long) hp->Window->MouseX));
				break;
			case 'y' :
				strcat(s, ltoa(bf, (long) hp->Window->MouseY));
				break;
			default  :
				if (isdigit(c)) 
				{
					n = c - '0';
					d = *t;
					if (isdigit(d)) 
					{
						m = c - '0';
						if (n == 1 && m < 6) 
						{
							n = 10 + m;
							t++;
						}
					}
					s = hp->MsgPtr[n];
					if (n > (mn - 1))
						mn = n + 1;
				}
				break;
			}
			i = strlen(s);
			if (i >= MAXIDCMPSIZE)
				return(0);
		}
	}
	
	return(mn);
}


/**
 *
 *   This function translates a class into its name
 *
**/
char *ch_classtoascii( ULONG class )
{
	return(ch_classtext(ch_classtoindex(class)));
}

/**
 *
 *   Function that given an index, will return the class text with that index
 *
**/
char *ch_classtext( int ind )
{
	static char *cltxt[] = 
	{
		"CLOSEWINDOW",
		"MOUSEBUTTONS",
		"MOUSEMOVE",
		"DELTAMOVE",
		"GADGETUP",
		"GADGETDOWN",
		"MENUPICK",
		"NEWSIZE",
		"VANILLAKEY",
		"RAWKEY",
		"NEWPREFS",
		"DISKINSERTED",
		"DISKREMOVED",
		"ACTIVEWINDOW",
		"INACTIVEWINDOW",
		"REFRESHWINDOW",
		"UNKNOWN",
	};
	
	if (ind >= 0 && ind < MAXCLASSES)
		return(cltxt[ind]);
	else
		return(NULL);
}

/**
 *
 *   This function retrieves a unique number for each class of IDCMP message
 *   suitable for indexing arrays.
 *
**/
int ch_classtoindex( ULONG class )
{
	switch (class) 
	{
	case CLOSEWINDOW    : return(0);
	case MOUSEBUTTONS   : return(1);
	case MOUSEMOVE      : return(2);
	case DELTAMOVE      : return(3);
	case GADGETUP       : return(4);
	case GADGETDOWN     : return(5);
	case MENUPICK       : return(6);
	case NEWSIZE        : return(7);
	case VANILLAKEY     : return(8);
	case RAWKEY         : return(9);
	case NEWPREFS       : return(10);
	case DISKINSERTED   : return(11);
	case DISKREMOVED    : return(12);
	case ACTIVEWINDOW   : return(13);
	case INACTIVEWINDOW : return(14);
	case REFRESHWINDOW  : return(15);
	default             : return(-1);
	}
}

/**
 *
 *   This function retrieves the class given an index;
 *
**/
ULONG ch_indextoclass( int ind )
{
	static ULONG clnums[] = 
	{
		CLOSEWINDOW    ,
		MOUSEBUTTONS   ,
		MOUSEMOVE      ,
		DELTAMOVE      ,
		GADGETUP       ,
		GADGETDOWN     ,
		MENUPICK       ,
		NEWSIZE        ,
		VANILLAKEY     ,
		RAWKEY         ,
		NEWPREFS       ,
		DISKINSERTED   ,
		DISKREMOVED    ,
		ACTIVEWINDOW   ,
		INACTIVEWINDOW ,
		REFRESHWINDOW
	};
	
	if (ind >= 0 && ind < MAXCLASSES)
		return(clnums[ind]);
	else
		return(0L);
}
