#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
 * 
 * Main Program, including some general routines
 */

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

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct LayersBase *LayersBase;

struct TextAttr Topaz80 = {
	(STRPTR) "topaz.font", TOPAZ_EIGHTY, FS_NORMAL, FPF_ROMFONT	};

struct TextAttr Topaz60 = {
	(STRPTR) "topaz.font", TOPAZ_SIXTY, FS_NORMAL, FPF_ROMFONT	};

struct NewScreen MandelNScreen =
{
	0, 0, 320, 256,         /* LeftEdge, TopEdge, Width, Height */
	4,                      /* Depth */
	2, 1,                   /* DetailPen, BlockPen */
	NULL,                   /* viewmode LORES */
	CUSTOMSCREEN,           /* type */
	&Topaz80,               /* Font (default) */
	(UBYTE *)"Mandelbrot Construction Set V1.0" /* DefaultTitle for menubar */
};

struct NewWindow MainNWindow =
{
	0, 0, 0, 0,            /* LeftEdge, TopEdge, Width, Height */
	2, 1,                  /* DetailPen, BlockPen */
	CLOSEWINDOW | MENUPICK | MOUSEBUTTONS | SIZEVERIFY |
		MENUVERIFY,
	WINDOWCLOSE | ACTIVATE | WINDOWSIZING | WINDOWDRAG |
	     WINDOWDEPTH | NOCAREREFRESH | SMART_REFRESH | GIMMEZEROZERO,
	NULL,					/* FirstGadget */
	NULL,                   /* default CheckMark */
	(UBYTE *) "Mandelbrot Construction Window",     /* Title */
	NULL,                   /* Screen */
	NULL,                   /* BitMap */
	60, 25,                 /* MinWidth, MinHeight */
	-1, -1,                 /* MaxWidth, MaxHeight */
	CUSTOMSCREEN            /* Screen type */
};

struct BorderInfo borderinfo;

struct IntuiText PositiveText =
	{	AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
		AUTOLEFTEDGE, AUTOTOPEDGE, NULL, (UBYTE *) "Continue", NULL	},
NegativeText =
	{	AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
		AUTOLEFTEDGE, AUTOTOPEDGE, &Topaz60, (UBYTE *) "Cancel", NULL	};

STATIC SHORT XY1[] = { 0,0, 0,13, 78,13, 78,0, 0,0 };
STATIC SHORT XY2[] = { 0,0, 0,17, 82,17, 82,0, 0,0 };
STATIC struct Border border[] = {
	{	 0,  0, 2,0, JAM1, 5, XY1, &border[1] },
	{	-2, -2, 3,0, JAM1, 5, XY2, NULL }
};
struct Gadget NegativeGadget = {
	NULL, -100, -20, 79, 14,	/* next, LTWH */
	GADGHCOMP | GRELBOTTOM | GRELRIGHT,
	RELVERIFY | ENDGADGET,
	BOOLGADGET | REQGADGET,
	(APTR) &border[0], NULL,
	&NegativeText, 0, NULL,
	NEGGADGETID, NULL	};
struct Gadget PositiveGadget = {
	&NegativeGadget, 20, -20, 79, 14,	/* next, LTWH */
	GADGHCOMP | GRELBOTTOM,
	RELVERIFY | ENDGADGET,
	BOOLGADGET | REQGADGET,
	(APTR) &border[0], NULL,
	&PositiveText, 0, NULL,
	POSGADGETID, NULL	};

USHORT ColorMap[MAXDEPTH];
bool ColorMapValid = FALSE;
UBYTE PenTable[MAXDEPTH];
unsigned PenTableMode = MODULO;
short RangeWidth = 2;

struct Screen *MandelScreen = NULL;  /* Pointer for OpenScreen return value */
struct Window *MainWindow = NULL;    /* Idem for OpenWindow */


void (*WritePixelDepth)() = ZQuadMinC;

double	LeftEdge = -0.800,    /* Left edge of the picture */
	RightEdge = 2.100,
	TopEdge = 1.200,
	BottomEdge = -1.200;

double	CXStep,               /* Stepsize through the complex plane */
	CYStep;
int	PixelStep=1,              /* Stepsize on the screen*/
	MaxDepth=100;             /* Maximum iteration count */
	NumColors;				  /* Number of colors on the screen */

unsigned short FrameX1, FrameX2,
	FrameY1, FrameY2;

short MouseStatus = NOTFRAMING;/* Where we are in the process of */
							  /* selecting a frame */

bool finished = FALSE;		  /* TRUE to stop the program */
bool Saved = TRUE;			  /* Indicates the picture has been SAVEd */
bool NameValid = FALSE;		  /* Indicates the file name is valid */
bool StillDrawing = FALSE;	  /* Are we still drawing ? */

/* Manx startup stubs, to save some memory. Not useful with Lattice! */

#ifdef MANX_C
_cli_parse() {}
_wb_parse() {}
#endif

/* M A I N   E N T R Y   P O I N T */

main()
{
	register ULONG Class;					/* IntuiMessage class */
	register USHORT Code;					/*  and Code field */
	struct IntuiMessage *message;			/* Expected message pointer */

/* Open each of the libraries and check for a NULL return, which
 * indicates unavailability.
 */

	IntuitionBase = (struct IntuitionBase *)
		OpenLibrary ("intuition.library", LIBRARY_VERSION);
	if (IntuitionBase == NULL) MyExit ("Can't open Intuition V1.2 or newer!");

	GfxBase = (struct GfxBase *)
		OpenLibrary("graphics.library", LIBRARY_VERSION);
	if (GfxBase == NULL) MyExit ("Can't open Gfx V1.2 or newer!");

	LayersBase = (struct LayersBase *)
		OpenLibrary("layers.library", LIBRARY_VERSION);
	if (LayersBase == NULL) MyExit ("Can't open Layers V1.2 or newer!");

	if (InitDisplay((bool)FALSE)) MyExit ("Can't initialise the display properly!");

	CalcCSteps();

again:
	finished = FALSE;
	while (!finished) {
		/* Wait for message port to become not empty, and extract the msg */
		WaitPort(MainWindow->UserPort);
		while ( message = (struct IntuiMessage *)
			GetMsg(MainWindow->UserPort) ) {
gotmessage:
			Class  = message->Class;
			Code   = message->Code;

			if (Class & ~(MOUSEBUTTONS | INTUITICKS)) {
				ReplyMsg(message);		/* Pointer is not valid anymore */
			}

			switch (Class) {
			case MENUPICK:
				GotMenu(Code); break;
			case MOUSEBUTTONS:
			case INTUITICKS:
				CheckMouse(Class, Code, message->MouseX, message->MouseY,
					message->Seconds, message->Micros);
				ReplyMsg(message);
				break;
			case SIZEVERIFY:
				StopFraming(); 				/* Fall Through to MenuVerify */
			case MENUVERIFY:
				break;		/* Just make sure there is no half-drawn frame */
			case CLOSEWINDOW:
				finished = TRUE;
				break;
			/* default: Ignore strange messages */
			}  /* End Switch The Class Of The Message */
		}  /* End While There Is A Message */
	}  /* End While Not Finished */

	/* So we are finished. 
	 * And, by the way, are we SURE we are finished alltogether ??
	 */
	if (!Sure()) {
		backto again;
	}

	CleanupDisplay((bool)TRUE);
	MyExit(NULL);                       /* Indicate Good Exit */
}  /* End of main */


/* Initialize the screen and the windows and the menus */

bool InitDisplay(borderless)
bool borderless;
{
	USHORT ViewModes = MandelNScreen.ViewModes;
	struct Screen WBScreen;
	int i;
	StopFraming();

	Saved = TRUE;	/* We can easily reconstruct this `picture' */

	/* Get Left, Top, Width, Height */
	GetScreenData(&WBScreen, (long)sizeof(WBScreen),
		(long)WBENCHSCREEN, NULL);
	MandelNScreen.Width = WBScreen.Width;
	MandelNScreen.Height = WBScreen.Height;
	/* Maybe we have an interlaced WorkBench screen */
	if (WBScreen.ViewPort.Modes & LACE) MandelNScreen.Height >>= 1;
	/* And maybe, if we use MWB, it isn't hires... */
	if (!(WBScreen.ViewPort.Modes & HIRES)) MandelNScreen.Width <<= 1;

	if (ViewModes & LACE) MandelNScreen.Height <<= 1;

	if (ViewModes & HIRES) {
		MandelNScreen.Depth = 4;
		NumColors = 16;

		/* Avoid a bug in MrgCop()?? if you have a PAL machine with */
		/* a screen wider than 640 taller than 213 and with 4 bitplanes */

		if (MandelNScreen.Width > 640)
			MandelNScreen.Width = 640;
	} else {
		MandelNScreen.Depth = 5;
		NumColors = 32;
		MandelNScreen.Width >>= 1;
	}

	if ( !MandelScreen && !(MandelScreen = OpenScreen(&MandelNScreen)) ) {
		skipto abort;
	}

	if (ColorMapValid)
		LoadRGB4(&MandelScreen->ViewPort, ColorMap, (long) NumColors);

	MainNWindow.Screen = MandelScreen;

	if (MainNWindow.Height < MainNWindow.MinHeight) {
		/* First time we open the window */
		MainNWindow.LeftEdge = 0;
		MainNWindow.TopEdge  = MandelScreen->BarHeight;
		MainNWindow.Width    = MandelScreen->Width;
		MainNWindow.Height   = MandelScreen->Height - MainNWindow.TopEdge;
	} else {
		if (ViewModes & HIRES) {
			MainNWindow.LeftEdge <<= 1;
			MainNWindow.Width <<= 1;
		}
		if (ViewModes & LACE) {
			MainNWindow.TopEdge <<= 1;
			MainNWindow.Height <<= 1;
		}
	}

	/* Check for windows that can't be opened */

	if (MainNWindow.LeftEdge + MainNWindow.Width > MandelScreen->Width)
		MainNWindow.Width = MandelScreen->Width - MainNWindow.LeftEdge;

	if (MainNWindow.TopEdge + MainNWindow.Height > MandelScreen->Height)
		MainNWindow.Height = MandelScreen->Height - MainNWindow.TopEdge;

	if ( !MainWindow && !(MainWindow = OpenWindow(&MainNWindow)) ) {
		skipto abort;
	}

	SetMenuStrip(MainWindow, MandelMenu);
	if (borderless) {
		DoBorderless(MainWindow, &borderinfo);
	}

	InitPenTable();

	return FALSE;

abort:
	CleanupDisplay((bool) TRUE);
	return TRUE;

}

bool CleanupDisplay(everything)
bool everything;
{
	bool wasborderless;
	int i;

	StopDrawing();
	StopFraming();

	wasborderless = (MainWindow != NULL) &&
		((MainWindow->Flags & BORDERLESS) != 0);

	if (MainWindow) {
		/* Save window appearance for later, in low-res non-lace pixels */
		MainNWindow.LeftEdge = MainWindow->LeftEdge;
		MainNWindow.TopEdge  = MainWindow->TopEdge;
		MainNWindow.Width    = MainWindow->Width;
		MainNWindow.Height   = MainWindow->Height;

		ClearMenuStrip(MainWindow);		/* Remove menu strip */
		CloseWindow(MainWindow);		/* Finally close it */
		MainWindow = NULL;
	}

	/* Save the colors we may have established with great care */

	if (MandelScreen) {
		for (i=0; i < NumColors; i++) {
			ColorMap[i] = GetRGB4(MandelScreen->ViewPort.ColorMap, i);
		}
		ColorMapValid = TRUE;

		if (MandelScreen->ViewPort.Modes & HIRES) {
			MainNWindow.LeftEdge >>= 1;
			MainNWindow.Width >>= 1;
		}
		if (MandelScreen->ViewPort.Modes & LACE) {
			MainNWindow.TopEdge >>= 1;
			MainNWindow.Height >>= 1;
		}

		/* Close the screen only if `everything' must be cleaned up */

		if (everything) {
			CloseScreen(MandelScreen);
			MandelScreen = NULL;
		}
	}

	return wasborderless;
}

bool DoBorderless(window, borderinfo)
struct Window *window;
struct BorderInfo *borderinfo;
{
	/* Make window borderless */

/* register struct Layer_Info *LayerInfo = &window->WScreen->LayerInfo; */
#define LayerInfo	NULL			/* Since V1.2 */
	register struct Layer *Layer = window->RPort->Layer;
	register long movex;
	register long movey;
	register long sizex;
	register long sizey;
	bool Success = FALSE;

	if (window->Flags & BORDERLESS)	return TRUE;

	LockLayer(LayerInfo, Layer);

	window->Flags |= BORDERLESS;

	movex = -window->BorderLeft;
	movey = -window->BorderTop;
	sizex = window->BorderRight - movex;
	sizey = window->BorderBottom - movey;

	window->BorderLeft =
	window->BorderTop =
	window->BorderRight =
	window->BorderBottom = 0;

	window->GZZWidth += sizex;
	window->GZZHeight += sizey;

	if ( MoveLayer(LayerInfo, Layer, movex, movey) ) {
		Success = SizeLayer(LayerInfo, Layer, sizex, sizey);
		if (!Success) {
		  	sizex = sizey = 0;
		}
	} else {
		movex = movey = sizex = sizey = 0;
	}
	UnlockLayer(Layer);

	borderinfo->MoveX = movex;
	borderinfo->MoveY = movey;
	borderinfo->SizeX = sizex;
	borderinfo->SizeY = sizey;

	/* Done making borderless */

	if (Success) {
		return Success;
	} else {	/* Try to clean up the mess */
		UndoBorderless(window, borderinfo);
		return FALSE;
	}
#undef LayerInfo
}

void UndoBorderless(window, borderinfo)
struct Window *window;
struct BorderInfo *borderinfo;
{
	/* ``Another fine mess you got me into!'' (Oliver Hardy) */

/*	register struct Layer_Info *LayerInfo = &window->WScreen->LayerInfo; */
#define LayerInfo	NULL			/* Since V1.2 */
	register struct Layer *Layer = window->RPort->Layer;
	register long movex = borderinfo->MoveX;
	register long movey = borderinfo->MoveY;
	register long sizex = borderinfo->SizeX;
	register long sizey = borderinfo->SizeY;
	bool Success = FALSE;

	if (!(window->Flags & BORDERLESS))	return;

	LockLayer(LayerInfo, Layer);
	SizeLayer(LayerInfo, Layer, -sizex, -sizey);
	MoveLayer(LayerInfo, Layer, -movex, -movey);

	window->GZZWidth -= sizex;
	window->GZZHeight -= sizey;

	window->BorderLeft = -movex;
	window->BorderTop = -movey;
	window->BorderRight = sizex + movex;
	window->BorderBottom = sizey + movey;

	window->Flags &= ~BORDERLESS;

	RefreshWindowFrame(window);

	UnlockLayer(Layer);

	/* Done Undoing Borderless */
#undef LayerInfo
}

void MyExit(status)
char *status;
{
#ifndef DEBUG2
	static char message[] = "\
\0\144\25Mandelbrot Construction Set -- By KosmoSoft Productions\0a\
\0\144\41                                                                 \0";
/*      3^5^  10^    ^  20^    ^  30^    ^  40^    ^  50^  55^58^     */
	register char *c=message+65;

	if (status) {
		/* Center the message. Notice `60' is even. */
		/* We assume WORD alignment for the string. */

		*((WORD *) (message+60)) = 320 - (strlen(status) << 2);
		while ((*(c++) = *(status++)) && c < message + 127);
		*c = 0;

		/* Let's be paranoid for a change... */
		if (!IntuitionBase) IntuitionBase = (struct IntuitionBase *)
			OpenLibrary ("intuition.library", 0L);
		if (IntuitionBase)
			DisplayAlert(RECOVERY_ALERT, message, 50L);
		else {	/* AT_Recovery | AG_OpenLib | AO_Intuition */
			CPTR AlertParameter = (CPTR) FindTask(NULL);
			Alert(0x00038004L, &AlertParameter);
		}

		status=1;
	}
#else
	if (status) {
		fprintf(stderr, "Mandelbrot Construction Set Error:\n    %s\n",
			status);
		status = 1;
	}
#endif
	CleanupDisplay((bool) TRUE);
	if (IntuitionBase) CloseLibrary(IntuitionBase);
	if (LayersBase) CloseLibrary(LayersBase);
	if (GfxBase) CloseLibrary(GfxBase);
	exit ((int) (status != NULL));
}

bool Sure()
{
	bool Result;
	ULONG OldIDCMP = MainWindow->IDCMPFlags;

	static struct IntuiText Body[] =
	{
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
			25, 10, NULL, (UBYTE *) "You are going to", &Body[1]	},
		{	MYFRONTPEN+1, AUTOBACKPEN, AUTODRAWMODE,
			57, 25, &Topaz60, (UBYTE *) "destroy", &Body[2]	},
		{	MYFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
			33, 40, NULL, (UBYTE *) "your picture !", NULL	}
	};

	if (Saved) return TRUE;

	ModifyIDCMP(MainWindow, OldIDCMP &~ (MENUVERIFY | SIZEVERIFY | REQVERIFY));
	Result = AutoRequest(MainWindow, &Body[0], &PositiveText, &NegativeText,
		NULL, NULL, 200L, 90L);
	ModifyIDCMP(MainWindow, OldIDCMP);

	return Result;
}

/***********************************************************************
 * Render a requester in a window. If it won't fit, open a new window.
 * This new window will share the IDCMP port with the original
 * window. This is to save memory, signal bits and VERIFY deadlocks.
 * Returns the window in which the requester actually appears.
 */

struct Window *MyRequest(request, window)
struct Requester *request;
struct Window *window;
{
	static struct NewWindow newwindow = {
		0, 0, 0, 0, 2, 1,
		NULL,			/* IDCMP flags -- port shared with main window */
		WINDOWDEPTH | WINDOWDRAG | ACTIVATE | SIMPLE_REFRESH | NOCAREREFRESH,
		NULL, NULL, NULL, NULL,
		NULL, 0, 0, 0, 0, NULL	};
	int width, height, left, top;
	int borderleft, borderright, bordertop, borderbottom;
	struct Window *oldwindow = window;

	borderleft   = window->WScreen->WBorLeft;
	borderright  = window->WScreen->WBorRight;
	bordertop    = window->WScreen->BarHeight + 1;
	borderbottom = window->WScreen->WBorBottom;

	/* Center the requester in the given window.
	 * If impossible, open a new window to the place the requester in.
	 */

	 width = window->Width - borderleft - borderright;
	 height = window->Height - bordertop - borderbottom;
	 left = ((width - request->Width) >> 1) + borderleft;
	 top = ((height - request->Height) >> 1) + bordertop;

	 if (width < request->Width || height < request-> Height) {
	 	/* Window too small. Open a new one */
		newwindow.Width = request->Width + 2 * borderleft;
		newwindow.Height = request->Height + bordertop + borderbottom;
		newwindow.LeftEdge = newwindow.TopEdge = 0;
		newwindow.Title = window->Title;
		newwindow.Screen = window->WScreen;
		newwindow.Type = window->WScreen->Flags & SCREENTYPE;

		if (window = OpenWindow(&newwindow)) {
			window->UserPort = oldwindow->UserPort;
			/* Upen up the other port */
			ModifyIDCMP(window, GADGETUP);
		}

		request->LeftEdge = borderleft;
		request->TopEdge = bordertop;
	} else {	/* The requester fits. Center it! */
		request->LeftEdge = left;
		request->TopEdge = top;
	}

	if (window && Request(request, window)) return window;

	if (window) CloseWindowSafely(window);
	return NULL;
}

void EndMyRequest(request, window, original)
struct Requester *request;
struct Window *window, *original;
{
	EndRequest(request, window);
	if (window != original) CloseWindowSafely(window);
}

/*************************************************************************
 * Wait on a request posted by MyRequest.
 * Returns when a gadget with GadgetID >= POSGADGETID is released,
 * so Gadgets with an ID < POSGADGETID will be ignored.
 * Any messages other than GADGETUP will be ignored.
 */

int WaitMyRequest(window)
struct Window *window;
{
	int ID = 0;
	struct IntuiMessage *message;
	struct Gadget *Gadget;
	ULONG Class;
	ULONG OldIDCMP = window->IDCMPFlags;

	if (!window)
		return NEGGADGETID;

	ModifyIDCMP(window, GADGETUP);
	while (ID < POSGADGETID) {
		WaitPort(window->UserPort);
		while (message = (struct IntuiMessage *) GetMsg(window->UserPort) ) {
			Class = message->Class;
			Gadget = (struct Gadget *)message->IAddress;
			ReplyMsg(message);
			if (Class != GADGETUP) continue;
			ID = Gadget->GadgetID;
			if (ID >= POSGADGETID) break; /* Also gets out of outer loop */
		}
	}
	ModifyIDCMP(window, OldIDCMP);
	return ID;
}

void RectDraw(rp, x1, y1, x2, y2)
struct RastPort *rp;
SHORT x1, y1, x2,y2;
{
	Move(rp, (long) x1, (long) y1);
	Draw(rp, (long) x2, (long) y1);
	Draw(rp, (long) x2, (long) y2);
	Draw(rp, (long) x1, (long) y2);
	if (y2 > y1) y1++; else y1--;	/* Don't XOR the first pixel twice */
	Draw(rp, (long) x1, (long) y1);
}

void CrossDraw(rp, x, y, left, right, top, bottom)
struct RastPort *rp;
SHORT x, y, top, bottom, left, right;
{
	Move(rp, (long) left, (long) y);
	Draw(rp, (long) right, (long) y);
	Move(rp, (long) x, (long) top);
	Draw(rp, (long) x, (long) bottom);
}

void DisableSystemGadgets(gadget)
struct Gadget *gadget;
{
	while (gadget) {
		if (gadget->GadgetType & SYSGADGET) {
			/* Ghost everything except the Title/Dragbar */
			if ((gadget->GadgetType & 0x00F0) != WDRAGGING)
				OffGadget(gadget, MainWindow, NULL);
			gadget->Flags |= GADGDISABLED;
		}
		gadget = gadget->NextGadget;
	}
}

void EnableSystemGadgets(gadget)
struct Gadget *gadget;
{
	USHORT Flags = 0;

	while (gadget) {
		if (gadget->GadgetType & SYSGADGET) {
			Flags |= gadget->Flags;
			gadget->Flags &= ~GADGDISABLED;
		}
		gadget = gadget->NextGadget;
	}
	/* Unghost everthing if necessary */
	if (Flags & GADGDISABLED)	RefreshWindowFrame(MainWindow);
}

void StopFraming()
{
	if (MainWindow) {
		EnableSystemGadgets(MainWindow->FirstGadget);
		ModifyIDCMP(MainWindow, MainWindow->IDCMPFlags & ~INTUITICKS);
	}
	MouseStatus = NOTFRAMING;
}

void CheckMouse(Class, Code, MouseX, MouseY, Secs, Micros)
ULONG Class;
USHORT Code;
SHORT MouseX, MouseY;
ULONG Secs, Micros;
{
	register SHORT top = MainWindow->BorderTop,
		bottom = MainWindow->Height - MainWindow->BorderBottom - 1,
		left = MainWindow->BorderLeft,
		right = MainWindow->Width - MainWindow->BorderRight - 1;
	static ULONG OldSecs, OldMicros;
	static SHORT MidX, MidY;

	if (StillDrawing || MouseStatus != FLASHING &&
		(MouseX < left || MouseX > right || MouseY < top || MouseY > bottom))
		return;

	MouseX -= left;
	MouseY -= top;

	if (Class == MOUSEBUTTONS) {
		if (Code == SELECTDOWN) {
			/* We selected a point */
			switch (MouseStatus) {
			case NOTFRAMING:
			case FLASHING:
				MouseStatus = NOPOINT;
				DisableSystemGadgets(MainWindow->FirstGadget);
				ModifyIDCMP(MainWindow, MainWindow->IDCMPFlags | INTUITICKS);
				/* Now we can select our first corner */
				break;
			case NOPOINT:
				FrameX1 = FrameX2 = MouseX;
				FrameY1 = FrameY2 = MouseY;
				MouseStatus = POINT1;
				OldMicros = Micros;
				OldSecs = Secs;
				/* We have the first point. Now go for the second */
				break;
			case POINT1:
				if (DoubleClick(OldSecs, OldMicros, Secs, Micros)) {
					/* Did we double-click? Then we have selected a center */
					MouseStatus = CENTERFRAMING;
					MidX = FrameX1;
					MidY = FrameY1;
					break;
				}
				FrameX2 = MouseX;
				FrameY2 = MouseY;
				/* Fall through to CENTERFRAMING */
			case CENTERFRAMING:
				if (FrameX1 == FrameX2 || FrameY1 == FrameY2) break;
				EnableSystemGadgets(MainWindow->FirstGadget);
				MouseStatus = FLASHING;
				/* Point 1 should be upper left */
				if (FrameX2 < FrameX1) {
					/* I DO know I am reusing a variable here. Sorry! */
					left=FrameX2; FrameX2=FrameX1; FrameX1=left;
				}
				if (FrameY2 < FrameY1) {
					left=FrameY2; FrameY2=FrameY1; FrameY1=left;
				}
				break;
			}	/* End switch MouseStatus */
		}	/* End if Code == SELECTDOWN */
		return;
	}	/* End if Class == MOUSEBUTTONS */

	/* We are moving the mouse. Show something! */

	SetDrMd(MainWindow->RPort, (ULONG) COMPLEMENT);

	switch (MouseStatus) {
	/* case NOTFRAMING: */
	/*	return; */
	case NOPOINT:
		FrameX1 = FrameX2 = MouseX;
		FrameY1 = FrameY2 = MouseY;
		/* WaitBOVP(&MandelScreen->ViewPort); */
		WaitTOF();
		CrossDraw(MainWindow->RPort, FrameX1, FrameY1,
					0, MainWindow->GZZWidth-1, 0, MainWindow->GZZHeight-1);
		/* WaitBOVP(&MandelScreen->ViewPort);	*/
		WaitTOF();
		CrossDraw(MainWindow->RPort, FrameX1, FrameY1,
					0, MainWindow->GZZWidth-1, 0, MainWindow->GZZHeight-1);
		break;
	case CENTERFRAMING:
		FrameX1 = MouseX;
		FrameY1 = MouseY;
		FrameX2 = 2*MidX - FrameX1;
		FrameY2 = 2*MidY - FrameY1;
		skipto flashing;
	case POINT1:
		FrameX2 = MouseX;
		FrameY2 = MouseY;
		/* Deliberate Fall-Through to FLASHING */
	case FLASHING:
flashing:
		/* WaitBOVP(&MandelScreen->ViewPort);	*/
		WaitTOF();
		RectDraw(MainWindow->RPort, FrameX1, FrameY1, FrameX2, FrameY2);
		/* WaitBOVP(&MandelScreen->ViewPort);	*/
		WaitTOF();
		RectDraw(MainWindow->RPort, FrameX1, FrameY1, FrameX2, FrameY2);
	}
}

void InitPenTable()
{
	register int i;

	switch (PenTableMode) {
	case MODULO:
		PenTable[0] = 0;
		for (i=1; i<MAXDEPTH; i++) PenTable[i] = 1 + i % (NumColors - 1);
		break;
	case RANGES:
		PenTable[0] = 0;
		for (i=1; i<MAXDEPTH; i++)
			PenTable[i] = 1 + (i/RangeWidth) % (NumColors - 1);
	case SELECT:
		/* Don't change the table if it is user-defined */
		break;
	}
}

void SelectMenu(MenuNum, CheckIt)
LONG MenuNum;
bool CheckIt;
{
	struct MenuItem *Item = ItemAddress(MandelMenu, MenuNum);

	ClearMenuStrip(MainWindow);

	if (CheckIt)
		Item->Flags |= CHECKED;
	else
		Item->Flags &= ~CHECKED;

	SetMenuStrip(MainWindow, MandelMenu);
}

void MakeMAND(mand)
struct Mand *mand;
{
	int i;

	mand->MandID = MAND;
	mand->Size = sizeof(struct Mand);
	mand->MaxDepth = MaxDepth;
	mand->RangeWidth = RangeWidth;

	mand->RainDist = RainbowDistance;
	mand->RainRMax = RainbowRMax;
	mand->RainGMax = RainbowGMax;
	mand->RainBMax = RainbowBMax;

	for (i=0; i < sizeof(mand->Coords); i++)
		mand->Coords[i] = '\0';
	sprintf(&mand->Coords[0], "%1.10g %1.10g %1.10g %1.10g",
		LeftEdge, RightEdge, TopEdge, BottomEdge);
}

bool InterpretMAND(mand)
struct Mand *mand;
{
	double NewLeftEdge, NewRightEdge, NewTopEdge, NewBottomEdge;

	/* Perform some checks on correctness of the chunk */
	if (mand->MandID != MAND || mand->Size > sizeof(struct Mand) ||
		mand->MaxDepth > MAXDEPTH || mand->RangeWidth > MAXDEPTH)
		return FALSE;

	if (sscanf(&mand->Coords[0], "%lf %lf %lf %lf",
		&NewLeftEdge, &NewRightEdge, &NewTopEdge, &NewBottomEdge) < 4)
		return FALSE;

	MaxDepth    = mand->MaxDepth;
	RangeWidth  = mand->RangeWidth;

	LeftEdge    = NewLeftEdge;
	RightEdge   = NewRightEdge;
	TopEdge     = NewTopEdge;
	BottomEdge  = NewBottomEdge;
	CalcCSteps();

	RainbowDistance = mand->RainDist;
	RainbowRMax = mand->RainRMax;
	RainbowGMax = mand->RainGMax;
	RainbowBMax = mand->RainBMax;

	return TRUE;
}

void CalcCSteps()
{
	CXStep = (double) (RightEdge - LeftEdge) / ( MainWindow->GZZWidth - 1);
	CYStep = (double) (TopEdge - BottomEdge) / ( MainWindow->GZZHeight - 1);
}
