// TSMorph - Amiga Morphing program
// Copyright (C) © 1993  Topicsave Limited

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

// mpaddock@cix.compulink.co.uk

// include precompiled headers if required
#ifndef TSMORPH_H
#include "TSMorph.h"
#endif

extern char TempFilename[256]="";	// file name buffer set up and used in various places

UBYTE r[256],g[256],b[256];			// Saved rgb colors
ULONG rl[256],gl[256],bl[256];			// Saved rgb32 colors

/* display short help message
 * at top of window
 * helpnum : number of help node
 */
void
ihelp(ULONG helpnum) {
	// only display if not zero, and there is a short message and the window is open
	if (helpnum) {
		if (*(ShortHelp[helpnum])) {
			if (TSMorphWnd) {
				GT_SetGadgetAttrs(TSMorphGadgets[GDX_Help],TSMorphWnd,NULL,
									GTTX_Text,ShortHelp[helpnum], TAG_END );
			}
		}
	}
}

/* Display help using amigaguide (if available)
 * based on a number. Also displays short help message
 */
void
help(ULONG helpnum) {
	// display amigaguide if available
	if (handle) {
		SetAmigaGuideContext(handle,helpnum,NULL);
		SendAmigaGuideContext(handle,NULL);
	}
	// display short message
	ihelp(helpnum);
}

/* Colour the picture based on it colormap
 * Saving the palette for later use
 * The palette is only set when the window is active and a menu is not displayed
 */
void
ColorWindow(struct Picture *pic) {
	UWORD i;					// Loop counter
	UWORD color;			// Colour
	ULONG col[4];			// Table for get RGB32
	if (!EGS) {
		if (palette) {
			if (pic) {
				if (pic->ilbm->colortable) {
					// Store and set up to 256 colours
		 			if (GfxBase->lib_Version > 38) {
						for (i=0; i<min((1 << pic->Win->WScreen->BitMap.Depth),256); i++) {
							GetRGB32(pic->Win->WScreen->ViewPort.ColorMap,i,1,col);
							rl[i]=col[0];
							gl[i]=col[1];
							bl[i]=col[2];
						}
					}
					else {
						for (i=0; i<min((1 << pic->Win->WScreen->BitMap.Depth),256); i++) {
							color = GetRGB4(pic->Win->WScreen->ViewPort.ColorMap,i);
							r[i]=(color&0x0f00)>>8;
							g[i]=(color&0x00f0)>>4;
							b[i]=(color&0x000f);
						}
					}
					LoadRGB4(&(pic->Win->WScreen->ViewPort),(UWORD *)pic->ilbm->colortable,
						MIN(pic->ilbm->ncolors,pic->Win->WScreen->ViewPort.ColorMap->Count));
				}
			}
		}
	}
}

/* Reset the palette to the original
 * stored in ColorWindow()
 * Called when window becomes inactive or menu is to be displayed
 *
 * Note: This routine and the one above are not really allowed by CBM
 *       But if no other program is changing the palette whilst its
 *       window is inactive then everything should (amy) be OK.
 */
void
UnColorWindow(struct Picture *pic) {
	UWORD i;		// Loop counter
	if (!EGS) {
		if (palette) {
			if (pic) {
				if (pic->ilbm->colortable) {
		 			if (GfxBase->lib_Version > 38) {
						for (i=0; i<min((1 << pic->Win->WScreen->BitMap.Depth),256); i++) {
							SetRGB32(&(pic->Win->WScreen->ViewPort),i,rl[i],gl[i],bl[i]);
						}
		 			}
		 			else {
						for (i=0; i<min((1 << pic->Win->WScreen->BitMap.Depth),256); i++) {
							SetRGB4(&(pic->Win->WScreen->ViewPort),i,r[i],g[i],b[i]);
						}
					}
				}
			}
		}
	}
}

/* Standard CloseWindowSafely
 * removing messages on shared ports
 */
void
CloseWindowSafely(struct Window *win) {
	Forbid();
	StripIntuiMessages(win->UserPort,win);
	win->UserPort = NULL;
	ModifyIDCMP(win,0);
	Permit();
	CloseWindow(win);
}

/* Standard StripIntuiMessages	*/
void
StripIntuiMessages(struct MsgPort *mp,struct Window *win) {
	struct IntuiMessage *msg, *succ;
	msg = (struct IntuiMessage *)mp->mp_MsgList.lh_Head;
	while(succ = (struct IntuiMessage *)msg->ExecMessage.mn_Node.ln_Succ) {
		if(msg->IDCMPWindow == win) {
			Remove((struct Node *)msg);
			ReplyMsg((struct Message *)msg);
		}
		msg = succ;
	}
}

/* Display an error message
 * ErrorMessage	: The main text
 * Gadget			: Text for one gadget
 * extra				: more of the text
 * helpnum			: Number of a help node
 *
 * Uses reqtools.library if available
 */
void
Error(char *ErrorMessage,char *Gadget,char *extra,ULONG helpnum) {
	struct EasyStruct EasyStruct = {
		sizeof(struct EasyStruct),
		0,
		NULL,
		NULL,
		NULL
	};
	UBYTE *title;
	struct Window *req;
	UBYTE gad[32];
	UBYTE gad1[32];
	UBYTE buffer[512];
	UBYTE *p;
	ULONG ret = CALL_HANDLER;
	struct AmigaGuideMsg *agm;
	ULONG signals;
	struct rtHandlerInfo *rth;
	struct ER_SimpleRequest *EGS_Request;
	BOOL finished = FALSE;
	// Disable the windows and display the requester
	title = MyGetMsg(MSG_TSMERR);
	strcpy(gad,MyGetMsg(MSG_HELPOR));
	strcpy(gad1,MyGetMsg(MSG__HELPOR));
	DisableWindows(helpnum);
	if (EGS && Pic1_Open) {
		sprintf(buffer,ErrorMessage,extra);
		p = buffer;
		while (*p) {
			if (*p == '\n') {
				*p = '|';
			}
			p++;
		}
		if (handle) {
			strcat(gad,Gadget);
		}
		else {
			strcpy(gad,Gadget);
		}
		if (EGS_Request = ER_CreateSimpleReq(NULL,buffer,gad)) {
			ER_ChangeRequestPos(EGS_Request,
					max(Pic1.EGS_Win->WScreen->EScreen->MouseX-20,0),
					max(Pic1.EGS_Win->WScreen->EScreen->MouseY-20,0));
			while (!finished) {
				if (ER_DoRequest(EGS_Request)) {
					if ((!EGS_Request->Selected) && handle) {
						help(helpnum);
					}
					else {
						finished = TRUE;
					}
				  	while (agm = GetAmigaGuideMsg(handle)) {
			 			ReplyAmigaGuideMsg(agm);
			 		}
				}
				else {
					finished = TRUE;
				}
			}
			ER_DeleteRequest(EGS_Request);
		}
	}
	else {
		if (ReqToolsBase) {
			// Set up gadgets depending on if amigaguide is available
			if (!handle) {
				strcpy(gad1,"_");
			}
			strcat(gad1,Gadget);
			// This loops until OK is pressed, displaying help if Help is pressed (if present)
			while (ret) {
				ret = CALL_HANDLER;
				if (rtEZRequestTags(ErrorMessage,gad1,NULL,&extra,
											RT_ReqHandler,	&rth,
											RT_Window,		TSMorphWnd,
											RT_Underscore,	'_',
											RTEZ_ReqTitle,	title,
											TAG_END) == CALL_HANDLER) {
					while (ret == CALL_HANDLER) {
						if (!rth->DoNotWait) {
							signals = Wait(rth->WaitMask | ASig);
						}
						ret = rtReqHandlerA(rth,signals,NULL);
						if (ret == 1) {
							help(helpnum);
						}
						if (signals & ASig) {
						  	while (agm = GetAmigaGuideMsg(handle)) {
					 			ReplyAmigaGuideMsg(agm);
					 		}
					 	}
					}
				}
				else {
					ret = 0;
				}
			}
		}
		else {
			EasyStruct.es_TextFormat = ErrorMessage;
			EasyStruct.es_Title = title;
			// Set up gadgets depending on if amigaguide is available
			if (handle) {
				strcat(gad,Gadget);
			}
			else {
				strcpy(gad,Gadget);
			}
			EasyStruct.es_GadgetFormat = gad;
			req = BuildEasyRequest(TSMorphWnd,&EasyStruct,NULL,extra);
			// This loops until OK is pressed, displaying help if Help is pressed (if present)
			while (ret) {
				signals  = Wait((1L << req->UserPort->mp_SigBit) | ASig);
				if (signals & (1L << req->UserPort->mp_SigBit)) {
					ret = SysReqHandler(req,NULL,FALSE);
					if (ret == 1) {
						help(helpnum);
					}
				}
				if (signals & ASig) {
				  	while (agm = GetAmigaGuideMsg(handle)) {
				 		ReplyAmigaGuideMsg(agm);
				 	}
				}
			}
			FreeSysRequest(req);
		}
	}
	EnableWindows();
}

/* As Error but
 * pic	: a picture to uncolor if required
 */
void
XError(struct Picture *pic,char *ErrorMessage,char *Gadget,char *extra,ULONG helpnum) {
	UnColorWindow(pic);
	Error(ErrorMessage,Gadget,extra,helpnum);
	ColorWindow(pic);
}

/* Draw all points
 * in both windows
 */
void
DrawAllPoints(void) {
	struct MyPoint *MyPoint;
	int i;
	// For each point
	for (MyPoint = (struct MyPoint *)PointList.lh_Head;
						  MyPoint->MyNode.mln_Succ;
						  MyPoint = (struct MyPoint *)MyPoint->MyNode.mln_Succ) {
		// Draw point in image 1
		DrawPixels(&Pic1,MyPoint->x,MyPoint->y,NULL);
		// Same stuff for image 2
		DrawPixels(&Pic2,MyPoint->x1,MyPoint->y1,NULL);
		/* For each linked point draw a line
		 * to the other if the other is a "later" point
		 */
		for (i = 0;
			  i < MAX_LINKS;
			  i++) {
			if (MyPoint->p[i]) {
				if (EGS) {
					if (MyPoint->p[i] > MyPoint) {
						EGSMyDraw(Pic1.EGS_Win->RPort,MyPoint->p[i]->x,MyPoint->p[i]->y,MyPoint->x,MyPoint->y);
						EGSMyDraw(Pic2.EGS_Win->RPort,MyPoint->p[i]->x1,MyPoint->p[i]->y1,MyPoint->x1,MyPoint->y1);
					}
				}
				else {
					if (MyPoint->p[i] > MyPoint) {
						MyDraw(Pic1.Win->RPort,MyPoint->p[i]->x<<Zoom,MyPoint->p[i]->y<<Zoom,MyPoint->x<<Zoom,MyPoint->y<<Zoom);
						MyDraw(Pic2.Win->RPort,MyPoint->p[i]->x1<<Zoom,MyPoint->p[i]->y1<<Zoom,MyPoint->x1<<Zoom,MyPoint->y1<<Zoom);
					}
				}
			}
		}
	}
}

/* Handle Raw Key in Information
 * Window - from GadToolsBox
 */
int
TSMorphRawKey(void) {
	UWORD X,Y;		// Mouse position
	ULONG HNum;		// Help node
	X = TSMorphWnd->MouseX;
	Y = TSMorphWnd->MouseY;
	switch (TSMorphMsg.Code) {
	// only help key does anything
	case 0x5f:
		/* Find gadget pointer is in
		 * otherwise use whole window
		 */
		if (PointInBox(X,Y,Rect[GDX_Help].MinX,Rect[GDX_Help].MinY,
								Rect[GDX_Help].MaxX,Rect[GDX_Help].MaxY))
			HNum = H_HelpGad;
		else
		if (PointInBox(X,Y,Rect[GDX_GetFile1].MinX,Rect[GDX_GetFile1].MinY,
								Rect[GDX_GetFile1].MaxX,Rect[GDX_GetFile1].MaxY))
			HNum = H_G24File1;
		else
		if (PointInBox(X,Y,Rect[GDX_GetFile2].MinX,Rect[GDX_GetFile2].MinY,
								Rect[GDX_GetFile2].MaxX,Rect[GDX_GetFile2].MaxY))
			HNum = H_G24File2;
		else
		if (PointInBox(X,Y,Rect[GDX_Width].MinX,Rect[GDX_Width].MinY,
								Rect[GDX_Width].MaxX,Rect[GDX_Width].MaxY))
			HNum = H_Width;
		else
		if (PointInBox(X,Y,Rect[GDX_Height].MinX,Rect[GDX_Height].MinY,
								Rect[GDX_Height].MaxX,Rect[GDX_Height].MaxY))
			HNum = H_Height;
		else
		if (PointInBox(X,Y,Rect[GDX_GetFileOne].MinX,Rect[GDX_GetFileOne].MinY,
								Rect[GDX_GetFileOne].MaxX,Rect[GDX_GetFileOne].MaxY))
			HNum = H_GFileOne;
		else
		if (PointInBox(X,Y,Rect[GDX_GetFileTwo].MinX,Rect[GDX_GetFileTwo].MinY,
								Rect[GDX_GetFileTwo].MaxX,Rect[GDX_GetFileTwo].MaxY))
			HNum = H_GFileTwo;
		else
		if (PointInBox(X,Y,Rect[GDX_EditPoints].MinX,Rect[GDX_EditPoints].MinY,
								Rect[GDX_EditPoints].MaxX,Rect[GDX_EditPoints].MaxY))
			HNum = H_EPoints;
		else
		if (PointInBox(X,Y,Rect[GDX_SinglePicture].MinX,Rect[GDX_SinglePicture].MinY,
								Rect[GDX_SinglePicture].MaxX,Rect[GDX_SinglePicture].MaxY))
			HNum = H_Single;
		else
		if (PointInBox(X,Y,Rect[GDX_GetSaveName].MinX,Rect[GDX_GetSaveName].MinY,
								Rect[GDX_GetSaveName].MaxX,Rect[GDX_GetSaveName].MaxY))
			HNum = H_GName;
		else
		if (PointInBox(X,Y,Rect[GDX_FileOne].MinX,Rect[GDX_FileOne].MinY,
								Rect[GDX_FileOne].MaxX,Rect[GDX_FileOne].MaxY))
			HNum = H_FileOne;
		else
		if (PointInBox(X,Y,Rect[GDX_FileTwo].MinX,Rect[GDX_FileTwo].MinY,
								Rect[GDX_FileTwo].MaxX,Rect[GDX_FileTwo].MaxY))
			HNum = H_FileTwo;
		else
		if (PointInBox(X,Y,Rect[GDX_File241].MinX,Rect[GDX_File241].MinY,
								Rect[GDX_File241].MaxX,Rect[GDX_File241].MaxY))
			HNum = H_24File1;
		else
		if (PointInBox(X,Y,Rect[GDX_File242].MinX,Rect[GDX_File242].MinY,
								Rect[GDX_File242].MaxX,Rect[GDX_File242].MaxY))
			HNum = H_24File2;
		else
		if (PointInBox(X,Y,Rect[GDX_Frames].MinX,Rect[GDX_Frames].MinY,
								Rect[GDX_Frames].MaxX,Rect[GDX_Frames].MaxY))
			HNum = H_Frames;
		else
		if (PointInBox(X,Y,Rect[GDX_Start].MinX,Rect[GDX_Start].MinY,
								Rect[GDX_Start].MaxX,Rect[GDX_Start].MaxY))
			HNum = H_Start;
		else
		if (PointInBox(X,Y,Rect[GDX_Name].MinX,Rect[GDX_Name].MinY,
								Rect[GDX_Name].MaxX,Rect[GDX_Name].MaxY))
			HNum = H_Name;
		else
		// following should use the gadget sizing stuff - doubt if anybody will notice
		if (PointInBox(X,Y,0,0,
								20,TSMorphWnd->BorderTop))
			HNum = H_IClose;
		else
		if (PointInBox(X,Y,(TSMorphWnd->Width - 24),0,
								TSMorphWnd->Width,TSMorphWnd->BorderTop))
			HNum = H_IDepth;
		else
		if (PointInBox(X,Y,(TSMorphWnd->Width - 24 - 24),0,
								(TSMorphWnd->Width - 24),TSMorphWnd->BorderTop))
			HNum = H_IZoom;
		else
			HNum = H_IWindow;
		help(HNum);
		break;
	}
	return 1;
}

/* Handle Vanilla Key in Information
 * Window - from GadToolsBox
 */
int
TSMorphVanillaKey(void) {
	switch (TSMorphMsg.Code) {
	case 'o':
	case 'O':
		/* O - if control window not open
		 * either activate string gadget
		 * or display file requester with shift
		 */
		if (!ControlWindow) {
			if (TSMorphMsg.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) {
				GetFileOneClicked();
			}
			else {
				ActivateGadget(TSMorphGadgets[GDX_FileOne],TSMorphWnd,NULL);
			}
		}
		break;
	case 't':
	case 'T':
		/* T - if control window not open
		 * either activate string gadget
		 * or display file requester with shift
		 */
		if (!ControlWindow) {
			if (TSMorphMsg.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) {
				GetFileTwoClicked();
			}
			else {
				ActivateGadget(TSMorphGadgets[GDX_FileTwo],TSMorphWnd,NULL);
			}
		}
		break;
	case '1':
	case '!':
		/* 1 - either activate string gadget
		 * or display file requester with shift
		 */
		if (TSMorphMsg.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) {
			GetFile1Clicked();
		}
		else {
			ActivateGadget(TSMorphGadgets[GDX_File241],TSMorphWnd,NULL);
		}
		break;
	case '2':
	case '"':
		/* 2 - either activate string gadget
		 * or display file requester with shift
		 */
		if (TSMorphMsg.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) {
			GetFile2Clicked();
		}
		else {
			ActivateGadget(TSMorphGadgets[GDX_File242],TSMorphWnd,NULL);
		}
		break;
	case 'r':
	case 'R':
		// R - Cycle Single
		if (!ControlWindow) {
			if (TSMorphMsg.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) {
				if (!SinglePicture) {
					// If we do not have reqtools then do not allow Anims
					if (ReqToolsBase) {
						SinglePicture = 3;
					}
					else {
						SinglePicture = 1;
					}
				}
				else {
					--SinglePicture;
				}
			}
			else {
				if (ReqToolsBase) {
					// If we do not have reqtools then do not allow Anims
					if (SinglePicture == 3) {
						SinglePicture = 0;
					}
					else {
						++SinglePicture;
					}
				}
				else {
					if (SinglePicture == 1) {
						SinglePicture = 0;
					}
					else {
						++SinglePicture;
					}
				}
			}
			Saved = FALSE;
			GT_SetGadgetAttrs(TSMorphGadgets[GDX_SinglePicture],TSMorphWnd,NULL,
									GTCY_Active,SinglePicture, TAG_END );
		}
		break;
	case 'f':
	case 'F':
		// F - activate string gadgtet
		if (!ControlWindow) {
			ActivateGadget(TSMorphGadgets[GDX_Frames],TSMorphWnd,NULL);
		}
		break;
	case 'a':
	case 'A':
		// A - activate string gadgtet
		if (!ControlWindow) {
			ActivateGadget(TSMorphGadgets[GDX_Start],TSMorphWnd,NULL);
		}
		break;
	case 'n':
	case 'N':
		/* N - either activate string gadget
		 * or display file requester with shift
		 */
		if (TSMorphMsg.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) {
			GetSaveNameClicked();
		}
		else {
			ActivateGadget(TSMorphGadgets[GDX_Name],TSMorphWnd,NULL);
		}
		break;
	case 'p':
	case 'P':
		/* P - either open Control and image windows
		 * or activate and bring control window to front
		 */
		if (!ControlWindow) {
			OpenPointsEdit();
		}
		else {
			ActivateWindow(ControlWindow);
			WindowToFront(ControlWindow);
		}
		break;
	default:
		// other key
		break;
	}
	return 1;
}

/* Open... menu item in Info window */
int
TSMorphMenuOpen(void) {
	// Just call MyOpen();
	DisableWindows(DI_WaitOpen);
	MyOpen(NULL,FALSE,TRUE);
	EnableWindows();
	return 1;
}

/* Save menu item in Info window */
int TSMorphMenuSave(void) {
	// Just call SaveAs(last saved name)
	DisableWindows(DI_WaitSave);
	SaveAs(savedfilename);
	EnableWindows();
  	return 1;
}

/* Save As... menu item in Info window */
int
TSMorphMenuSaveAs(void) {
	// Just call SaveAs(no name)
	DisableWindows(DI_WaitSave);
	SaveAs(NULL);
	EnableWindows();
	return 1;
}

/* Delete... menu item in Info window */
int
TSMorphMenuDelete(void) {
	BPTR cd;
	BOOL ok = TRUE;
	struct AnchorPath ap = {0};
	// Delete a project with Icon and all .nnn files
	DisableWindows(DI_Delete);
	if (GetAFile("",MyGetMsg(MSG_DELETEP),FILF_SAVE)) {
		// delete main file and icon
		DeleteFile(TempFilename);
		DeleteDiskObject(TempFilename);
		// delete "project.nnn" files
		strcat(TempFilename,".[0-9][0-9][0-9]");
		if (!MatchFirst(TempFilename,&ap)) {
			while (ok) {
				cd = CurrentDir(ap.ap_Current->an_Lock);
				DeleteFile(ap.ap_Info.fib_FileName);
				CurrentDir(cd);
				ok = !MatchNext(&ap);
			}
		}
		MatchEnd(&ap);
	}
	EnableWindows();
	return 1;
}

/* About menu item in Info window */
int
TSMorphMenuAbout(void) {
	// Just Call About()
	DisableWindows(DI_About);
	About();
	EnableWindows();
	return 1;
}

/* Quit menu item in Info window */
int
TSMorphMenuQuit(void) {
	// Suggest save - quit if requested
  	if (!Saved) {
 		return !SaveRequester();
  	}
  	else {
  		return 0;
  	}
}

/* Edit Points... menu item in Info window */
int
TSMorphMenuEditPoints(void) {
	// Open window if not open
	if (!ControlWindow) {
		OpenPointsEdit();
	}
	// Otherwise activate and bring to front
	else {
		ActivateWindow(ControlWindow);
		WindowToFront(ControlWindow);
	}
	return 1;
}

/* Window closed, acts like quit	*/
int TSMorphCloseWindow(void) {
	// Suggest save - quit if required
  	if (!Saved) {
 		return !SaveRequester();
  	}
  	else {
  		return 0;
  	}
}

/* Help pressed on a menu in the info window	*/
int
TSMorphMenuHelp(void) {
	ULONG HNum;		// Help node
	// Forget any previous changes
	// This means all menu selections before help is pressed are ignored
	RemoveMenus();
	SwitchMenuItem(MM_PALETTE,palette,FALSE);
	SwitchMenuItem(MM_ZOOM,Zoom,FALSE);
	SwitchMenuItem(MM_ICONSP,CreateIconsP,FALSE);
	UpdateOpenMode(FALSE);
	SwitchMenuItem(MM_ICONSR,CreateIconsR,FALSE);
	UpdateRenderMode(FALSE);
	UpdateSaveFormat(FALSE);
	AddMenus();
	// Determine help number from menu position
	switch (TSMorphMsg.Code) {
	case FULLMENUNUM(0,NOITEM,NOSUB):
	case FULLMENUNUM(0,2,NOSUB):
	case FULLMENUNUM(0,6,NOSUB):
	case FULLMENUNUM(0,8,NOSUB):
	case FULLMENUNUM(0,10,NOSUB):
		HNum = H_IMP;
		break;
	case FULLMENUNUM(0,0,NOSUB):
		HNum = H_IMPN;
		break;
	case FULLMENUNUM(0,1,NOSUB):
		HNum = H_IMPO;
		break;
	case FULLMENUNUM(0,3,NOSUB):
		HNum = H_IMPS;
		break;
	case FULLMENUNUM(0,4,NOSUB):
		HNum = H_IMPSA;
		break;
	case FULLMENUNUM(0,5,NOSUB):
		HNum = H_IMPD;
		break;
	case FULLMENUNUM(0,7,NOSUB):
		HNum = H_IMPA;
		break;
	case FULLMENUNUM(0,9,NOSUB):
		HNum = H_IMPQ;
		break;
	case FULLMENUNUM(0,11,NOSUB):
		HNum = H_IMPP;
		break;
	case FULLMENUNUM(0,12,NOSUB):
		HNum = H_IMPE;
		break;
	case FULLMENUNUM(1,NOITEM,NOSUB):
	case FULLMENUNUM(1,MM_C1,NOSUB):
	case FULLMENUNUM(1,MM_C2,NOSUB):
	case FULLMENUNUM(1,MM_C3,NOSUB):
	case FULLMENUNUM(1,MM_C4,NOSUB):
		HNum = H_IMS;
		break;
	case FULLMENUNUM(1,MM_ICONS,NOSUB):
		HNum = H_P_CI;
		break;
	case FULLMENUNUM(1,MM_PALETTE,NOSUB):
		HNum = H_P_CP;
		break;
	case FULLMENUNUM(1,MM_ZOOM,NOSUB):
		HNum = H_P_ZO;
		break;
	case FULLMENUNUM(1,MM_OPENMODE,NOSUB):
	case FULLMENUNUM(1,MM_OPENMODE,MI_ALWAYS):
	case FULLMENUNUM(1,MM_OPENMODE,MI_IFILBM):
	case FULLMENUNUM(1,MM_OPENMODE,MI_IFCOLOURS):
	case FULLMENUNUM(1,MM_OPENMODE,MI_REMAP):
	case FULLMENUNUM(1,MM_OPENMODE,MI_OPAL):
		HNum = H_P_OM;
		break;
	case FULLMENUNUM(1,MM_SCREEN,NOSUB):
		HNum = H_P_PS;
		break;
	case FULLMENUNUM(1,MM_CUSTOM,NOSUB):
		HNum = H_PCUSTM;
		break;
	case FULLMENUNUM(1,MM_CDEPTH,NOSUB):
		HNum = H_PCUSTD;
		break;
	case FULLMENUNUM(1,MM_SPREVIEW,NOSUB):
		HNum = H_P_Preview;
		break;
	case FULLMENUNUM(1,MM_ICONSP,NOSUB):
		HNum = H_P_CIP;
		break;
	case FULLMENUNUM(1,MM_KEEPS,NOSUB):
		HNum = H_P_KS;
		break;
	case FULLMENUNUM(1,MM_LOADS,NOSUB):
		HNum = H_P_LOADS;
		break;
	case FULLMENUNUM(1,MM_SAVES,NOSUB):
		HNum = H_P_SAVES;
		break;
	case FULLMENUNUM(1,MM_SAVESAS,NOSUB):
		HNum = H_P_SAVESAS;
		break;
	case FULLMENUNUM(1,MM_ICONSR,NOSUB):
		HNum = H_P_CIR;
		break;
	case FULLMENUNUM(1,MM_DX,NOSUB):
		HNum = H_P_DX;
		break;
	case FULLMENUNUM(1,MM_DY,NOSUB):
		HNum = H_P_DY;
		break;
	case FULLMENUNUM(1,MM_LOADSCRIPT,NOSUB):
		HNum = H_P_LS;
		break;
	case FULLMENUNUM(1,MM_PRESCRIPT,NOSUB):
		HNum = H_P_PES;
		break;
	case FULLMENUNUM(1,MM_POSTSCRIPT,NOSUB):
		HNum = H_P_POS;
		break;
	case FULLMENUNUM(1,MM_DEPTH,NOSUB):
		HNum = H_P_DE;
		break;
	case FULLMENUNUM(1,MM_RENDERMODE,NOSUB):
	case FULLMENUNUM(1,MM_RENDERMODE,MI_MODE1):
	case FULLMENUNUM(1,MM_RENDERMODE,MI_MODE2):
	case FULLMENUNUM(1,MM_RENDERMODE,MI_MODE4):
	case FULLMENUNUM(1,MM_RENDERMODE,MI_MODE8):
		HNum = H_P_MO;
		break;
	case FULLMENUNUM(1,MM_SAVEFORMAT,NOSUB):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_ILBM24):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_OPAL24):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_OPAL24T):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_OPAL24F):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_OPAL24FT):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_JPEG):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_JPEGT):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_PBM):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_BW16):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_BW256):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_HAM6):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_HAM8):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_DCTV3):
	case FULLMENUNUM(1,MM_SAVEFORMAT,MI_DCTV4):
		HNum = H_P_SF;
		break;
	case FULLMENUNUM(1,MM_QUALITY,NOSUB):
		HNum = H_P_QU;
		break;
	case FULLMENUNUM(1,MM_SCREENR,NOSUB):
		HNum = H_P_PSR;
		break;
	case FULLMENUNUM(1,MM_ANTIALIAS,NOSUB):
		HNum = H_P_AA;
		break;
	case FULLMENUNUM(1,MM_INTEGER,NOSUB):
		HNum = H_P_INT;
		break;
	case FULLMENUNUM(1,MM_RESETD,NOSUB):
		HNum = H_S_RD;
		break;
	case FULLMENUNUM(1,MM_LASTS,NOSUB):
		HNum = H_S_LS;
		break;
	case FULLMENUNUM(1,MM_RESTORE,NOSUB):
		HNum = H_S_R;
		break;
	case FULLMENUNUM(1,MM_HELP,NOSUB):
		HNum = H_P_HE;
		break;
	case MENUNULL:
	default:
		HNum = H_IM;
		break;
	}
	// Display help
	help(HNum);
	return 1;
}

/* String gadget clicked */
int
File241Clicked(void) {
	// Only read when required	- display help if help pressed
	if (TSMorphMsg.Code == 0x5F) {
		help(H_24File1);
	}
	Saved = FALSE;
	return 1;
}

/* String gadget clicked */
int
File242Clicked(void) {
	// Only read when required	- display help if help pressed
	if (TSMorphMsg.Code == 0x5F) {
		help(H_24File2);
	}
	Saved = FALSE;
	return 1;
}

/* String gadget clicked */
int
FramesClicked(void) {
	// Only read when required	- display help if help pressed
	if (TSMorphMsg.Code == 0x5F) {
		help(H_Frames);
	}
	Saved = FALSE;
	return 1;
}

/* String gadget clicked */
int
FileOneClicked(void) {
	// Only read when required	- display help if help pressed
	if (TSMorphMsg.Code == 0x5F) {
		help(H_FileOne);
	}
	Saved = FALSE;
	return 1;
}

/* String gadget clicked */
int FileTwoClicked(void) {
	// Only read when required	- display help if help pressed
	if (TSMorphMsg.Code == 0x5F) {
		help(H_FileTwo);
	}
	Saved = FALSE;
	return 1;
}

/* CheckBox clicked */
int
SinglePictureClicked(void) {
	// Toggle BOOL
	SinglePicture = TSMorphMsg.Code;
	Saved = FALSE;
	return 1;
}

/* String gadget clicked */
int
NameClicked(void) {
	// Only read when required	- display help if help pressed
	if (TSMorphMsg.Code == 0x5F) {
		help(H_Name);
	}
	Saved = FALSE;
	return 1;
}

/* String gadget clicked */
int
StartClicked(void) {
	// Read when required - display help if help pressed
	if (TSMorphMsg.Code == 0x5F) {
		help(H_Start);
	}
	Saved = FALSE;
	return 1;
}

/* Edit Points gadget clicked */
int
EditPointsClicked(void) {
	// Open window if not open
	if (!ControlWindow) {
		OpenPointsEdit();
	}
	// Otherwise activate and bring to front
	else {
		ActivateWindow(ControlWindow);
		WindowToFront(ControlWindow);
	}
	return 1;
}

/* Open the Control and Image Windows	*/
void
OpenPointsEdit(void) {
	char buffer[257];
	BOOL ok=TRUE;
	// Open Control window, Pic1 and Pic2
	if ((SinglePicture == 2) || (SinglePicture == 3)) {	// Anim so need frame number
		ok = GetFrameNumber();
	}
	if (ok) {
		if (ControlWindow = OpenControlWindow()) {			// note multiple calls to DisableWindows()
			DisableWindows(DI_Image1);								// are safe, required to disable just opened window
			Pic1_Open = openAPicture(GetString(TSMorphGadgets[GDX_FileOne]),&Pic1,TRUE);
			if (Pic1_Open) {
				DisableWindows(DI_Image2);
		  		Pic2_Open = openAPicture(GetString(TSMorphGadgets[GDX_FileTwo]),&Pic2,TRUE);
				if (Pic2_Open) {
	 				DisableWindows(DI_Draw);
	 				// validate image sizes
			 	  	if ((Pic1.ilbm->Bmhd.w != Pic2.ilbm->Bmhd.w) ||
		  	 			 (Pic1.ilbm->Bmhd.h != Pic2.ilbm->Bmhd.h)) {
			  	 		Error(MyGetMsg(MSG_BISIZE),MyGetMsg(MSG_OK),NULL,HE_Size);
						CloseAPicture(&Pic2);
		  				Pic2_Open = FALSE;
						CloseAPicture(&Pic1);
		  				Pic1_Open = FALSE;
			  	  	}
			  	  	else {
			  	  		// if we have changed from non anim to anim then we may still have
			  	  		// points in memory - so draw, otherwise try and load points file
						if ((!(((struct MyPoint *)PointList.lh_Head)->MyNode.mln_Succ)) &&
							((SinglePicture == 2) || (SinglePicture == 3))) {
							strcpy(TempFilename,savedfilename);
							strcat(TempFilename,".%03ld");
							sprintf(buffer,TempFilename,FrameNumber);
							MyOpen(buffer,TRUE,FALSE);
						}
						else {
							DrawAllPoints();
						}
			  	  		// disable some menus, generally set up and activate things
						SetMyPointer();
						EnableWindows();
						OffMenu(TSMorphWnd,FULLMENUNUM(0,0,NOSUB));
						OffMenu(TSMorphWnd,FULLMENUNUM(0,1,NOSUB));
						ActivateWindow(ControlWindow);
						return;
					}
				}
				// Tidy up and close down cleanly
				else {
					CloseAPicture(&Pic1);
	  				Pic1_Open = FALSE;
				}
			}
			CloseControlWindow();
		}
		EnableWindows();
	}
}

/* Display ASL requesters	*/
int
GetFile1Clicked(void) {
	if (GetAFile(GetString(TSMorphGadgets[GDX_File241]),MyGetMsg(MSG_GET241),0)) {
		GT_SetGadgetAttrs(TSMorphGadgets[GDX_File241],TSMorphWnd,NULL,
								GTST_String, TempFilename, TAG_END );
		Saved = FALSE;
	}
	return 1;
}

/* Display ASL requesters	*/
int
GetFile2Clicked(void) {
	if (GetAFile(GetString(TSMorphGadgets[GDX_File242]),MyGetMsg(MSG_GET242),0)) {
		GT_SetGadgetAttrs(TSMorphGadgets[GDX_File242],TSMorphWnd,NULL,
								GTST_String, TempFilename, TAG_END );
		Saved = FALSE;
	}
	return 1;
}

/* Display ASL requesters	*/
int
GetFileOneClicked(void) {
	if (!ControlWindow) {
		if (GetAFile(GetString(TSMorphGadgets[GDX_FileOne]),MyGetMsg(MSG_PIC1),0)) {
			GT_SetGadgetAttrs(TSMorphGadgets[GDX_FileOne],TSMorphWnd,NULL,
									GTST_String, TempFilename, TAG_END );
			Saved = FALSE;
		}
	}
	return 1;
}

/* Display ASL requesters	*/
int
GetFileTwoClicked(void) {
	if (!ControlWindow) {
		if (GetAFile(GetString(TSMorphGadgets[GDX_FileTwo]),MyGetMsg(MSG_PIC2),0)) {
			GT_SetGadgetAttrs(TSMorphGadgets[GDX_FileTwo],TSMorphWnd,NULL,
									GTST_String, TempFilename, TAG_END );
			Saved = FALSE;
		}
	}
	return 1;
}

/* Display ASL requesters	*/
int
GetSaveNameClicked(void) {
	if (GetAFile(GetString(TSMorphGadgets[GDX_Name]),MyGetMsg(MSG_MFILEN),0)) {
		GT_SetGadgetAttrs(TSMorphGadgets[GDX_Name],TSMorphWnd,NULL,
								GTST_String, TempFilename, TAG_END );
		Saved = FALSE;
	}
	return 1;
}

/* Displays requester to add a grid of points */
void
AddGrid(void) {
	int flag = 1;
	// Open window and wait till close selected then close it
	DisableWindows(DI_Grid);
	if (!OpenGridRequestWindow()) {
		while (flag) {
			Wait((1L << GridRequestWnd->UserPort->mp_SigBit));
			flag = HandleGridRequestIDCMP();
		}
	}
	else {
		Error(MyGetMsg(MSG_UNABLE),MyGetMsg(MSG_OK),MyGetMsg(MSG_OPGRIDR),HE_OGrid);
	}
	CloseGridRequestWindow();
	EnableWindows();
}

/* Actually add the grid of points	*/
void
ReallyAddGrid(void) {
	UWORD xgrid;	//	Number horizontally
	UWORD ygrid;	// Number vertically
	UWORD x;			// X coord
	UWORD y;			// Y coord
	BOOL ok = TRUE;// Is it working?
	struct MyPoint **MyPointp;	// Point Pointer
	struct MyPoint *MyPoint;	// Point
	// Read X and Y numbers
	xgrid = GetNumber(GridRequestGadgets[GDX_XCells]);
	ygrid = GetNumber(GridRequestGadgets[GDX_YCells]);
	if (xgrid && ygrid) {
		/* Allocate Pointer to points memory
		 * We need an array but cannot allocate all points
		 * as one block of memory as a single point can be deleted
		 */
		if (MyPointp = AllocMem(sizeof(struct MyPoint *)*((xgrid+1)*(ygrid+1)),MEMF_CLEAR)) {
			// Create and draw all points
			for (x = 0;
					ok && (x <= xgrid);
					++x) {
				for (y = 0;
						ok  && (y <= ygrid);
						++y) {
					if (MyPoint = MyPointp[x*(xgrid+1)+y] = AllocMem(sizeof(struct MyPoint),MEMF_CLEAR)) {
						Saved = FALSE;
						MyPoint->x = (x * (Width-1))/xgrid;
						MyPoint->y = (y * (Height-1))/ygrid;
						MyPoint->x1 = MyPoint->x;
						MyPoint->y1 = MyPoint->y;
						AddTail(&PointList,(struct Node *)MyPoint);
						DrawPixels(&Pic1,MyPoint->x,MyPoint->y,MyPoint);
						DrawPixels(&Pic2,MyPoint->x1,MyPoint->y1,MyPoint);
					}
					else {
						ok = FALSE;
					}
				}
			}
			if (ok) {
				// Link all points - link top left to bottom right
				for (x = 0;
						x <= xgrid;
						++x) {
					for (y = 0;
							y <= ygrid;
							++y) {
						if (x < xgrid) {
							LinkPoints(MyPointp[x*(xgrid+1)+y],MyPointp[(x+1)*(xgrid+1)+y]);
						}
						if (y < ygrid) {
							LinkPoints(MyPointp[x*(xgrid+1)+y],MyPointp[x*(xgrid+1)+y+1]);
						}
					}
				}
			}
			// Free the Points pointers
			FreeMem(MyPointp,sizeof(struct MyPoint *)*((xgrid+1)*(ygrid+1)));
		}
		else {
			ok = FALSE;
		}
	}
	else {
		Error(MyGetMsg(MSG_BOTHGT0),MyGetMsg(MSG_OK),NULL,HE_Grid0);
	}
	if (!ok) {
		Error(MyGetMsg(MSG_OUTMP),MyGetMsg(MSG_OK),NULL,HE_MPoints);
	}
}

/* Display help on a control or image window menu
 * Code	: menu code
 * Note: this is a virtual clone of the code above (with 1 changed to M_SETTINGS)
 */
void
DoMenuHelp(USHORT Code) {
	ULONG HNum;		// Help node
	/* Reset everything which could have been set
	 * using the menu
	 */
	RemoveMenus();
	MySetMode(Mode);
	// Forget any previous changes
	SwitchMenuItem(MM_PALETTE,palette,FALSE);
	SwitchMenuItem(MM_ZOOM,Zoom,FALSE);
	SwitchMenuItem(MM_ICONSP,CreateIconsP,FALSE);
	UpdateOpenMode(FALSE);
	SwitchMenuItem(MM_ICONSR,CreateIconsR,FALSE);
	UpdateRenderMode(FALSE);
	UpdateSaveFormat(FALSE);
	AddMenus();
	// Determine help node
	switch (Code) {
	case FULLMENUNUM(M_PROJECT,NOITEM,NOSUB):
	case FULLMENUNUM(M_PROJECT,MM_B1,NOSUB):
	case FULLMENUNUM(M_PROJECT,MM_B2,NOSUB):
	case FULLMENUNUM(M_PROJECT,MM_B3,NOSUB):
	case FULLMENUNUM(M_PROJECT,MM_B4,NOSUB):
	case FULLMENUNUM(M_PROJECT,MM_B5,NOSUB):
		HNum = H_CMP;
		break;
	case FULLMENUNUM(M_PROJECT,MM_NEW,NOSUB):
		HNum = H_CMPN;
		break;
	case FULLMENUNUM(M_PROJECT,MM_OPEN,NOSUB):
		HNum = H_CMPO;
		break;
	case FULLMENUNUM(M_PROJECT,MM_SAVE,NOSUB):
		HNum = H_CMPS;
		break;
	case FULLMENUNUM(M_PROJECT,MM_SAVEAS,NOSUB):
		HNum = H_CMPSA;
		break;
	case FULLMENUNUM(M_PROJECT,MM_ABOUT,NOSUB):
		HNum = H_CMPA;
		break;
	case FULLMENUNUM(M_PROJECT,MM_EXITPOINTS,NOSUB):
		HNum = H_CMPE;
		break;
	case FULLMENUNUM(M_PROJECT,MM_QUIT,NOSUB):
		HNum = H_CMPQ;
		break;
	case FULLMENUNUM(M_PROJECT,MM_PPREVIEW,NOSUB):
		HNum = H_CMPP;
		break;
	case FULLMENUNUM(M_EDIT,NOITEM,NOSUB):
		HNum = H_CME;
		break;
	case FULLMENUNUM(M_EDIT,MM_GRID,NOSUB):
		HNum = H_CMEG;
		break;
	case FULLMENUNUM(M_EDIT,MM_TRIANGULATE,NOSUB):
		HNum = H_CMET;
		break;
	case FULLMENUNUM(M_EDIT,MM_FRAME,NOSUB):
		HNum = H_CMEF;
		break;
	case FULLMENUNUM(M_EDIT,MM_FRAME,MI_FIRST):
		HNum = H_CMEFF;
		break;
	case FULLMENUNUM(M_EDIT,MM_FRAME,MI_PREV):
		HNum = H_CMEFP;
		break;
	case FULLMENUNUM(M_EDIT,MM_FRAME,MI_GOTO):
		HNum = H_CMEFG;
		break;
	case FULLMENUNUM(M_EDIT,MM_FRAME,MI_NEXT):
		HNum = H_CMEFN;
		break;
	case FULLMENUNUM(M_EDIT,MM_FRAME,MI_LAST):
		HNum = H_CMEFL;
		break;
	case FULLMENUNUM(M_EDIT,MM_MODE,NOSUB):
		HNum = H_CMSM;
		break;
	case FULLMENUNUM(M_EDIT,MM_MODE,MI_EDITONE):
		HNum = H_EOne;
		break;
	case FULLMENUNUM(M_EDIT,MM_MODE,MI_EDITTWO):
		HNum = H_ETwo;
		break;
	case FULLMENUNUM(M_EDIT,MM_MODE,MI_EDITREL):
		HNum = H_ERel;
		break;
	case FULLMENUNUM(M_EDIT,MM_MODE,MI_ADD):
		HNum = H_EAdd;
		break;
	case FULLMENUNUM(M_EDIT,MM_MODE,MI_DELETE):
		HNum = H_EDel;
		break;
	case FULLMENUNUM(M_EDIT,MM_MODE,MI_LINK):
		HNum = H_ELnk;
		break;
	case FULLMENUNUM(M_EDIT,MM_MODE,MI_UNLINK):
		HNum = H_EUnl;
		break;
	case FULLMENUNUM(M_EDIT,MM_MODE,MI_NONE):
		HNum = H_EMov;
		break;
	case FULLMENUNUM(M_SETTINGS,NOITEM,NOSUB):
	case FULLMENUNUM(M_SETTINGS,MM_C1,NOSUB):
	case FULLMENUNUM(M_SETTINGS,MM_C2,NOSUB):
	case FULLMENUNUM(M_SETTINGS,MM_C3,NOSUB):
	case FULLMENUNUM(M_SETTINGS,MM_C4,NOSUB):
		HNum = H_IMS;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_ICONS,NOSUB):
		HNum = H_P_CI;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_PALETTE,NOSUB):
		HNum = H_P_CP;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_ZOOM,NOSUB):
		HNum = H_P_ZO;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_OPENMODE,NOSUB):
	case FULLMENUNUM(M_SETTINGS,MM_OPENMODE,MI_ALWAYS):
	case FULLMENUNUM(M_SETTINGS,MM_OPENMODE,MI_IFILBM):
	case FULLMENUNUM(M_SETTINGS,MM_OPENMODE,MI_IFCOLOURS):
	case FULLMENUNUM(M_SETTINGS,MM_OPENMODE,MI_REMAP):
	case FULLMENUNUM(M_SETTINGS,MM_OPENMODE,MI_OPAL):
		HNum = H_P_OM;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_SCREEN,NOSUB):
		HNum = H_P_PS;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_CUSTOM,NOSUB):
		HNum = H_PCUSTM;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_CDEPTH,NOSUB):
		HNum = H_PCUSTD;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_SPREVIEW,NOSUB):
		HNum = H_P_Preview;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_ICONSP,NOSUB):
		HNum = H_P_CIP;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_KEEPS,NOSUB):
		HNum = H_P_KS;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_LOADS,NOSUB):
		HNum = H_P_LOADS;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_SAVES,NOSUB):
		HNum = H_P_SAVES;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_SAVESAS,NOSUB):
		HNum = H_P_SAVESAS;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_ICONSR,NOSUB):
		HNum = H_P_CIR;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_DX,NOSUB):
		HNum = H_P_DX;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_DY,NOSUB):
		HNum = H_P_DY;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_LOADSCRIPT,NOSUB):
		HNum = H_P_LS;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_PRESCRIPT,NOSUB):
		HNum = H_P_PES;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_POSTSCRIPT,NOSUB):
		HNum = H_P_POS;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_DEPTH,NOSUB):
		HNum = H_P_DE;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_RENDERMODE,NOSUB):
	case FULLMENUNUM(M_SETTINGS,MM_RENDERMODE,MI_MODE1):
	case FULLMENUNUM(M_SETTINGS,MM_RENDERMODE,MI_MODE2):
	case FULLMENUNUM(M_SETTINGS,MM_RENDERMODE,MI_MODE4):
	case FULLMENUNUM(M_SETTINGS,MM_RENDERMODE,MI_MODE8):
		HNum = H_P_MO;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,NOSUB):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_ILBM24):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_OPAL24):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_OPAL24T):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_OPAL24F):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_OPAL24FT):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_JPEG):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_JPEGT):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_PBM):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_BW16):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_BW256):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_HAM6):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_HAM8):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_DCTV3):
	case FULLMENUNUM(M_SETTINGS,MM_SAVEFORMAT,MI_DCTV4):
		HNum = H_P_SF;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_QUALITY,NOSUB):
		HNum = H_P_QU;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_SCREENR,NOSUB):
		HNum = H_P_PSR;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_ANTIALIAS,NOSUB):
		HNum = H_P_AA;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_INTEGER,NOSUB):
		HNum = H_P_INT;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_RESETD,NOSUB):
		HNum = H_S_RD;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_LASTS,NOSUB):
		HNum = H_S_LS;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_RESTORE,NOSUB):
		HNum = H_S_R;
		break;
	case FULLMENUNUM(M_SETTINGS,MM_HELP,NOSUB):
		HNum = H_P_HE;
		break;
	case MENUNULL:
	default:
		HNum = H_CM;
		break;
	}
	help(HNum);
}

/* String gadget clicked */
int
XCellsClicked(void) {
	// Display help if required
	if (GridRequestMsg.Code == 0x5F) {
		help(H_RAGGX);
	}
	return 1;

}

/* String gadget clicked */
int
YCellsClicked(void) {
	// Display help if required
	if (GridRequestMsg.Code == 0x5F) {
		help(H_RAGGY);
	}
	return 1;
}

/* OK gadget clicked so add the grid	*/
int
OKClicked(void) {
	ReallyAddGrid();
	return 0;
}

/* Cancel clicked  - do not add grid	*/
int
CancelClicked(void) {
	return 0;
}

/* Close Window - do not add grid	*/
int
GridRequestCloseWindow(void) {
	return 0;
}

/* Vanilla Key in Add Grid	*/
int
GridRequestVanillaKey(void) {
	switch (GridRequestMsg.Code) {
	case 'o':
	case 'O':
		// Same as OK
		OKClicked();
		return 0;
		break;
	case 'x':
	case 'X':
		// Activate string gadget
		ActivateGadget(GridRequestGadgets[GDX_XCells],GridRequestWnd,NULL);
		break;
	case 'y':
	case 'Y':
		// Activate string gadget
		ActivateGadget(GridRequestGadgets[GDX_YCells],GridRequestWnd,NULL);
		break;
	case 'c':
	case 'C':
		// Cancel
		return 0;
		break;
	}
	return 1;
}

/* Raw key - Only help does anything	*/
int
GridRequestRawKey(void) {
	UWORD X,Y;			// Mouse coords
	ULONG HNum;			// Help node
	X = GridRequestWnd->MouseX;
	Y = GridRequestWnd->MouseY;
	switch (GridRequestMsg.Code) {
	case 0x5f:
		// Help key - determine gadget or whole window
		if (PointInBox(X,Y,GRect[GDX_XCells].MinX,GRect[GDX_XCells].MinY,
								GRect[GDX_XCells].MaxX,GRect[GDX_XCells].MaxY))
			HNum = H_RAGGX;
		else
		if (PointInBox(X,Y,GRect[GDX_YCells].MinX,GRect[GDX_YCells].MinY,
								GRect[GDX_YCells].MaxX,GRect[GDX_YCells].MaxY))
			HNum = H_RAGGY;
		else
		if (PointInBox(X,Y,GRect[GDX_OK].MinX,GRect[GDX_OK].MinY,
								GRect[GDX_OK].MaxX,GRect[GDX_OK].MaxY))
			HNum = H_RAGGOK;
		else
		if (PointInBox(X,Y,GRect[GDX_Cancel].MinX,GRect[GDX_Cancel].MinY,
								GRect[GDX_Cancel].MaxX,GRect[GDX_Cancel].MaxY))
			HNum = H_RAGGCA;
		else
		// should really use dynamic gadget size stuff
		if (PointInBox(X,Y,0,0,
								20,GridRequestWnd->BorderTop))
			HNum = H_RAGGCL;
		else
		if (PointInBox(X,Y,(GridRequestWnd->Width - 24),0,
								GridRequestWnd->Width,GridRequestWnd->BorderTop))
			HNum = H_RAGGDE;
		else
			HNum = H_RAG;
		help(HNum);
		break;
	}
	return 1;
}

/* If project not saved then
 * display requester and then
 * save if required
 * Returns:	TRUE keep going
 */
BOOL
SaveRequester(void) {
	struct EasyStruct EasyStruct = {
		sizeof(struct EasyStruct),
		0,
		NULL,
		NULL,
		NULL
	};
	UBYTE *title = "TSMorph";
	UBYTE *body;
	UBYTE *gadget;
	struct Window *req;
	LONG gad=-1;
	ULONG ret = 2;
	ULONG signals;
	struct rtHandlerInfo *rth;
	struct AmigaGuideMsg *agm;
	BOOL ret1;
	char *other = &(savedfilename[0]);
	// Disable the windows and display the requester - using reqtools if available
	DisableWindows(DI_Unsaved);
	body = MyGetMsg(MSG_UNSAVED);
	if (ReqToolsBase) {
		// set up gadgets depending on availability of amigaguide
		if (handle) {
			gadget = MyGetMsg(MSG__SHAC);
		}
		else {
			gadget = MyGetMsg(MSG__SAC);
		}
		// Loop until Save,Abandon or Cancel pressed
		while (((ret == 2) && handle) || (gad == -1)) {
			ret = CALL_HANDLER;
			if (rtEZRequestTags(body,gadget,NULL,&other,
										RT_ReqHandler, &rth,
										RT_Window, TSMorphWnd,
										RT_Underscore, '_',
										RTEZ_ReqTitle, title,
										TAG_END) == CALL_HANDLER) {
				while (ret == CALL_HANDLER) {
					if (!rth->DoNotWait) {
						signals = Wait(rth->WaitMask | ASig);
					}
					ret = rtReqHandlerA(rth,signals,NULL);
					if ((ret == 2) && handle) {
						help(H_Unsaved);
					}
					if (signals & ASig) {
					  	while (agm = GetAmigaGuideMsg(handle)) {
				 			ReplyAmigaGuideMsg(agm);
				 		}
				 	}
				}
			}
			else {
				ret = 0;
			}
			gad = ret;
		}
	}
	else {
		EasyStruct.es_TextFormat = body;
		EasyStruct.es_Title = title;
		// set up gadgets depending on availability of amigaguide
		if (handle) {
			EasyStruct.es_GadgetFormat = MyGetMsg(MSG_SHAC);
		}
		else {
			EasyStruct.es_GadgetFormat = MyGetMsg(MSG_SAC);
		}
		req = BuildEasyRequest(TSMorphWnd,&EasyStruct,NULL,savedfilename);
		// Loop until Save,Abandon or Cancel pressed
		while ((((gad = SysReqHandler(req,NULL,TRUE)) == 2) && handle) ||
				 (gad == -1)) {
			if (handle) {
				help(H_Unsaved);
			}
		}
		FreeSysRequest(req);
	}
	switch(gad) {
	case 1:
		if (*savedfilename) {
			ret1 = SaveAs(savedfilename);
		}
		else {
			ret1 = SaveAs(NULL);
		}
		break;
	case 2:
	case 3:				// 2 if no help, 3 otherwise
		ret1 = TRUE;
		break;
	case 0:
		ret1 = FALSE;
		break;
	}
	EnableWindows();
	return ret1;
}

/* Link two points
 * Drawing in the windows if open
 * Does not link already linked points
 */
void
LinkPoints(struct MyPoint* MyPoint,struct MyPoint *MyPoint1) {
	int i,j=0,j1=0;
	BOOL Ok = TRUE;
	// Link in first free position
	if ((MyPoint->NumLinks < MAX_LINKS) &&
		 (MyPoint1->NumLinks < MAX_LINKS)) {
		for (i = 0;
			  i < MAX_LINKS;
			  i++) {
			if (MyPoint->p[i] == MyPoint1) {
				Ok = FALSE;
			}
			if ((!MyPoint->p[i]) && (!j)) {
				j = i;
			}
		}
		for (i = 0;
			  (i < MAX_LINKS) && !j1;
			  i++) {
			if ((!MyPoint1->p[i]) && (!j1)) {
				j1 = i;
			}
		}
		if (Ok) {
			MyPoint->NumLinks++;
			MyPoint1->NumLinks++;
			MyPoint->p[j] = MyPoint1;
			MyPoint1->p[j1] = MyPoint;
			// If windows open then draw points and lines
			if (EGS) {
				if (Pic1_Open) {
					EGSMyDraw(Pic1.EGS_Win->RPort,MyPoint1->x,MyPoint1->y,MyPoint->x,MyPoint->y);
				}
				if (Pic2_Open) {
					EGSMyDraw(Pic2.EGS_Win->RPort,MyPoint1->x1,MyPoint1->y1,MyPoint->x1,MyPoint->y1);
				}
			}
			else {
				if (Pic1_Open) {
					MyDraw(Pic1.Win->RPort,MyPoint1->x<<Zoom,MyPoint1->y<<Zoom,MyPoint->x<<Zoom,MyPoint->y<<Zoom);
				}
				if (Pic2_Open) {
					MyDraw(Pic2.Win->RPort,MyPoint1->x1<<Zoom,MyPoint1->y1<<Zoom,MyPoint->x1<<Zoom,MyPoint->y1<<Zoom);
				}
			}
		}
	}
}

/* Unlink two points
 * Drawing on the screen if required
 */
void
UnlinkPoints(struct MyPoint* MyPoint,struct MyPoint *MyPoint1,BOOL MYDraw) {
	int i;
	// Erase lines if required
	if (MYDraw) {
		if (EGS) {
			EGSMyDraw(Pic1.EGS_Win->RPort,MyPoint1->x,MyPoint1->y,MyPoint->x,MyPoint->y);
			EGSMyDraw(Pic2.EGS_Win->RPort,MyPoint1->x1,MyPoint1->y1,MyPoint->x1,MyPoint->y1);
		}
		else {
			MyDraw(Pic1.Win->RPort,MyPoint1->x<<Zoom,MyPoint1->y<<Zoom,MyPoint->x<<Zoom,MyPoint->y<<Zoom);
			MyDraw(Pic2.Win->RPort,MyPoint1->x1<<Zoom,MyPoint1->y1<<Zoom,MyPoint->x1<<Zoom,MyPoint->y1<<Zoom);
		}
	}
	// Unlink each way
	for (i = 0;
		  i < MAX_LINKS;
		  i++) {
		if (MyPoint->p[i] == MyPoint1) {
			MyPoint->p[i] = NULL;
			MyPoint->NumLinks--;
		}
	}
	for (i = 0;
		  i < MAX_LINKS;
		  i++) {
		if (MyPoint1->p[i] == MyPoint) {
			MyPoint1->p[i] = NULL;
			MyPoint1->NumLinks--;
		}
	}
}

/* Unlink all points
 * Erasing lines if images are open
 */
void
UnlinkAllPoints(BOOL Draw) {
	int i;
	struct MyPoint *MyPoint;
	/* Loop round looking at the first point every time
	 * since DeletePoint removes it from the list
	 */
	for (MyPoint = (struct MyPoint *)PointList.lh_Head;
			MyPoint->MyNode.mln_Succ;
			MyPoint = (struct MyPoint *)MyPoint->MyNode.mln_Succ) {
		for (i = 0;
			  i < MAX_LINKS;
			  i++) {
			if (MyPoint->p[i]) {
				UnlinkPoints(MyPoint,MyPoint->p[i],Draw);
			}
		}
	}
}

/* Find a point in an image given
 * x and y coordinates
 * Returns	: NULL if none closeby
 */
struct MyPoint *
FindPoint(struct Picture *pic,WORD x,WORD y) {
	struct MyPoint *MyPoint;			// Point to look at
	LONG diff = 36;						// Must be closer than sqrt(36) pixels
	LONG diff1;								// sqr(distance apart) for current point
	struct MyPoint *RetPoint = NULL;	// Point being returned
	// adjust coords for zoom and scroll
	x+=(pic->Left>>Zoom);
	y+=(pic->Top>>Zoom);
	// Step thru all points and find closest
	for (MyPoint = (struct MyPoint *)PointList.lh_Head;
			MyPoint->MyNode.mln_Succ;
			MyPoint = (struct MyPoint *)MyPoint->MyNode.mln_Succ) {
		if (pic == &Pic1) {
			diff1 = ((MyPoint->x-x)*(MyPoint->x-x)+
						(MyPoint->y-y)*(MyPoint->y-y));
		}
		else {
			diff1 = ((MyPoint->x1-x)*(MyPoint->x1-x)+
						(MyPoint->y1-y)*(MyPoint->y1-y));
		}
  		if (diff1<diff) {
  			diff = diff1;
  			RetPoint = MyPoint;
  		}
	}
	return RetPoint;
}

/* Delete a point
 * removing all links
 */
void
DeletePoint(struct MyPoint *MyPoint){
	int i;
	// Unlink any links
	for (i = 0;
		  i < MAX_LINKS;
		  i++) {
		if (MyPoint->p[i]) {
			UnlinkPoints(MyPoint,MyPoint->p[i],FALSE);
		}
	}
	// remove from list and free memory
	Remove((struct Node *)MyPoint);
	FreeMem(MyPoint,sizeof(struct MyPoint));
}

/* Similar to Draw() but always draws top left to bottom right
 * to prevent stray pixels if erasing by drawing in the other direction
 */
void
MyDraw(struct RastPort *rp, SHORT x, SHORT y, SHORT oldx, SHORT oldy) {
	if ((oldx < x) || ((oldx == x) && (oldy < y))) {
		Move(rp,oldx,oldy);
		Draw(rp,x,y);
	}
	else {
		Move(rp,x,y);
		Draw(rp,oldx,oldy);
	}
}

/* Similar to EG_Draw() but always draws top left to bottom right
 * to prevent stray pixels if erasing by drawing in the other direction
 */
void
EGSMyDraw(struct EG_RastPort *rp, SHORT x, SHORT y, SHORT oldx, SHORT oldy) {
	if ((oldx < x) || ((oldx == x) && (oldy < y))) {
		EG_Move(rp,oldx,oldy);
		EG_Draw(rp,x,y);
	}
	else {
		EG_Move(rp,x,y);
		EG_Draw(rp,oldx,oldy);
	}
}

/* Shows ASL file requester for a file
 * name	: current file name
 * Prompt: Title
 * flags	: e.g. for save flag
 * Returns: TRUE if file selected, name is TempFileName
 */
BOOL
GetAFile(char *name,char *Prompt,ULONG flags) {
	char rtfilename[108] = "\0";	// reqtools filename from requester
	// Split of directory name
	strncpy(TempFilename,name,PathPart(name) - name);
	TempFilename[PathPart(name) - name] = 0;
	// Show requesters
	if (rtfilereq) {
		rtChangeReqAttr(rtfilereq,
							RTFI_Dir, TempFilename,
							TAG_DONE);
		strcpy(rtfilename,FilePart(name));
		if (rtFileRequest(rtfilereq, rtfilename, Prompt,
								RT_Window, TSMorphWnd,
								RTFI_Flags,(flags & FILF_SAVE)?FREQF_SAVE:0,
								TAG_DONE)) {
			strncpy(TempFilename,rtfilereq->Dir,256);
			AddPart(TempFilename,rtfilename,256);
			return TRUE;
		}
		else {
			return FALSE;
		}
}
	else {
		if (AslRequestTags((APTR) filereq,
								ASL_Hail,(Tag) Prompt,
								ASL_FuncFlags,flags,
								ASL_Dir, (Tag) TempFilename,
								ASL_File,(Tag) FilePart(name),
								ASL_Window, TSMorphWnd,
								TAG_DONE)) {
			// rejoin name
			strncpy(TempFilename,filereq->rf_Dir,256);
			AddPart(TempFilename,filereq->rf_File,256);
			return TRUE;
		}
		else {
			return FALSE;
		}
	}
}

/* New selected on the Info window */
int
TSMorphMenuNew(void) {
	// Suggest save if unsaved
	if (!Saved) {
		if (!SaveRequester()) {
			return 1;
		}
		DisableWindows(DI_New);
	}
	// Reset everything
	Saved = TRUE;
	savedfilename[0]=0;
	DeleteAllPoints();
	GT_SetGadgetAttrs(TSMorphGadgets[GDX_FileOne],TSMorphWnd,NULL,
							GTST_String, "", TAG_END );
	GT_SetGadgetAttrs(TSMorphGadgets[GDX_FileTwo],TSMorphWnd,NULL,
							GTST_String, "", TAG_END );
	GT_SetGadgetAttrs(TSMorphGadgets[GDX_File241],TSMorphWnd,NULL,
							GTST_String, "", TAG_END );
	GT_SetGadgetAttrs(TSMorphGadgets[GDX_File242],TSMorphWnd,NULL,
							GTST_String, "", TAG_END );
	GT_SetGadgetAttrs(TSMorphGadgets[GDX_Name],TSMorphWnd,NULL,
							GTST_String, "", TAG_END );
	GT_SetGadgetAttrs(TSMorphGadgets[GDX_Frames],TSMorphWnd,NULL,
							GTIN_Number,1, TAG_END );
	GT_SetGadgetAttrs(TSMorphGadgets[GDX_Start],TSMorphWnd,NULL,
							GTIN_Number,0, TAG_END );
	SinglePicture = 0;
	GT_SetGadgetAttrs(TSMorphGadgets[GDX_SinglePicture],TSMorphWnd,NULL,
							GTCY_Active,SinglePicture, TAG_END );
	Width = 0;
	GT_SetGadgetAttrs(TSMorphGadgets[GDX_Width],TSMorphWnd,NULL,
							GTNM_Number,Width, TAG_END );
	Height = 0;
	GT_SetGadgetAttrs(TSMorphGadgets[GDX_Height],TSMorphWnd,NULL,
							GTNM_Number,Height, TAG_END );
	MaxWidth = 0;
	MaxHeight = 0;
	EnableWindows();
	return 1;
}

/* Limit the coordinates of a point to the image size */
void
LimitPoints(SHORT *x,SHORT *y,struct Picture *pic) {
	if (*x < 0)
		*x = 0;
	if (*x > pic->width-1)
		*x = pic->width-1;
	if (*y < 0)
		*y = 0;
	if (*y > pic->height-1)
		*y = pic->height-1;
}

static BOOL realfirstcall = TRUE;	// Is this really the first call
												// not sure I understand this!
												// but some parameters (e.g. Public screen) must
												// only be set on the very first call
// Initialise all settings values from parameters etc.
void
InitParams(BOOL firstcall) {
	UBYTE *omode;
	ULONG OZoom = Zoom;
	UBYTE *FormatStr;
	if (realfirstcall) {
		PubScreenName 	= MyArgString("PUBSCREEN",NULL,!firstcall);
	}
	if (realfirstcall || !ControlWindow) {
		PaletteAllowed = !MatchToolValue(MyArgString("CHANGEPALETTE",PaletteAllowed?"YES":"OFF",!firstcall),"OFF");
		ZoomAllowed 	= !MatchToolValue(MyArgString("ZOOM",ZoomAllowed?"NO":"OFF",!firstcall),"OFF");
	}
	realfirstcall = FALSE;
	strcpy(ScreenName,MyArgString("PUBSCREEN",ScreenName,!firstcall));
	strcpy(CustomName,MyArgString("CUSTOMMODE",CustomName,!firstcall));
	CustomDepth = MyArgInt("CUSTOMDEPTH",CustomDepth,!firstcall);
	strcpy(ScreenNameR,MyArgString("PUBSCREENR",ScreenNameR,!firstcall));
	CreateIcons 	= MatchToolValue(MyArgString("CREATEICONS",CreateIcons?"YES":"NO",!firstcall),"YES");
	CreateIconsR 	= MatchToolValue(MyArgString("CREATEICONSR",CreateIconsR?"YES":"NO",!firstcall),"YES");
	CreateIconsP 	= MatchToolValue(MyArgString("CREATEICONSP",CreateIconsP?"YES":"NO",!firstcall),"YES");
	KeepSettings   = MatchToolValue(MyArgString("KEEPSETTINGS",KeepSettings?"YES":"NO",!firstcall),"YES");
	if (PaletteAllowed) {
		palette 			= MatchToolValue(MyArgString("CHANGEPALETTE",palette?"YES":"NO",!firstcall),"YES");
	}
	else {
		palette = FALSE;
	}
	if (ZoomAllowed) {
		Zoom = (MatchToolValue(MyArgString("ZOOM",Zoom?"YES":"NO",!firstcall),"YES") ? 1 : 0);	// Note: not a BOOL
	}
	else {
		Zoom = FALSE;
	}
	switch (OpenMode) {
	case OPEN_ILBM_IF_ILBM:
		omode = "IFILBM";
		break;
	case OPEN_ILBM_ALWAYS:
		omode = "ALWAYS";
		break;
	case OPEN_REMAP:
		omode = "REMAP";
		break;
	case OPEN_OPAL:
		omode = "OPAL";
		break;
	default:
		omode = "IFCOLOURS";
		break;
	}
	omode = MyArgString("OPENMODE",omode,!firstcall);
	if (MatchToolValue(omode,"IFILBM")) {
		OpenMode = OPEN_ILBM_IF_ILBM;
	}
	else {
		if (MatchToolValue(omode,"ALWAYS")) {
			OpenMode = OPEN_ILBM_ALWAYS;
		}
		else {
			if (MatchToolValue(omode,"REMAP")) {
				OpenMode = OPEN_REMAP;
			}
			else {
				if (MatchToolValue(omode,"OPAL")) {
					OpenMode = OPEN_OPAL;
				}
				else {
					OpenMode = OPEN_ILBM_IF_COLOURS;
				}
			}
		}
	}
	switch (SaveFormat) {
	case MI_OPAL24:
		FormatStr = "OPAL24";
		break;
	case MI_OPAL24T:
		FormatStr = "OPAL24T";
		break;
	case MI_OPAL24F:
		FormatStr = "OPAL24F";
		break;
	case MI_OPAL24FT:
		FormatStr = "OPAL24FT";
		break;
	case MI_JPEG:
		FormatStr = "JPEG";
		break;
	case MI_JPEGT:
		FormatStr = "JPEGT";
		break;
	case MI_PBM:
		FormatStr = "PPM";
		break;
	case MI_BW16:
		FormatStr = "BW16";
		break;
	case MI_BW256:
		FormatStr = "BW256";
		break;
	case MI_HAM6:
		FormatStr = "HAM6";
		break;
	case MI_HAM8:
		FormatStr = "HAM8";
		break;
	case MI_DCTV3:
		FormatStr = "DCTV3";
		break;
	case MI_DCTV4:
		FormatStr = "DCTV4";
		break;
	default:
		FormatStr = "ILBM24";
		break;
	}
	FormatStr = MyArgString("SAVEFORMAT",FormatStr,!firstcall);
	if (MatchToolValue(FormatStr,"OPAL24")) {
		SaveFormat = MI_OPAL24;
	}
	else {
		if (MatchToolValue(FormatStr,"OPAL24T")) {
			SaveFormat = MI_OPAL24T;
		}
		else {
			if (MatchToolValue(FormatStr,"OPAL24F")) {
				SaveFormat = MI_OPAL24F;
			}
			else {
				if (MatchToolValue(FormatStr,"OPAL24FT")) {
					SaveFormat = MI_OPAL24FT;
				}
				else {
					if (MatchToolValue(FormatStr,"JPEG")) {
						SaveFormat = MI_JPEG;
					}
					else {
						if (MatchToolValue(FormatStr,"JPEGT")) {
							SaveFormat = MI_JPEGT;
						}
						else {
							if (MatchToolValue(FormatStr,"PPM")) {
								SaveFormat = MI_PBM;
							}
							else {
								if (MatchToolValue(FormatStr,"BW16")) {
									SaveFormat = MI_BW16;
								}
								else {
									if (MatchToolValue(FormatStr,"BW256")) {
										SaveFormat = MI_BW256;
									}
									else {
										if (MatchToolValue(FormatStr,"HAM6")) {
											SaveFormat = MI_HAM6;
										}
										else {
											if (MatchToolValue(FormatStr,"HAM8")) {
												SaveFormat = MI_HAM8;
											}
											else {
												if (MatchToolValue(FormatStr,"DCTV3")) {
													SaveFormat = MI_DCTV3;
												}
												else {
													if (MatchToolValue(FormatStr,"DCTV4")) {
														SaveFormat = MI_DCTV4;
													}
													else {
														SaveFormat = MI_ILBM24;
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	Quality = MyArgInt("QUALITY",Quality,!firstcall);
	strcpy(PreScript,MyArgString("PRESCRIPT",PreScript,!firstcall));
	strcpy(PostScript,MyArgString("POSTSCRIPT",PostScript,!firstcall));
	strcpy(LoadScript,MyArgString("LOADSCRIPT",LoadScript,!firstcall));
	strcpy(PreviewScript,MyArgString("PREVIEW",PreviewScript,!firstcall));
	DX = MyArgInt("DX",DX,!firstcall);
	DY = MyArgInt("DY",DY,!firstcall);
	Depth = MyArgInt("DEPTH",Depth,!firstcall);
	RenderMode = MyArgInt("MODE",RenderMode,!firstcall);
	AntiAlias = MatchToolValue(MyArgString("ANTIALIAS",AntiAlias?"YES":"NO",!firstcall),"YES");
	Integer = MatchToolValue(MyArgString("INTEGER",Integer?"YES":"NO",!firstcall),"YES");
	GHelp = MatchToolValue(MyArgString("HELP",GHelp?"YES":"NO",!firstcall),"YES");
	// Update with the new parameters
	UpdateParams(OZoom);
}

// Update to new settings - OZoom is old zoom mode
void
UpdateParams(ULONG OZoom) {
	// remove all the menus
	RemoveMenus();
	// change all the menu items
	SwitchMenuItem(MM_PALETTE,palette,FALSE);
	if (ZoomAllowed && (Zoom != OZoom)) {
		// If windows are open and changing zoom then update the zoom
		if (ControlWindow) {
			DisableWindows(DI_Zoom);
			if (OZoom) {
				UnZoom();
			}
			else {
				ReZoom();
			}
			EnableWindows();
		}
		SwitchMenuItem(MM_ZOOM,Zoom,FALSE);
	}
	UpdateOpenMode(FALSE);
	SwitchMenuItem(MM_ICONSR,CreateIconsR,FALSE);
	SwitchMenuItem(MM_ICONSP,CreateIconsP,FALSE);
	SwitchMenuItem(MM_KEEPS,KeepSettings,FALSE);
	SwitchMenuItem(MM_ANTIALIAS,AntiAlias,FALSE);
	SwitchMenuItem(MM_INTEGER,Integer,FALSE);
	SwitchMenuItem(MM_HELP,GHelp,FALSE);
	UpdateRenderMode(FALSE);
	UpdateSaveFormat(FALSE);
	// Add the menus back
	AddMenus();
	// and disable/enable items
	if (PaletteAllowed) {
		if (TSMorphWnd) {
			OnMenu(TSMorphWnd,FULLMENUNUM(1,MM_PALETTE,NOSUB));
		}
		if (ControlWindow) {
			OnMenu(ControlWindow,FULLMENUNUM(M_SETTINGS,MM_PALETTE,NOSUB));
		}
	}
	else {
		if (TSMorphWnd) {
			OffMenu(TSMorphWnd,FULLMENUNUM(1,MM_PALETTE,NOSUB));
		}
		if (ControlWindow) {
			OffMenu(ControlWindow,FULLMENUNUM(M_SETTINGS,MM_PALETTE,NOSUB));
		}
	}
	if (ZoomAllowed) {
		if (TSMorphWnd) {
			OnMenu(TSMorphWnd,FULLMENUNUM(1,MM_ZOOM,NOSUB));
		}
		if (ControlWindow) {
			OnMenu(ControlWindow,FULLMENUNUM(M_SETTINGS,MM_ZOOM,NOSUB));
		}
	}
	else {
		if (TSMorphWnd) {
			OffMenu(TSMorphWnd,FULLMENUNUM(1,MM_ZOOM,NOSUB));
		}
		if (ControlWindow) {
			OffMenu(ControlWindow,FULLMENUNUM(M_SETTINGS,MM_ZOOM,NOSUB));
		}
	}
}

/* Preview render
 * Basically calls ARexx script with parameters - frame number and filename
 */
int
Preview(void) {
	UBYTE buffer[140];
	UBYTE buffer1[256];
	// save first if not saved
	if (!Saved) {
		if (!SaveRequester()) {
			return 1;
		}
	}
	if (GetFrameNumber()) {	// Get the frame number
		SaveSettings("T:TSMorph.prefs");	// save temporary settings
		DisableWindows(DI_Preview);		// disable all windows
		strcpy(buffer,PreviewScript);		// call ARexx script
		strcat(buffer," %ld %s");
		sprintf(buffer1,buffer,FrameNumber,savedfilename);
		SendRxMsg(buffer1,FALSE);
		EnableWindows();						// and re-enable windows
		return 1;
	}
	return 1;
}

/* Tries to execute a Rexx script
 * msgtxt = name of script
 * IgnoreError = TRUE then do not display error message
 * Returns:	error message
 */
LONG
SendRxMsg(char *msgtxt,BOOL IgnoreError) {
	struct MsgPort *reply_port;
	struct MsgPort *rx_port;
	struct RexxMsg *rx_msg;
	LONG 				ret 			= RC_FATAL;

	if (reply_port = CreateMsgPort()) {
   	if (rx_msg = CreateRexxMsg(reply_port,"TSM","REXX")) {
      	rx_msg->rm_Args[0] = msgtxt;
			if (FillRexxMsg(rx_msg,1,0)) {
				rx_msg->rm_Action = RXCOMM;
        		Forbid();
        		if (rx_port = (struct MsgPort *)FindPort("REXX")) {
          		PutMsg(rx_port, (struct Message *)rx_msg);
          		Permit();
          		WaitPort(reply_port);
          		ReplyMsg(GetMsg(reply_port));
          		ret = rx_msg->rm_Result1;
		     	}
        		else {
          		Permit();
        		}
        		ClearRexxMsg(rx_msg, 1);
      	}
     		DeleteRexxMsg(rx_msg);
    	}
    	DeleteMsgPort(reply_port);
  	}
	// Display an error message (if required)
  	if (!IgnoreError && ret) {
  		Error(MyGetMsg(MSG_EAREXX),MyGetMsg(MSG_OK),msgtxt,H_Arexx);
  	}
  	return ret;
}

struct Screen *myscreen = NULL;

/* Opens a custom screen
 * Public Screen name is ScreenName
 * Mode name is CustomName
 * Depth is CustomDepth
 * Most other stuff is cloned from the WorkBench
 *
 * This whole thing needs redoing a bit
 */
BOOL
OpenCustomScreen(void) {
	struct Screen *Screen;
	struct NameInfo buff;
	ULONG id = INVALID_ID;
	ULONG myid = INVALID_ID;
	BOOL used[256] = {0,0,0,0,0,0,0,0,	// table of in use palette entry
							0,0,0,0,0,0,0,0,	// we use the rest for grey scale
							0,0,0,0,0,0,0,0,
							0,0,0,0,0,0,0,0,
							0,0,0,0,0,0,0,0,
							0,0,0,0,0,0,0,0,
							0,0,0,0,0,0,0,0,
							0,0,0,0,0,0,0,0,
							};
	UWORD numcols;
	UWORD i;
	ULONG diff;
	UBYTE Color;
	ULONG Color32;
	BOOL usedri = TRUE;
	UWORD mydir = 0xFFFF;
	struct DrawInfo *dri;
	struct DrawInfo *mydri;
	BOOL retflag = TRUE;
	char *emsg;
	char *e1=NULL;
	ULONG err=0;

	// check we have a name and a mode and depth
	if (ScreenName && *ScreenName && CustomName && *CustomName && (CustomDepth > 0)) {
		// check public screen not already open
		if (!(Screen = LockPubScreen(ScreenName))) {
			// try and find modeid
			id = NextDisplayInfo(id);
			while ((id != INVALID_ID) && (myid == INVALID_ID)) {
				if (GetDisplayInfoData(NULL,(UBYTE *)&buff,sizeof(struct NameInfo),DTAG_NAME,id)) {
					if (!Stricmp(buff.Name,CustomName)) {
						myid = id;
					}
				}
				id = NextDisplayInfo(id);
			}
			if (myid != INVALID_ID) {
				// Get info from the workbench
				if (Screen = LockPubScreen("Workbench")) {
					if (dri = GetScreenDrawInfo(Screen)) {
						for (i = 0;
							  i < dri->dri_NumPens;
							  ++i) {
							if (!(dri->dri_Pens[i] < (1<<CustomDepth))) {
								usedri = FALSE;
							}
						}
						// open sort of cloned screen
						if (myscreen = OpenScreenTags(NULL,
									SA_Depth, CustomDepth,
									SA_Overscan, OSCAN_TEXT,
									SA_Pens, usedri?((ULONG) (dri->dri_Pens)):((ULONG) &mydir),
									SA_SysFont, 1,
									SA_DisplayID, myid,
									SA_Type, PUBLICSCREEN,
									SA_PubName, ScreenName,
									SA_Title, ScreenName,
									SA_FullPalette, TRUE,
									SA_Interleaved, TRUE,
									TAG_END)) {
							// set unused palette entries to grey scale
							if (mydri = GetScreenDrawInfo(myscreen)) {
								numcols = 1<<CustomDepth;
								for (i = 0;
									  i < mydri->dri_NumPens;
									  ++i) {
									if (!used[min(mydri->dri_Pens[i],256)]) {
										used[min(mydri->dri_Pens[i],256)] = TRUE;
										--numcols;
									}
								}
								if (numcols) {
						 			if (GfxBase->lib_Version > 38) {
										diff = (0xFFFFFFFF / (numcols + 1));
										Color32 = diff;
										if (!diff) {
											diff = 1;
										}
										for (i = 0;
											  (i < (1<<CustomDepth));
											  ++i) {
											if (!used[i]) {
												SetRGB32(&(myscreen->ViewPort),i,Color32,Color32,Color32);
												Color32 += diff;
											}
//											else {
//												if (i < (1<< dri->dri_Depth)) {
//													rgb = GetRGB4(Screen->ViewPort.ColorMap,i);
//													SetRGB4(&(myscreen->ViewPort),i,rgb>>8,(rgb & 0x0f0)>>4,rgb & 0x0f);
//												}
//											}
										}
								 	}
						 		 	else {
										diff = (16 / (numcols + 1));
										Color = diff;
										if (!diff) {
											diff = 1;
										}
										for (i = 0;
											  (i < (1<<CustomDepth));
											  ++i) {
											if (!used[i] && (Color<16)) {
												SetRGB4(&(myscreen->ViewPort),i,Color,Color,Color);
												Color += diff;
											}
//											else {
//												if (i < (1<< dri->dri_Depth)) {
//													rgb = GetRGB4(Screen->ViewPort.ColorMap,i);
//													SetRGB4(&(myscreen->ViewPort),i,rgb>>8,(rgb & 0x0f0)>>4,rgb & 0x0f);
//												}
//											}
										}
									}
								}
								// Free everything, set screen to public and return
								FreeScreenDrawInfo(myscreen,mydri);
							}
							PubScreenStatus(myscreen,NULL);
							FreeScreenDrawInfo(Screen,dri);
							UnlockPubScreen(NULL,Screen);
							OpenedScreen = TRUE;
							return TRUE;
						}
						// Free everything and display error
						else {
							retflag = FALSE;
							emsg = MyGetMsg(MSG_EOPSCR);
							err = HE_OScreen;
						}
						FreeScreenDrawInfo(Screen,dri);
					}
					else {
						retflag = FALSE;
						emsg = MyGetMsg(MSG_UNDRI);
						err = HE_DrawI;
					}
					UnlockPubScreen(NULL,Screen);
				}
				else {
					retflag = FALSE;
					emsg = MyGetMsg(MSG_LOCKWB);
					err = HE_LockW;
				}
			}
			else {
				retflag = FALSE;
				emsg = MyGetMsg(MSG_INVMODE);
				e1 = CustomName;
				err = HE_ScreenMode;
			}
		}
		else {
			UnlockPubScreen(NULL,Screen);
		}
	}
	if (!retflag) {
		Error(emsg,MyGetMsg(MSG_QUIT),e1,err);
	}
	return retflag;
}

/* Attempt to close the public screen
 *
 * Now does it sort of properly
 *
 * Has to reopen AmigaGuide if required
 */
void
CloseCustomScreen(void) {
	while (!CloseScreen(myscreen)) {
		// Open backdrop window to force requesters to screen
		TSMorphWnd = OpenWindowTags(NULL,
			WA_Width,			1,
			WA_Height,			1,
			WA_IDCMP,			0,
			WA_Flags,			WFLG_BACKDROP |
									WFLG_BORDERLESS |
									WFLG_NOCAREREFRESH,
			WA_PubScreen,		myscreen,
			TAG_END );
		if (AmigaGuideBase) {
			if (handle = OpenAmigaGuideAsync(&nag, NULL)) {
				ASig = AmigaGuideSignal(handle);
			}
		}
		Error("TSMorph is attempting to close this screen.\n"
				"Please close all windows on this screen.",
				"Retry","",HE_CScreen);
		if (handle) {
			CloseAmigaGuide(handle);
			handle = NULL;
			ASig = NULL;
		}
		if (TSMorphWnd) {
			CloseWindow(TSMorphWnd);
			TSMorphWnd = NULL;
		}
	}
}

/* Unlinks all points and triangulates */
void
Triangulate(void) {
	// Open window and wait till close selected then close it
  	if (!Saved) {
		if (!SaveRequester()) {
			return;
		}
	}
	DisableWindows(DI_Triangulate);
	UnlinkAllPoints(TRUE);
	GenerateTriangles();
	Saved = FALSE;
	EnableWindows();
}

/* nsort() was written by Dave Watson and uses the algorithm described in -
 *    Watson, D.F., 1981, Computing the n-dimensional Delaunay tessellation with 
 *          application to Voronoi polytopes: The Computer J., 24(2), p. 167-172. 
 */

#define SQ(x)				(x) * (x)
#define BIGNUM				1E37
#define EPSILON         0.00001
#define TSIZE				75                  /* temporary storage size factor*/
#define XRANGE          10.0                /* factor (>=1) for radius of control points */
#define FILENAMELEN     32
#define AND             &&
#define OR              ||
#define EQ              ==
#define NE              !=

BOOL
GenerateTriangles(void) {
	BOOL ok = FALSE;
	BOOL flag = TRUE;
   double xx, bgs, **mxy, **wrk, **pts, **ccr;
   int i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i11, ii[3], 
       dm, nts, tsz, *id, **tmp, **a3s;
   struct MyPoint *MyPoint,**MyPoints,*MyPoint1,*MyPoint2,*MyPoint3;
	ULONG PointCount;

	PointCount = 0;
	for (MyPoint = (struct MyPoint *)PointList.lh_Head;
			MyPoint->MyNode.mln_Succ;
			MyPoint = (struct MyPoint *)MyPoint->MyNode.mln_Succ) {
		PointCount++;
	}
if (PointCount && (MyPoints = (struct MyPoint **)AllocVec((sizeof (struct MyPoint *))*PointCount,MEMF_CLEAR))) {
   if (mxy = DoubleMatrix(2, 2)) {
	   for (i0=0; i0<2; i0++) {
	  		mxy[0][i0] = - (mxy[1][i0] = BIGNUM);
	   }
	   if (wrk = DoubleMatrix(2, 3)) {
		   for (i0=0; i0<2; i0++) {
		   	for (i1=0; i1<3; i1++) {
		   		wrk[i0][i1] = -XRANGE;
		   	}
		   }
		   for (i0=0; i0<2; i0++) {
		   	wrk[i0][i0] = XRANGE * (3 * 2 - 1);
		   }
			if (pts = DoubleMatrix(PointCount + 3, 2)) {
				for (i0=0, MyPoint = (struct MyPoint *)PointList.lh_Head;
						i0<PointCount;
						MyPoint = (struct MyPoint *)MyPoint->MyNode.mln_Succ, i0++) {
					MyPoints[i0] = MyPoint;
					pts[i0][0] = (MyPoint->x + MyPoint->x1)>>1;
					pts[i0][1] = (MyPoint->y + MyPoint->y1)>>1;
					for (i1=0; i1<2; i1++) {
						if (mxy[0][i1] < pts[i0][i1]) {
							mxy[0][i1] = pts[i0][i1];
						}
						if (mxy[1][i1] > pts[i0][i1]) {
							mxy[1][i1] = pts[i0][i1];
						}
					}
				}
				for (bgs=0, i0=0; i0<2; i0++) {
					mxy[0][i0] -= mxy[1][i0];
			      if (bgs < mxy[0][i0]) {
			      	bgs = mxy[0][i0];
			      }
			   }
				bgs *= EPSILON;
			   for (i0=0; i0<PointCount; i0++) {
			   	for (i1=0; i1<2; i1++) {
						pts[i0][i1] += bgs * (0.5 - (double)rand() / 0x7fffffff);
					}
				}
				for (i0=0; i0<3; i0++) {
					for (i1=0; i1<2; i1++) {
						pts[PointCount+i0][i1] = mxy[1][i1] + wrk[i1][i0] * mxy[0][i1];
					}
				}
			   for (i1=1, i0=2; i0<3; i0++) {
			   	i1 *= i0;
			   }
			   tsz = TSIZE * i1;
			   if (tmp = IntMatrix(tsz + 1, 2)) {
				   i1 *= (PointCount + i1);
				   if (id = IntVect(i1)) {
					   for (i0=0; i0<i1; i0++) {
					   	id[i0] = i0;
					   }
						if (a3s = IntMatrix(i1,3)) {
						  	if (ccr = DoubleMatrix(i1,3)) {
							  	for (a3s[0][0]=PointCount, i0=1; i0<3; i0++) {
							  		a3s[0][i0] = a3s[0][i0-1] + 1;
							  	}
								for (ccr[0][2]=BIGNUM, i0=0; i0<2; i0++) {
									ccr[0][i0] = 0;
								}
							   nts = i4 = 1;
							   dm = 2 - 1;
							   for (i0=0; i0<PointCount; i0++) {
							      i1 = i7 = -1;
							      i9 = 0;
							      for (i11=0; i11<nts; i11++) {
							         i1++;
							         while (a3s[i1][0] < 0) { 
							         	i1++;
							         }
							         xx = ccr[i1][2];
							         for (i2=0; i2<2; i2++) {
							            xx -= SQ(pts[i0][i2] - ccr[i1][i2]);
										   if (xx<0) {
										   	goto Corner3;
										   }
							         }
							         i9--;
										i4--;
										id[i4] = i1;
										for (i2=0; i2<3; i2++) {
										   ii[0] = 0;
							            if (ii[0] EQ i2) {
							            	ii[0]++;
							            }
							            for (i3=1; i3<2; i3++) {
							               ii[i3] = ii[i3-1] + 1;
							               if (ii[i3] EQ i2) {
							               	ii[i3]++;
							               }
							            }
							            if (i7>dm) {
											   i8 = i7;
												for (i3=0; i3<=i8; i3++) {
												   for (i5=0; i5<2; i5++) {
												   	if (a3s[i1][ii[i5]] NE tmp[i3][i5]) {
												   		goto Corner1;
												   	}
												   }
													for (i6=0; i6<2; i6++) {
														tmp[i3][i6] = tmp[i8][i6];
													}
													i7--;
													goto Corner2;
Corner1:;
												}
											}
											if (++i7 > tsz) {
												goto returnfail;
											}
											for (i3=0; i3<2; i3++) {
												tmp[i7][i3] = a3s[i1][ii[i3]];
											}
Corner2:;
										}
								      a3s[i1][0] = -1;
Corner3:;
								   }
								   for (i1=0; i1<=i7; i1++) {
								      for (i2=0; i2<2; i2++) {
								   		for (wrk[i2][2]=0, i3=0; i3<2; i3++) {
								            wrk[i2][i3] = pts[tmp[i1][i2]][i3] - pts[i0][i3];
										   	wrk[i2][2] += wrk[i2][i3] * (pts[tmp[i1][i2]][i3] + pts[i0][i3]) / 2;
											}
										}
										xx = wrk[0][0] * wrk[1][1] - wrk[1][0] * wrk[0][1];
										ccr[id[i4]][0] = (wrk[0][2] * wrk[1][1] - wrk[1][2] * wrk[0][1]) / xx;
										ccr[id[i4]][1] = (wrk[0][0] * wrk[1][2] - wrk[1][0] * wrk[0][2]) / xx;
								      for (ccr[id[i4]][2]=0, i2=0; i2<2; i2++) {
								        	ccr[id[i4]][2] += SQ(pts[i0][i2] - ccr[id[i4]][i2]);
										   a3s[id[i4]][i2] = tmp[i1][i2];
								      }
										a3s[id[i4]][2] = i0;
										i4++;
								      i9++;
								   }
								   nts += i9;
								}
								i0 = -1;
								for (i11=0; flag && (i11<nts); i11++) {
									i0++;
								   while (a3s[i0][0] < 0) {
								   	i0++;
								   }
								   if (a3s[i0][0] < PointCount) {
										for (i1=0; i1<2; i1++) for (i2=0; i2<2; i2++)  {
								      	wrk[i1][i2] = pts[a3s[i0][i1]][i2] - pts[a3s[i0][2]][i2];
								      }
								      xx = wrk[0][0] * wrk[1][1] - wrk[0][1] * wrk[1][0];
								      if (fabs(xx) > EPSILON) {
								      	MyPoint1 = MyPoints[a3s[i0][0]];
								      	MyPoint2 = MyPoints[a3s[i0][1]];
								      	MyPoint3 = MyPoints[a3s[i0][2]];
							      		LinkPoints(MyPoint1,MyPoint2);
							      		LinkPoints(MyPoint2,MyPoint3);
							      		LinkPoints(MyPoint3,MyPoint1);
								      	// Could set flag
								      }
								   }
								}
							   ok = flag;
returnfail:
								FreeMatrixd(ccr);
							}
							FreeMatrixi(a3s);
						}
						MyFreeVecti(id);
					}
				   FreeMatrixi(tmp);
				}
				FreeMatrixd(pts);
			}
			FreeMatrixd(wrk);
		}
		FreeMatrixd(mxy);
	}
	FreeVec(MyPoints);
}
else {
	ok = FALSE;
}
return ok;
}
	
int
*IntVect(int ncols) {  
	return (int *) AllocVec(ncols * sizeof(int),0);
}

void
MyFreeVecti(int *vectptr) {
	if (vectptr) {
		FreeVec(vectptr);
	}
}

int
**IntMatrix(int nrows, int ncols) {
   int i0;
   int **matptr;
   if (nrows<2) nrows = 2;
   if (ncols<2) ncols = 2;
   if ((matptr = (int **) AllocVec(nrows * sizeof(int *),0)) EQ NULL) {
   	return NULL;
   }
   if ((matptr[0] = (int *) AllocVec(nrows * ncols * sizeof(int),0)) EQ NULL) {
   	FreeVec(matptr);
   	return NULL;
   }
   for (i0=1; i0<nrows; i0++) {
   	matptr[i0] = matptr[0] + i0 * ncols;
   }
   return matptr;
}

void
FreeMatrixi(int **matptr) {  
	if (matptr) {
		if (matptr[0]) {
			FreeVec(matptr[0]);
		}
		FreeVec(matptr);
	}
}

double
**DoubleMatrix(int nrows, int ncols) {
   int i0;
   double **matptr;
   if (nrows<2) nrows = 2;
   if (ncols<2) ncols = 2;
   if ((matptr = (double **) AllocVec(nrows * sizeof(double *),0)) EQ NULL) {
   	return NULL;
   }
   if ((matptr[0] = (double *) AllocVec(nrows * ncols * sizeof(double),0)) EQ NULL) {
   	FreeVec(matptr);
   	return NULL;
   }
   for (i0=1; i0<nrows; i0++) {
	   matptr[i0] = matptr[0] + i0 * ncols;
	}
   return matptr;
}

void
FreeMatrixd(double **matptr) {
	if (matptr) {
		if (matptr[0]) {
		   FreeVec(matptr[0]);
		}
		FreeVec(matptr);
	}
}

