#define DEBUG

/* :ts=4
 * M A N D E L B R O T   C O N S T R U C T I O N   S E T
 * 
 * MenuPick and some related things
 */

#include <exec/types.h>
#ifndef EXEC_SEMAPHORES_H
#include <exec/semaphores.h>
#endif
#include <intuition/intuition.h>
#ifdef DEBUG
#	include <stdio.h>
#	undef STATIC
#	define STATIC	/* EMPTY */
#endif
#include "mandel.h"

STATIC TEXT FileName[FNAME_SIZE+1] = "Mandel.pic";
STATIC TEXT DirName[DNAME_SIZE+2]  = "df0:";
STATIC UBYTE Buffer[5][20];

/* Forward declarations of static procedures */
void PrjNew();

void GotMenu(Code)
USHORT Code;
{
	static void (*MenuFunc[])()= { 
		CprMenu, PrjMenu, OptMenu, FunMenu 	};
	while (Code != MENUNULL) {
		(*MenuFunc[MENUNUM(Code)]) (Code);
		Code = ItemAddress(MandelMenu, (long) Code) -> NextSelect;
		/* This tends to make endless loops possible ... */
	}
}

void CprMenu(Code)
USHORT Code;
{
	ULONG OldIDCMP = MainWindow->IDCMPFlags;

	static struct IntuiText Body[] =
	{  
		{   MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
			10, 15, NULL, (UBYTE *)"Mandelbrot Construction Set", &Body[1] },
		{   MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
			10, 30, NULL, (UBYTE *)"By KosmoSoft Productions", &Body[2] },
		{   MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
			10, 40, NULL, (UBYTE *)"15 September 1987 V1.0", &Body[3] },
		{   MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
			10, 55, NULL, (UBYTE *)"Ohh, please Copy-Me!", NULL }
	},
	ExitText =
	{   MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
	    AUTOLEFTEDGE, AUTOTOPEDGE, NULL, (UBYTE *)" OK ", NULL };

	ModifyIDCMP(MainWindow, OldIDCMP &~ (MENUVERIFY | SIZEVERIFY | REQVERIFY));
	AutoRequest(MainWindow, &Body[0], NULL, &ExitText, NULL, NULL, 258L, 100L);
	ModifyIDCMP(MainWindow, OldIDCMP);
}

STATIC void PrjMenu(Code)
USHORT Code;
{
	int SubNum = SUBNUM(Code);
	int ItemNum = ITEMNUM(Code);

	struct Mand MandChunk;
	struct BitMap *bitmap;
	struct ILBM_info *ilbminfo;
	unsigned char ea_colormap[3*MAXCOL];
	char Name[DNAME_SIZE + FNAME_SIZE + 3];

	switch (ItemNum) {
	case PRJNEW: PrjNew(SubNum); break;
	case PRJOPN:
		if ( get_fname(MainWindow, "Select a filename to LOAD",
				FileName, DirName) == NULL ) break;
		strcpy(Name, DirName);
		if (Name[strlen(Name) - 1] != ':') strcat(Name, "/");
		strcat(Name, FileName);
		StopDrawing();
		MandChunk.MandID = 0;
		if (ilbminfo =  read_iff(Name, 
			(short)FALSE, sizeof(MandChunk), &MandChunk)) {
			put_ea_cmap(&ilbminfo->cmap, NumColors, MandelScreen);
			InterpretMAND(&MandChunk);
		}
		Saved = TRUE;
		break;
	case PRJSVE:	/* Save */
		if (NameValid) skipto prjsve;
		/* Fall Through */
	case PRJSVA:	/* Save As */
		if ( get_fname(MainWindow, "Select a filename to SAVE",
				FileName, DirName) == NULL ) break;
prjsve:
		strcpy(Name, DirName);
		if (Name[strlen(Name) - 1] != ':') strcat(Name, "/");
		strcat(Name, FileName);
		get_ea_cmap(ea_colormap, NumColors, MandelScreen);
		MakeMAND(&MandChunk);
		bitmap = MainWindow->RPort->BitMap;
/*** Warning: This MoveWindow will not happen immediately!!!    ***/
/*** For some reason, this will deadlock Intuition, if I        ***/
/*** uncomment both MoveWindow and DoBorderless. It seems to    ***/
/*** collide with the closing filename requester window...      ***/
/*** Maybe because the locking of the layers interfere buggyly? ***/
/*** Anyhow, if I LockLayers(), then the GZZ border doesn't     ***/
/*** get updated until I UnlockLayers(), and the mouse          ***/
/*** pointer is frozen! But we have to suspend the drawing      ***/
/*** task somehow, to prevent creating a corrupt IFF file.      ***/
/*** Who said `Very near to betting that it can't be            ***/
/*** deadlocked' ?? [allchanges file: intuition 290]            ***/
		/* 	MoveWindow(MainWindow, (long)-MainWindow->LeftEdge,
			(long)-MainWindow->TopEdge); */
		/* DoBorderless(MainWindow); */
		SuspendDrawing();
		NameValid = Saved = write_iff(Name, ea_colormap, bitmap,
				(short) 0, (short) 0, (short) bitmap->BytesPerRow * 8,
				(short) TRUE, sizeof(MandChunk), &MandChunk);
		ResumeDrawing();
		break;
	case PRJQUI:
		if (StillDrawing)
			StopDrawing();
		else
			finished = TRUE;
	}
}

STATIC void OptMenu(Code)
USHORT Code;
{
	int SubNum = SUBNUM(Code);
	int ItemNum = ITEMNUM(Code);

	switch (ItemNum) {
	case OPTCOL:
		switch (SubNum) {
		case OCSEL:
			SelectMenu(MENU(OPTMENU, OPTCOL, OCSEL), (bool)FALSE);
			UnImpl(); break;
		case OCMOD:
			PenTableMode = MODULO; break;
		case OCRAN:
			PenTableMode = RANGES; break;
		case OCPAL:
			Palette(MainWindow);
		} /* End Switch SUBNUM */
		InitPenTable();
		break;
	case OPTRES:
		switch (SubNum) {
		case ORNRM: PixelStep = 1; break;
		case OR12:  PixelStep = 2; break;
		case OR13:  PixelStep = 3; break;
		case OR14:  PixelStep = 4; break;
		case ORFIL:
			DrawPicture((bool)TRUE); break;	/* Fill in */
		case ORHI:
		case ORILC: 
			if (!Sure()) break;
			{
				USHORT newmode = MandelNScreen.ViewModes & ~(HIRES | LACE);
				bool WasBorderless;

				if (ItemAddress(MandelMenu, MENU(OPTMENU, OPTRES, ORHI))
					-> Flags & CHECKED)
						newmode |= HIRES;
				if (ItemAddress(MandelMenu, MENU(OPTMENU, OPTRES, ORILC))
					-> Flags & CHECKED)
						newmode |= LACE;
				if (newmode != MandelNScreen.ViewModes) {
					WasBorderless = CleanupDisplay((bool) TRUE);
					MandelNScreen.ViewModes = newmode;
					if (InitDisplay(WasBorderless)) { /* Trouble */
						MandelNScreen.ViewModes &= ~(HIRES | LACE);
						if (InitDisplay((bool) FALSE))
							MyExit("Can't re-init display - Maybe low on memory");
						SelectMenu(MENU(OPTMENU, OPTRES, ORHI), (bool)FALSE);
						SelectMenu(MENU(OPTMENU, OPTRES, ORILC), (bool)FALSE);
						SelectMenu(MENU(OPTMENU, OPTRES, ORBCK), (bool)FALSE);
					}
				}
			}
			break;
		case ORBCK: 
			if (ItemAddress(MandelMenu, MENU(OPTMENU,OPTRES,ORBCK))->Flags & CHECKED)
				DoBorderless(MainWindow, &borderinfo);
			else
				UndoBorderless(MainWindow, &borderinfo);
		} /* End Switch SUBNUM */
		break;
	case OPTPAR: Parameters(); break;
	} /* End Switch ITEMNUM */
}

void FunMenu(Code)
USHORT Code;
{
	switch (ITEMNUM(Code)) {
	case FUN1:
		WritePixelDepth = ZQuadMinC; break;
	case FUN2:
		WritePixelDepth = ZC1MinZ; break;
	case FUN3:
		WritePixelDepth = Z3PlusZCMin1MinC; break;
	}
}

void UnImpl()
{
	static char alert[] = "\
\0\144\25Mandelbrot Construction Set -- By KosmoSoft Productions\0a\
\0\170\40Sorry, this function has not been implemented yet!\0";
	DisplayAlert(RECOVERY_ALERT, alert, 50L);
}

STATIC USHORT OldMinWidth, OldMinHeight, OldMaxWidth, OldMaxHeight;

/* Do not nest calls to DisableSizing: the original values will be lost. */

STATIC void DisableSizing()
{
	OldMinWidth = MainWindow->MinWidth;
 	OldMaxWidth = MainWindow->MaxWidth;
	OldMinHeight = MainWindow->MinHeight;
	OldMaxHeight = MainWindow->MaxHeight;
	WindowLimits(MainWindow, (long) MainWindow->Width, (long) MainWindow->Height,
					(long) MainWindow->Width, (long) MainWindow->Height);
}

STATIC void EnableSizing()
{
	WindowLimits(MainWindow, (long) OldMinWidth, (long) OldMinHeight,
					(long) OldMaxWidth, (long) OldMaxHeight);
}

/* Complex multiplication using pointers to reduce overhead.   */
/* YOU must make sure there are no (dynamic) aliases around... */

STATIC void MulCplx(ReRes, ImRes, ReA, ImA, ReB, ImB)
double *ReRes, *ImRes, *ReA, *ImA, *ReB, *ImB;
{
	*ReRes = *ReA * *ReB - *ImA * *ImB;
	*ImRes = *ImA * *ReB + *ReA * *ImB;
}

/* Z^2-C: 4 multiplications per loop */

void ZQuadMinC(x, y, ReC, ImC)
long x, y;
double ReC, ImC;
{
	double ReZ = 0.0, ImZ = 0.0;
	double ReQuad, ImQuad;
	int Depth = -1;

	while (ImQuad=ImZ*ImZ, ReQuad=ReZ*ReZ, Depth++,
		(ImQuad + ReQuad < 8) && (Depth <= MaxDepth) )
	{
		ImZ = 2 * ImZ * ReZ - ImC;
		ReZ = ReQuad - ImQuad - ReC;
	}

	if (Depth > MaxDepth)
		Depth = PenTable[0];
	else 
		Depth = PenTable[Depth];

	SetAPen(MainWindow->RPort, (long) Depth);
	WritePixel(MainWindow->RPort, x, y);
}

/* Z*C*(1-Z): 10 multiplications per loop */

void ZC1MinZ(x, y, ReC, ImC)
long x, y;
double ReC, ImC;
{
	static double ReZ, ImZ;
	static double NewReZ, NewImZ;
	static double Re1MinZ, Im1MinZ;
	int Depth = -1;

	ReZ = ReC;
	ImZ = ImC;

	while (Depth++, (ImZ*ImZ + ReZ*ReZ < 8) && (Depth <= MaxDepth) )
	{
		Re1MinZ = 1 - ReZ;
		Im1MinZ = -ImZ;
		MulCplx(&NewReZ, &NewImZ, &ReZ, &ImZ, &ReC, &ImC);
		MulCplx(&ReZ, &ImZ, &NewReZ, &NewImZ, &Re1MinZ, &Im1MinZ);
	}

	if (Depth > MaxDepth)
		Depth = PenTable[0];
	else 
		Depth = PenTable[Depth];

	SetAPen(MainWindow->RPort, (long) Depth);
	WritePixel(MainWindow->RPort, x, y);
}

/* Z^3+Z*(C-1)-C: 12 multiplications per loop */

void Z3PlusZCMin1MinC(x, y, ReC, ImC)
long x, y;
double ReC, ImC;
{
	static double ReZ, ImZ;
	static double ReCMin1;
	static double ReZ2, ImZ2, ReZ3, ImZ3;
	double ReQuad, ImQuad;
	int Depth = -1;

	ReZ = ReC;
	ImZ = ImC;

	while (ImQuad=ImZ*ImZ, ReQuad=ReZ*ReZ, Depth++,
		(ImQuad + ReQuad < 8) && (Depth <= MaxDepth) )
	{
		/* Calculate z^2 */
		ReZ2 = ReQuad - ImQuad;
		ImZ2 = 2 * ReZ * ImZ;

		/* Make z^3 */
		MulCplx(&ReZ3, &ImZ3, &ReZ2, &ImZ2, &ReZ, &ImZ);

		/* Calculate z(c-1) while destroying z^2 */
		ReCMin1 = ReC - 1;
		MulCplx(&ReZ2, &ImZ2, &ReZ, &ImZ, &ReCMin1, &ImC);

		/* Add everything */
		ReZ = ReZ3 + ReZ2 - ReC;
		ImZ = ImZ3 + ImZ2 - ImC;
	}

	if (Depth > MaxDepth)
		Depth = PenTable[0];
	else 
		Depth = PenTable[Depth];

	SetAPen(MainWindow->RPort, (long) Depth);
	WritePixel(MainWindow->RPort, x, y);
}

/* Some *VERY PRIVATE* variables */

STATIC struct Task *DrawTask = NULL;
STATIC struct Task *MandelTask = NULL;
STATIC struct SignalSemaphore DrawSemaphore;

/* This is what it is all about! */

STATIC bool MyFillIn;	/* Parameter for new task */

STATIC void ActuallyDrawPicture()
{
	static double x, y, Leftx, MyXstep, MyYstep;
	static long px, py, Leftpx, minpx, maxpx, minpy, maxpy;
	static int MyPixelStep, XOffset, YOffset;

	register void (*Function)();

	geta4();		/* Manx small memory model */

	ObtainSemaphore(&DrawSemaphore);

	StillDrawing = TRUE;
	DisableSizing();
	StopFraming();
	OffMenu(MainWindow, (ULONG) SHIFTMENU(PRJMENU) | SHIFTITEM(PRJNEW) |
		SHIFTSUB(NOSUB) );
	OffMenu(MainWindow, (ULONG) SHIFTMENU(OPTMENU) | SHIFTITEM(OPTRES) |
		SHIFTSUB(ORFIL) );

	if (!MyFillIn && !Sure())	skipto exit;
	if (!MyFillIn)	NameValid = FALSE;
	Saved = FALSE;
	MyPixelStep = PixelStep;
	XOffset = 0;
	YOffset = MyFillIn? 1: 0;

	minpx = 0;
	maxpx = MainWindow->GZZWidth - 1;
	minpy = 0;
	maxpy = MainWindow->GZZHeight - 1;

	CalcCSteps();

	if (!MyFillIn) {	/* Clear the window */
		SetAPen(MainWindow->RPort, (long) PenTable[0]);
		SetDrMd(MainWindow->RPort, (long) JAM1);
		RectFill(MainWindow->RPort, minpx, minpy, maxpx, maxpy);
	}

	MyXstep = MyPixelStep * CXStep;
	MyYstep = MyPixelStep * CYStep;

	Function = WritePixelDepth;

	ReleaseSemaphore(&DrawSemaphore);

again:
	Leftpx = minpx + XOffset;	/* Start in the upper left-hand corner */
	py = minpy + YOffset;		/* of the window */
	Leftx = LeftEdge + XOffset * CXStep;
	y = TopEdge - YOffset * CYStep;

	for ( ; py <= maxpy; y -= MyYstep, py += MyPixelStep) {
		ObtainSemaphore(&DrawSemaphore);
		SetDrMd(MainWindow->RPort, (long) JAM1);
		for (px=Leftpx, x=Leftx ; px <= maxpx; x += MyXstep, px += MyPixelStep) {
			(*Function)(px, py, x, y);
			/* Check if we are asked to terminate. Don't release s'phore. */
			if (StillDrawing < 0)	skipto exit;
		}
		ReleaseSemaphore(&DrawSemaphore);
	}

	if (MyFillIn && (++YOffset < MyPixelStep) ) {
		backto again;		 /* Draw pixels below current pixel */
	}
	if (MyFillIn && (++XOffset < MyPixelStep) ) {
		YOffset = 0;		/* and next to them */
		backto again;
	}

exit:
	DisplayBeep(MandelScreen);
	ObtainSemaphore(&DrawSemaphore);
	EnableSizing();
	OnMenu(MainWindow, (ULONG) SHIFTMENU(PRJMENU) | SHIFTITEM(PRJNEW) |
		SHIFTSUB(NOSUB) );
	OnMenu(MainWindow, (ULONG) SHIFTMENU(OPTMENU) | SHIFTITEM(OPTRES) |
		SHIFTSUB(ORFIL) );
	DrawTask = NULL;
	if (StillDrawing < 0) {
		/* Release the semaphore now. The main task waits for it. */
		ReleaseSemaphore(&DrawSemaphore);
	}
	StillDrawing = FALSE;	/* We are finished, finally... */
	ReleaseSemaphore(&DrawSemaphore);
	DisplayBeep(MandelScreen);
}

void DrawPicture(FillIn)
bool FillIn;
{
	BYTE Priority;

	MandelTask = FindTask(NULL);
	Priority = MandelTask->tc_Node.ln_Pri;
	MyFillIn = FillIn;

	InitSemaphore(&DrawSemaphore);

	DrawTask = CreateTask("Mandelbrot_Drawing.task", (long) Priority,
		ActuallyDrawPicture, 2048L);

}

/* Interface because I wanted to CreateTask the drawing... */

void StopDrawing()
{
	if (StillDrawing) {
		StillDrawing = -1;	/* Indicate termination is wanted. */

		/* In worst-case I think we will make the loop at most twice. */
		/* This is only when the drawing task has not obtained it, */
		/* which is only for very little of the time. */
		do {
			ObtainSemaphore(&DrawSemaphore);
			ReleaseSemaphore(&DrawSemaphore);
		} while (StillDrawing < 0);
	}
}

/* This is the external interface to the DrawSemaphore. Use it with care. */
/* Always balance calls to SuspendDrawing() with ResumeDrawing() !!! */

void SuspendDrawing()
{
	ObtainSemaphore(&DrawSemaphore);
}

void ResumeDrawing()
{
	ReleaseSemaphore(&DrawSemaphore);
}

STATIC void PrjNew(SubNum)
USHORT SubNum;
{
	struct Window *window;
	int ID;
	static double HShift = 0.0,
		VShift = 0.0;
	double Width = RightEdge - LeftEdge,
		Height = BottomEdge - TopEdge,
		NewLeftEdge = LeftEdge,
		NewTopEdge = TopEdge,
		NewRightEdge = RightEdge,
		NewBottomEdge = BottomEdge;

	/* Stuff for the ABSOLUTE requester */

	static struct IntuiText RatioText = {
		MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, 10, 90, NULL,
		(UBYTE *) "Ratio:                    ", NULL	};
	static struct IntuiText AbsText = {
		MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, 10, 10, NULL,
		(UBYTE *) "Select an absolute position", &RatioText	};
	static struct IntuiText LRTBText[] = {
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, -56, 0, NULL,
			(UBYTE *) "Left", NULL	},
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, -56, 0, NULL,
			(UBYTE *) "Right", NULL	},
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, -56, 0, NULL,
			(UBYTE *) "Top", NULL	},
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, -56, 0, NULL,
			(UBYTE *) "Bottom", NULL	}
	};
	static struct StringInfo LRTBinfo[] = {
		{	&Buffer[0][0], &Buffer[4][0], 0, 20, 0	},
		{	&Buffer[1][0], &Buffer[4][0], 0, 20, 0	},
		{	&Buffer[2][0], &Buffer[4][0], 0, 20, 0	},
		{	&Buffer[3][0], &Buffer[4][0], 0, 20, 0	}
	};
	static struct Gadget LRTBGadget[] = {
		{	&LRTBGadget[1], 66, 30, 160, 10,			/* next, LTWH */
			GADGHCOMP,									/* Flags */
			RELVERIFY,									/* Activation */
			STRGADGET | REQGADGET,						/* GadgetType */
			(APTR) NULL, NULL,							/* rendering */
			&LRTBText[0], 0, (APTR) &LRTBinfo[0],		/* "Left" */
			NEGGADGETID+1, NULL	},
		{	&LRTBGadget[2], 66, 45, 160, 10,			/* next, LTWH */
			GADGHCOMP,
			RELVERIFY,
			STRGADGET | REQGADGET,
			(APTR) NULL, NULL,
			&LRTBText[1], 0, (APTR) &LRTBinfo[1],		/* Right */
			NEGGADGETID+1, NULL	},
		{	&LRTBGadget[3], 66, 60, 160, 10,			/* next, LTWH */
			GADGHCOMP,
			RELVERIFY,
			STRGADGET | REQGADGET,
			(APTR) NULL, NULL,
			&LRTBText[2], 0, (APTR) &LRTBinfo[2],		/* Top */
			NEGGADGETID+1, NULL	},
		{	NULL, 66, 75, 160, 10,						/* next, LTWH */
			GADGHCOMP,
			RELVERIFY,
			STRGADGET | REQGADGET,
			(APTR) NULL, NULL,
			&LRTBText[3], 0, (APTR) &LRTBinfo[3],		/* Bottom */
			NEGGADGETID+1, NULL	}
	};
	static struct Requester AbsRequest = {
		NULL, 25, 40, 260, 130, 0,0, &PositiveGadget, NULL,
		&AbsText, 0, 1	};

	/* Stuff for the SHIFT requester */

	static struct IntuiText ShiftText = {
		MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, 10, 10, NULL,
		(UBYTE *) "Select a window shift amount", NULL	};
	static struct IntuiText RDText[] = {
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, -56, 0, NULL,
			(UBYTE *) "Right", NULL	},
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, -56, 0, NULL,
			(UBYTE *) "Down", NULL	}
	};
	static struct Gadget RDGadget[] = {
		{	&RDGadget[1], 66, 45, 160, 10,			/* next, LTWH */
			GADGHCOMP,
			0,
			STRGADGET | REQGADGET,
			(APTR) NULL, NULL,
			&RDText[0], 0, (APTR) &LRTBinfo[0],		/* Right */
			0, NULL	},
		{	NULL, 66, 60, 160, 10,					/* next, LTWH */
			GADGHCOMP,
			0,
			STRGADGET | REQGADGET,
			(APTR) NULL, NULL,
			&RDText[1], 0, (APTR) &LRTBinfo[1],		/* Down */
			0, NULL	}
	};
	static struct Requester ShiftRequest = {
		NULL, 25, 40, 260, 130, 0,0, &PositiveGadget, NULL,
		&ShiftText, 0, 1	};

	switch (SubNum) {
	case PNRED: 
		if (MouseStatus != FLASHING) return;
		else {
			register SHORT w = MainWindow -> GZZWidth,
						   h = MainWindow -> GZZHeight;
			register SHORT X1, Y1, X2, Y2;

			/* If you cast less, Aztec seems to do it wrong... */
			X1 = (-(long)FrameX1 * w) / ((long)FrameX2 - (long)FrameX1);
			Y1 = (-(long)FrameY1 * h) / ((long)FrameY2 - (long)FrameY1);
			X2 = ((long)(w-1-FrameX1) * w) / ((long)FrameX2 - (long)FrameX1)
				- 1;
			Y2 = ((long)(h-1-FrameY1) * h) / ((long)FrameY2 - (long)FrameY1)
				- 1;
#ifdef DEBUG2
			fprintf(stderr,"REDUCE: w=%d h=%d X1=%d Y1=%d X2=%d Y2=%d\n",
				w, h, X1, Y1, X2, Y2);
			fprintf(stderr,"        FrameX1=%d FrameY1=%d FrameX2=%d FrameY2=%d\n",
				FrameX1, FrameY1, FrameX2, FrameY2);
#endif

			NewLeftEdge   = LeftEdge + X1 * CXStep;
			NewRightEdge  = LeftEdge + X2 * CXStep;
			NewTopEdge    =  TopEdge - Y1 * CYStep;
			NewBottomEdge =  TopEdge - Y2 * CYStep;

			skipto pnabs;
		}
	case PNENL:
		if (MouseStatus != FLASHING) return;

		NewLeftEdge   = LeftEdge + FrameX1 * CXStep;
		NewRightEdge  = LeftEdge + FrameX2 * CXStep;
		NewTopEdge    =  TopEdge - FrameY1 * CYStep;
		NewBottomEdge =  TopEdge - FrameY2 * CYStep;
		skipto pnabs;
	case PNSHF:
		NegativeGadget.NextGadget = &RDGadget[0];
		do {
			sprintf(Buffer[0], "%1.6g",HShift);
			sprintf(Buffer[1], "%1.6g",VShift);

			window = MyRequest(&ShiftRequest, MainWindow);
			ID = WaitMyRequest(window);
			EndMyRequest(&ShiftRequest, window, MainWindow);
			if (ID == NEGGADGETID) return;

		} while (sscanf(Buffer[0], "%lf", &HShift)+
				sscanf(Buffer[1], "%lf", &VShift) != 2);

		NewLeftEdge   = LeftEdge + HShift * Width;
		NewRightEdge  = RightEdge + HShift * Width;
		NewTopEdge    = TopEdge + VShift * Height;
		NewBottomEdge = BottomEdge + VShift * Height;

		skipto pnabs;
	case PNABS:
		NewLeftEdge = LeftEdge;
		NewRightEdge = RightEdge;
		NewTopEdge = TopEdge;
		NewBottomEdge = BottomEdge;

pnabs:
		NegativeGadget.NextGadget = &LRTBGadget[0];
		sprintf(Buffer[0], "%1.10g", NewLeftEdge);
		sprintf(Buffer[1], "%1.10g", NewRightEdge);
		sprintf(Buffer[2], "%1.10g", NewTopEdge);
		sprintf(Buffer[3], "%1.10g", NewBottomEdge);
		sprintf(RatioText.IText+7, "%1.4f     ", Ratio(NewLeftEdge,
				NewRightEdge, NewTopEdge, NewBottomEdge, MainWindow));

		do {
			window = MyRequest(&AbsRequest, MainWindow);

			while ( (ID = WaitMyRequest(window) ) > NEGGADGETID) {
				sscanf(Buffer[0], "%lf", &NewLeftEdge);
				sscanf(Buffer[1], "%lf", &NewRightEdge);
				sscanf(Buffer[2], "%lf", &NewTopEdge);
				sscanf(Buffer[3], "%lf", &NewBottomEdge);
				sprintf(RatioText.IText+7, "%1.4f     ",
					Ratio(NewLeftEdge, NewRightEdge, NewTopEdge,
					NewBottomEdge, MainWindow));

				PrintIText(AbsRequest.ReqLayer->rp, &RatioText, 0L, 0L);
			}

			EndMyRequest(&AbsRequest, window, MainWindow);

		} while (sscanf(Buffer[0], "%lf", &NewLeftEdge)+
				 sscanf(Buffer[1], "%lf", &NewRightEdge)+
				 sscanf(Buffer[2], "%lf", &NewTopEdge)+
				 sscanf(Buffer[3], "%lf", &NewBottomEdge) < 4);

		if (ID != POSGADGETID) return;

		LeftEdge = NewLeftEdge;
		RightEdge = NewRightEdge;
		TopEdge = NewTopEdge;
		BottomEdge = NewBottomEdge;

		StopFraming();
		StopDrawing();
		
		break;
	}
	DrawPicture((bool)FALSE);	/* Don't fill in */
}

float Ratio(l, r, t, b, window)
double l, r, t, b;
struct Window *window;
{
	float PixelRatio;
	float ReImRatio;

	if (t == b) t = b+1;	/* You never know... */

	PixelRatio = (float) window->GZZWidth / window->GZZHeight;

	if (window->WScreen->ViewPort.Modes & HIRES) PixelRatio /= 2;
	if (window->WScreen->ViewPort.Modes & LACE) PixelRatio *= 2;
	ReImRatio = (r - l)/(t - b);

	return ReImRatio / PixelRatio;
}

void Parameters()
{
	struct Window *window;
	int ID;
	int NewMaxDepth, NewRangeWidth;

	static struct IntuiText ParamText = {
		MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, 10, 10, NULL,
		(UBYTE *) "Select these parameters", NULL	};
	static struct IntuiText ParmText[] = {
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, -120, 0, NULL,
			(UBYTE *) "Max depth", NULL	},
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, -120, 0, NULL,
			(UBYTE *) "Range width", NULL	},
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, -120, 0, NULL,
			(UBYTE *) "", NULL	},
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE, -120, 0, NULL,
			(UBYTE *) "", NULL	}
	};
	static struct StringInfo Parminfo[] = {
		{	&Buffer[0][0], &Buffer[4][0], 0, 20, 0	},
		{	&Buffer[1][0], &Buffer[4][0], 0, 20, 0	},
		{	&Buffer[2][0], &Buffer[4][0], 0, 20, 0	},
		{	&Buffer[3][0], &Buffer[4][0], 0, 20, 0	}
	};
	static struct Gadget ParmGadget[] = {
		{	&ParmGadget[1], 130, 30, 96, 10,			/* next, LTWH */
			GADGHCOMP,									/* Flags */
			RELVERIFY | LONGINT,						/* Activation */
			STRGADGET | REQGADGET,						/* GadgetType */
			(APTR) NULL, NULL,							/* rendering */
			&ParmText[0], 0, (APTR) &Parminfo[0],		/* "MaxDepth" */
			0, NULL	},
		{	&ParmGadget[2], 130, 45, 96, 10,			/* next, LTWH */
			GADGHCOMP,
			RELVERIFY | LONGINT,
			STRGADGET | REQGADGET,
			(APTR) NULL, NULL,
			&ParmText[1], 0, (APTR) &Parminfo[1],		/* "RangeWidth" */
			0, NULL	},
		{	&ParmGadget[3], 130, 60, 96, 10,			/* next, LTWH */
			GADGHCOMP,
			RELVERIFY,
			STRGADGET | REQGADGET,
			(APTR) NULL, NULL,
			&ParmText[2], 0, (APTR) &Parminfo[2],		/* */
			0, NULL	},
		{	NULL, 130, 75, 96, 10,						/* next, LTWH */
			GADGHCOMP,
			RELVERIFY,
			STRGADGET | REQGADGET,
			(APTR) NULL, NULL,
			&ParmText[3], 0, (APTR) &Parminfo[3],		/* */
			0, NULL	}
	};
	static struct Requester ParmRequest = {
		NULL, 25, 40, 260, 130, 0,0, &PositiveGadget, NULL,
		&ParamText, 0, 1	};

	/* Stuff for the PARAMETERS requester */
	NegativeGadget.NextGadget = &ParmGadget[0];
	Parminfo[0].LongInt = MaxDepth;
	Parminfo[1].LongInt = RangeWidth;
	do {
		sprintf(Buffer[0], "%ld", Parminfo[0].LongInt);
		sprintf(Buffer[1], "%ld", Parminfo[1].LongInt);
		Buffer[2][0] = Buffer[3][0] = '\0';

		window = MyRequest(&ParmRequest, MainWindow);
		ID = WaitMyRequest(window);
		EndMyRequest(&ParmRequest, window, MainWindow);
		if (ID == NEGGADGETID) return;

		NewMaxDepth = Parminfo[0].LongInt;
		NewRangeWidth = Parminfo[1].LongInt;
	} while ( NewMaxDepth < 0 || NewMaxDepth > MAXDEPTH ||
			  NewRangeWidth < 0 || NewRangeWidth > MAXDEPTH );

	if (NewRangeWidth != RangeWidth)	InitPenTable();
	MaxDepth = NewMaxDepth;
	RangeWidth = NewRangeWidth;
}
