/*
 * This source file is Copyright 1995 by Evan Scott.
 * All rights reserved.
 * Permission is granted to distribute this file provided no
 * fees beyond distribution costs are levied.
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/alerts.h>

#include <intuition/intuition.h>
#include <intuition/sghooks.h>

#include <dos/dos.h>
#include <dos/dosextens.h>

#include <libraries/commodities.h>
#include <libraries/gadtools.h>

#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/commodities.h>
#include <proto/gadtools.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "evtypes.h"
#include "verify.h"
#include "ftp.h"
#include "site.h"

#include "globals.h"
#include "strings.h"

#include "request.h"

#define V_IntuiText 18798
#define V_Gadget 18273

#define V_rastport 1001
#define V_bitmap 1008

struct RastPort *make_rastport(b32 width, b32 height, b32 depth, struct GfxBase *GfxBase)
{
	struct RastPort *rp;
	struct BitMap *bm;
	int i;
	b8 *z;
	
	rp = (struct RastPort *)allocate_flags(sizeof(*rp), MEMF_PUBLIC | MEMF_CLEAR, V_rastport);
	if (!rp) return 0;
	
	bm = (struct BitMap *)allocate_flags(sizeof(*bm), MEMF_PUBLIC | MEMF_CLEAR, V_bitmap);
	if (bm) {		
		InitBitMap(bm, depth, width, height);
		
		bm->Planes[0] = AllocRaster(width, height * depth);
		if (bm->Planes[0]) {
			z = (b8 *)bm->Planes[0];
			for (i = 1; i < depth; i++) {
				z += bm->BytesPerRow * bm->Rows;
				bm->Planes[i] = (void *)z;
			}
			InitRastPort(rp);
			
			rp->BitMap = bm;
			rp->Mask = 0xff;
			
			return rp;
		}
		
		deallocate(bm, V_bitmap);
	}
	
	deallocate(rp, V_rastport);
	
	return 0;
}

void free_rastport(struct RastPort *rp, struct GfxBase *GfxBase)
{
	FreeRaster(rp->BitMap->Planes[0], rp->BitMap->BytesPerRow * 8, rp->BitMap->Rows * rp->BitMap->Depth);

	deallocate(rp->BitMap, V_bitmap);
	deallocate(rp, V_rastport);
}

struct IntuiText nulltext = {
	0, 0,
	JAM1,
	0, 0,
	(void *)0,
	"",
	(void *)0
};

struct gim *make_gim(b8 *name, b32 textpen, b32 lightpen, b32 darkpen, struct Screen *s, 
			struct IntuitionBase *IntuitionBase, struct GfxBase *GfxBase)
{
	struct IntuiText txt;
	int width, height, owidth, oheight;
	struct gim *gim;
	sb32 x, y, Rx, Ry, Rx2, Ry2, R2, new_delta, old_delta;
	
	txt.FrontPen = textpen;
	txt.BackPen = 0;
	txt.DrawMode = JAM2;
	txt.LeftEdge = 0;
	txt.TopEdge = 0;
	txt.ITextFont = s->Font;
	txt.IText = name;
	txt.NextText = nil;
	
	if (s->Font) height = s->Font->ta_YSize;
	else height = 8;
	
	width = IntuiTextLength(&txt);
	
	owidth = width + height;
	oheight = 3 * height / 2;
	
	gim = (struct gim *)allocate(sizeof(*gim), V_gim);
	if (!gim) return nil;
	
	ensure(gim, V_gim);
	
	gim->rp1 = make_rastport(owidth, oheight, 3, GfxBase);
	if (!gim->rp1) {
		deallocate(gim, V_gim);
		return nil;
	}
	
	gim->rp2 = make_rastport(owidth, oheight, 3, GfxBase);
	if (!gim->rp2) {
		free_rastport(gim->rp1, GfxBase);
		deallocate(gim, V_gim);
		return nil;
	}
	
	gim->im1.LeftEdge = 0;
	gim->im1.TopEdge = 0;
	gim->im1.Width = owidth;
	gim->im1.Height = oheight;
	gim->im1.Depth = 3;
	gim->im1.ImageData = (void *)gim->rp1->BitMap->Planes[0];
	
	gim->im1.PlanePick = 7;
	gim->im1.PlaneOnOff = 0;
	gim->im1.NextImage = nil;
	
	gim->im2 = gim->im1;
	gim->im2.ImageData = (void *)gim->rp2->BitMap->Planes[0];
	
	SetRast(gim->rp1, 0);
	SetRast(gim->rp2, 0);
	
	PrintIText(gim->rp1, &txt, height / 2, height / 4);
	PrintIText(gim->rp2, &txt, height / 2 + 1, height / 4 + 1);
	
	Rx = Ry = 2 * height / 3;
	
	Rx2 = Rx * Rx;
	Ry2 = Ry * Ry;
	
	R2 = Rx2 * Ry2;
	
	for (x = 0; x < Rx; x++) {
		y = 0;
		new_delta = abs(R2 - Ry2*x*x - Rx2*y*y);
		do {
			old_delta = new_delta;
			y++;
			new_delta = abs(R2 - Ry2*x*x - Rx2*y*y);
		} while (old_delta > new_delta);
		
		SetAPen(gim->rp1, lightpen);
		
		WritePixel(gim->rp1, Rx - x, Ry - (y - 1));
		WritePixel(gim->rp1, owidth - Rx - 1 + x, Ry - (y - 1));
		
		SetAPen(gim->rp1, darkpen);
		
		WritePixel(gim->rp1, Rx - x, oheight - Ry - 1 + (y - 1));
		WritePixel(gim->rp1, owidth - Rx - 1 + x, oheight - Ry - 1 + (y - 1));
		
		SetAPen(gim->rp2, darkpen);
		
		WritePixel(gim->rp2, Rx - x, Ry - (y - 1));
		WritePixel(gim->rp2, owidth - Rx - 1 + x, Ry - (y - 1));
		
		SetAPen(gim->rp2, lightpen);
		
		WritePixel(gim->rp2, Rx - x, oheight - Ry - 1 + (y - 1));
		WritePixel(gim->rp2, owidth - Rx - 1 + x, oheight - Ry - 1 + (y - 1));
	}
	
	for (y = 0; y < Ry; y++) {
		x = 0;
		new_delta = abs(R2 - Ry2 * x * x - Rx2 * y * y);
		do {
			old_delta = new_delta;
			x++;
			new_delta = abs(R2 - Ry2 * x * x - Rx2 * y * y);
		} while (old_delta > new_delta);
		
		SetAPen(gim->rp1, lightpen);
		
		WritePixel(gim->rp1, Rx - (x - 1), Ry - y);
		WritePixel(gim->rp1, Rx - (x - 1), oheight - Ry - 1 + y);

		SetAPen(gim->rp1, darkpen);

		WritePixel(gim->rp1, owidth - Rx - 1 + (x - 1), Ry - y);
		WritePixel(gim->rp1, owidth - Rx - 1 + (x - 1), oheight - Ry - 1 + y);
		
		SetAPen(gim->rp2, darkpen);
		
		WritePixel(gim->rp2, Rx - (x - 1), Ry - y);
		WritePixel(gim->rp2, Rx - (x - 1), oheight - Ry - 1 + y);

		SetAPen(gim->rp2, lightpen);

		WritePixel(gim->rp2, owidth - Rx - 1 + (x - 1), Ry - y);
		WritePixel(gim->rp2, owidth - Rx - 1 + (x - 1), oheight - Ry - 1 + y);
	}
	
	SetAPen(gim->rp1, lightpen);
	
	Move(gim->rp1, Rx, 0);
	Draw(gim->rp1, owidth - Rx - 1, 0);
	
	Move(gim->rp1, 0, Ry);
	Draw(gim->rp1, 0, oheight - Ry - 1);
	
	SetAPen(gim->rp1, darkpen);
	
	Move(gim->rp1, Rx, oheight - 1);
	Draw(gim->rp1, owidth - Rx - 1, oheight - 1);
	
	Move(gim->rp1, owidth - 1, Ry);
	Draw(gim->rp1, owidth - 1, oheight - Ry - 1);

	SetAPen(gim->rp2, darkpen);
	
	Move(gim->rp2, Rx, 0);
	Draw(gim->rp2, owidth - Rx - 1, 0);
	
	Move(gim->rp2, 0, Ry);
	Draw(gim->rp2, 0, oheight - Ry - 1);
	
	SetAPen(gim->rp2, lightpen);
	
	Move(gim->rp2, Rx, oheight - 1);
	Draw(gim->rp2, owidth - Rx - 1, oheight - 1);
	
	Move(gim->rp2, owidth - 1, Ry);
	Draw(gim->rp2, owidth - 1, oheight - Ry - 1);
	
	return gim;
}

void free_gim(struct gim *gim, struct IntuitionBase *IntuitionBase, struct GfxBase *GfxBase)
{
	verify(gim, V_gim);

	free_rastport(gim->rp1, GfxBase);
	free_rastport(gim->rp2, GfxBase);
	deallocate(gim, V_gim);
	
	return;
}

struct Gadget *make_gadget(struct gim *gim)
{
	struct Gadget *g;
	
	if (!gim) return nil;

	verify(gim, V_gim);
	
	g = (struct Gadget *)allocate(sizeof(*g), V_Gadget);
	if (!g) return nil;
	
	g->NextGadget = nil;
	g->LeftEdge = 0;
	g->TopEdge = 0;
	g->Width = gim->im1.Width;
	g->Height = gim->im1.Height;
	
	g->Flags = GFLG_GADGHIMAGE | GFLG_GADGIMAGE;
	g->Activation = GACT_RELVERIFY;
	g->GadgetType = GTYP_BOOLGADGET;
	g->GadgetRender = &gim->im1;
	g->SelectRender = &gim->im2;
	g->GadgetText = &nulltext;
	
	g->MutualExclude = 0;
	g->SpecialInfo = nil;
	g->GadgetID = 0;
	g->UserData = nil;
	
	return g;
}

void free_gadget(struct Gadget *g)
{
	deallocate(g, V_Gadget);
}

struct Window *connect_req(site *sp, b8 *s)
{
	struct Window *w;
	struct IntuitionBase *IntuitionBase;
	struct GfxBase *GfxBase;
	struct Screen *pub_screen;
	b32 screen_modeID;
	struct Rectangle rect;
	struct Gadget *cancel;
	struct IntuiText txt;
	int width, swidth, sheight, fheight;
	b8 *z;
	
	verify(sp, V_site);
	
	GfxBase = sp->GBase;
	IntuitionBase = sp->IBase;
	
	pub_screen = LockPubScreen(nil);
	if (pub_screen) {
		screen_modeID = GetVPModeID(&pub_screen->ViewPort);
		if (screen_modeID != INVALID_ID) {
			if (QueryOverscan(screen_modeID, &rect, OSCAN_TEXT)) {
				cancel = make_gadget(cancel_gim);
				if (cancel) {
					z = (b8 *)allocate(strlen(s) + 19, V_cstr);
					if (z) {
						strcpy(z, strings[MSG_CONNECTING_TO]);
						strcat(z, s);
						strcat(z, " ...");
						
						txt.FrontPen = 1;
						txt.BackPen = 0;
						txt.DrawMode = JAM1;
						txt.LeftEdge = 0;
						txt.TopEdge = 0;
						txt.ITextFont = pub_screen->Font;
						txt.IText = z;
						txt.NextText = nil;
						
						if (pub_screen->Font) fheight = pub_screen->Font->ta_YSize;
						else fheight = 8;
						
						width = IntuiTextLength(&txt) * 4 / 3;
						
						txt.LeftEdge = width / 8;
						txt.TopEdge = fheight / 2;
						
						swidth = rect.MaxX - rect.MinX + 1;
						sheight = rect.MaxY - rect.MinY + 1;
						
						if (pub_screen->TopEdge > 0)
							sheight -= pub_screen->TopEdge;
						
						if (sheight > pub_screen->Height) sheight = pub_screen->Height;
						if (swidth > pub_screen->Width) swidth = pub_screen->Width;
						
						if (sheight < fheight * 6) sheight = fheight * 6;
						if (swidth < width) swidth = width;
						
						w = OpenWindowTags(nil,
							WA_Left,	swidth / 2 - pub_screen->LeftEdge - width / 2,
							WA_Top,		sheight / 2 - pub_screen->TopEdge - fheight * 3,
							WA_Width,	width,
							WA_Height,	fheight * 6,
							WA_Flags,	WFLG_DEPTHGADGET | WFLG_DRAGBAR |
									WFLG_SMART_REFRESH | 
									WFLG_NOCAREREFRESH,
							WA_IDCMP,	IDCMP_GADGETUP,
							WA_PubScreen,	pub_screen,
							WA_Title,	strings[MSG_CONNECTING],
							TAG_END,	0
						);
						
						if (w) {
							UnlockPubScreen(nil, pub_screen);
							PrintIText(w->RPort, &txt, 0, w->BorderTop);
							deallocate(z, V_cstr);
							w->UserData = (void *)cancel;
							cancel->LeftEdge = w->Width - w->BorderRight - fheight / 2 - cancel->Width;
							cancel->TopEdge = w->Height - w->BorderBottom - fheight / 3 - cancel->Height;
							AddGadget(w, cancel, (b32)0);
							RefreshGList(cancel, w, nil, 1);
							
							return w;
						}
						
						deallocate(z, V_cstr);
					}
					
					free_gadget(cancel);
				}
			}
		}
		UnlockPubScreen(nil, pub_screen);
	}
	
	return nil;
}

void close_req(site *sp, struct Window *w)
{
	struct Gadget *cancel;
	struct Message *msg;
	
	struct IntuitionBase *IntuitionBase;
	
	verify(sp, V_site);
	
	IntuitionBase = sp->IBase;
	
	Forbid();
	while (msg = GetMsg(w->UserPort)) ReplyMsg(msg);

	cancel = (struct Gadget *)w->UserData;
	RemoveGadget(w, cancel);

	free_gadget(cancel);
	
	CloseWindow(w);
	Permit();
}

struct phdata {
	b8 password[MAX_PASS_LENGTH + 1];
	b8 undo[MAX_PASS_LENGTH + 1];
};

boolean __saveds __asm password_hook(register __a0 struct Hook *hook, 
				register __a1 b32 *msg, 
				register __a2 struct SGWork *sgw)
{
	struct phdata *phd;
	
	phd = hook->h_Data;
	
	if (*msg == SGH_KEY) {
		switch (sgw->EditOp) {
		case EO_REPLACECHAR:
		case EO_INSERTCHAR:
			phd->password[sgw->BufferPos - 1] = sgw->WorkBuffer[sgw->BufferPos - 1];
			sgw->WorkBuffer[sgw->BufferPos - 1] = '*';
			break;
		case EO_MOVECURSOR:
			sgw->Actions &=~ SGA_USE;
			sgw->Actions |= SGA_BEEP;
			break;
		case EO_RESET:
			strcpy(phd->password, phd->undo);
			break;
		}
		if (sgw->BufferPos != sgw->NumChars) {
			sgw->BufferPos = sgw->NumChars;
			sgw->Actions |= SGA_REDISPLAY;
		}
		phd->password[sgw->NumChars] = 0;
		return true;
	} else if (*msg == SGH_CLICK) {
		sgw->BufferPos = sgw->NumChars;
		sgw->Actions |= SGA_REDISPLAY;
		return true;
	} else {
		return false;
	}
}

boolean user_pass_request(site *sp, struct Window *canw)
/*
 * open a requester asking for username and password, filled in as appropriate
 * Inputs:
 *	sp	: site pointer
 *	canw	: cancel window (will still be open in the background, and may be used)
 *
 * Result:
 *	false if cancel is selected in either canw or the requester, or a major failure occurred
 *	true if login is selected, or return is pressed in password field
 */
{
	struct Gadget *glist, *gad, *login, *cancel, *userg, *passg;
	struct NewGadget user, pass;
	struct Screen *s;
	void *vi;
	b32 screen_modeID;
	struct Rectangle rect;
	struct Window *w;
	sb32 swidth, sheight, fheight, wheight;
	b32 signals, csig;
	struct IntuiMessage *im;
	struct Hook pass_hook;
	b8 *z;
	struct phdata phd;

	struct IntuitionBase *IntuitionBase;
	struct Library *GadToolsBase;
	struct GfxBase *GfxBase;
	
	verify(sp, V_site);
	
	IntuitionBase = sp->IBase;
	GfxBase = sp->GBase;
	GadToolsBase = sp->GTBase;

	glist = nil;
	
	pass_hook.h_Entry = (b32 (*)())password_hook;
	pass_hook.h_SubEntry = (b32 (*)())nil;
	pass_hook.h_Data = (void *)&phd;
	
	s = LockPubScreen(nil);
	if (!s) return false;

	ScreenToFront(s);
	
	if (s->Font) fheight = s->Font->ta_YSize;
	else fheight = 8;
	
	if (sp->password) {
		strcpy(phd.password, sp->password);
		strcpy(phd.undo, sp->password);
		
		z = sp->password;
		while (*z) {
			*z++ = '*';
		}
	} else {
		phd.password[0] = 0;
		phd.undo[0] = 0;
	}
	
	wheight = fheight * 8;
	
	vi = GetVisualInfo(s, TAG_END);
	if (vi != nil) {
		screen_modeID = GetVPModeID(&s->ViewPort);
		if (screen_modeID != INVALID_ID) {
			if (QueryOverscan(screen_modeID, &rect, OSCAN_TEXT)) {
				swidth = rect.MaxX - rect.MinX + 1;
				sheight = rect.MaxY - rect.MinY + 1;
				
				sprintf(sp->read_buffer, strings[MSG_LOGIN_TO], sp->host);
				
				w = OpenWindowTags(nil,
					WA_Left,	swidth / 4 - s->LeftEdge,
					WA_Top,		sheight / 2 - wheight / 2 - s->TopEdge,
					WA_Width,	swidth / 2,
					WA_Height,	wheight,
					WA_IDCMP,	IDCMP_ACTIVEWINDOW | IDCMP_REFRESHWINDOW | 
							BUTTONIDCMP | STRINGIDCMP,
					WA_PubScreen,	s,
					WA_Activate,	true,
					WA_DragBar,	true,
					WA_DepthGadget,	true,
					WA_Title,	sp->read_buffer,
					WA_AutoAdjust,	true,
					TAG_END
				);
				if (w) {
					gad = CreateContext(&glist);
					if (gad) {
						user.ng_LeftEdge = w->Width / 4;
						user.ng_TopEdge = w->BorderTop + fheight / 2;
						user.ng_Width = (w->Width * 3 / 4) - w->BorderRight - fheight / 2;
						user.ng_Height = fheight * 3 / 2;
						user.ng_GadgetText = strings[MSG_USER_NAME];
						user.ng_TextAttr = s->Font;
						user.ng_GadgetID = 1;
						user.ng_Flags = PLACETEXT_LEFT;
						user.ng_VisualInfo = vi;
						
						pass.ng_LeftEdge = w->Width / 4;
						pass.ng_TopEdge = w->BorderTop + (fheight * 5 / 2);
						pass.ng_Width = (w->Width * 3 / 4) - w->BorderRight - fheight / 2;
						pass.ng_Height = fheight * 3 / 2;
						pass.ng_GadgetText = strings[MSG_PASSWORD_NAME];
						pass.ng_TextAttr = s->Font;
						pass.ng_GadgetID = 2;
						pass.ng_Flags = PLACETEXT_LEFT;
						pass.ng_VisualInfo = vi;
						
						userg = gad = CreateGadget(STRING_KIND, gad, &user,
							GTST_String,	sp->user,
							GTST_MaxChars,	MAX_USER_LENGTH,
							TAG_END
						);
						
						passg = gad = CreateGadget(STRING_KIND, gad, &pass,
							GTST_String,	sp->password,
							GTST_MaxChars,	MAX_PASS_LENGTH,
							GTST_EditHook,	(b32)&pass_hook,
							TAG_END
						);
						
						if (gad) {
							login = make_gadget(login_gim);
							if (login) {
								cancel = make_gadget(cancel_gim);
								if (cancel) {
									login->LeftEdge = fheight / 2 + w->BorderLeft;
									login->TopEdge = w->Height - w->BorderBottom - login->Height - fheight / 2;
									
									cancel->LeftEdge = w->Width - w->BorderRight - cancel->Width - fheight / 2;
									cancel->TopEdge = login->TopEdge;
									
									login->GadgetID = 3;
									cancel->GadgetID = 4;
									
									AddGadget(w, cancel, 100);
									AddGadget(w, login, 100);
							
									AddGList(w, glist, 0, 100, nil);
									
									RefreshGadgets(glist, w, nil);
									GT_RefreshWindow(w, nil);
							
									goto listen;
								}
								free_gadget(login);
							}
						}
						
						FreeGadgets(glist);
					}
					CloseWindow(w);
				}
			}
		}
		FreeVisualInfo(vi);
	}
	
	UnlockPubScreen(nil, s);
	
	return false;

listen:
	csig = 1 << canw->UserPort->mp_SigBit | sp->abort_signals | sp->disconnect_signals;
	signals = (1 << w->UserPort->mp_SigBit) | csig;

	while (1) {
		if (Wait(signals) & csig) {
			CloseWindow(w);

			FreeGadgets(glist);

			free_gadget(cancel);
			free_gadget(login);

			FreeVisualInfo(vi);
			UnlockPubScreen(nil, s);
							
			return false;
		}
		
		while (im = GT_GetIMsg(w->UserPort)) {
			gad = (struct Gadget *)im->IAddress;
			
			switch (im->Class) {
			case IDCMP_ACTIVEWINDOW:
				if (sp->needs_user) {
					ActivateGadget(userg, w, nil);
				} else {
					ActivateGadget(passg, w, nil);
				}
				break;
			case IDCMP_GADGETUP:
				switch (gad->GadgetID) {
				case 1:
					if (im->Code == 0) {
						ActivateGadget(passg, w, nil);
					}
					sp->needs_user = false;
					break;
				case 2:
					if (im->Code != 0) {
						break;
					}
					/* else fall through to Login */
				case 3:
					if (sp->user) deallocate(sp->user, V_cstr);
					if (sp->password) deallocate(sp->password, V_cstr);
					
					z = ((struct StringInfo *)userg->SpecialInfo)->Buffer;
					if (z[0] != '\0') {
						sp->user = (b8 *)allocate(strlen(z) + 1, V_cstr);
						if (sp->user) {
							strcpy(sp->user, z);
						}
					} else {
						sp->user = nil;
					}
					
					z = phd.password;
					if (z[0] != '\0') {
						sp->password = (b8 *)allocate(strlen(z) + 1, V_cstr);
						if (sp->password) {
							strcpy(sp->password, z);
						}
					} else {
						sp->password = nil;
					}
					
					Forbid();
					GT_ReplyIMsg(im);
					
					CloseWindow(w);
					Permit();
					
					FreeGadgets(glist);
					free_gadget(cancel);
					free_gadget(login);

					FreeVisualInfo(vi);
					UnlockPubScreen(nil, s);
							
					return true;
				case 4:
					Forbid();
					GT_ReplyIMsg(im);
					
					CloseWindow(w);
					Permit();
					
					FreeGadgets(glist);
					
					free_gadget(cancel);
					free_gadget(login);

					FreeVisualInfo(vi);
					UnlockPubScreen(nil, s);
							
					return false;
				}
				break;
			case IDCMP_REFRESHWINDOW:
				GT_BeginRefresh(w);
				GT_EndRefresh(w, true);
				break;
			}
			
			GT_ReplyIMsg(im);
		}
	}
}

struct Window *open_main_window(struct List *site_labels, struct IntuitionBase *IntuitionBase, struct Library *GadToolsBase, struct GfxBase *GfxBase)
{
	struct Gadget *glist, *gad;
	struct NewGadget ng;
	struct Screen *s;
	void *vi;
	b32 screen_modeID;
	struct Rectangle rect;
	struct Window *w;
	sb32 swidth, sheight;
	
	glist = nil;
	
	s = LockPubScreen(nil);
	if (!s) return nil;
	
	vi = GetVisualInfo(s, TAG_END);
	if (vi != nil) {
		screen_modeID = GetVPModeID(&s->ViewPort);
		if (screen_modeID != INVALID_ID) {
			if (QueryOverscan(screen_modeID, &rect, OSCAN_TEXT)) {
				swidth = rect.MaxX - rect.MinX + 1;
				sheight = rect.MaxY - rect.MinY + 1;
				
				w = OpenWindowTags(nil,
					WA_Left,	swidth / 3 - s->LeftEdge,
					WA_Top,		sheight / 4 - s->TopEdge,
					WA_Width,	swidth / 3,
					WA_Height,	sheight / 2,
					WA_IDCMP,	IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | LISTVIEWIDCMP,
					WA_PubScreen,	s,
					WA_Activate,	true,
					WA_DragBar,	true,
					WA_DepthGadget,	true,
					WA_CloseGadget,	true,
					WA_Title,	strings[MSG_CURRENT_SITES],
					WA_AutoAdjust,	true,
					TAG_END
				);
				if (w) {
					gad = CreateContext(&glist);
					if (gad) {
						ng.ng_TextAttr = s->Font;
						ng.ng_VisualInfo = vi;
						ng.ng_LeftEdge = w->BorderLeft;
						ng.ng_TopEdge = w->BorderTop;
						ng.ng_Width = w->Width - w->BorderLeft - w->BorderRight;
						ng.ng_Height = w->Height - w->BorderTop - w->BorderBottom;
						ng.ng_GadgetText = nil;
						ng.ng_GadgetID = 0;
						ng.ng_Flags = 0;
						gad = CreateGadget(LISTVIEW_KIND, gad, &ng, 
							GTLV_ReadOnly,	false, 
							GTLV_Labels,	site_labels,
							TAG_END
						);
						if (gad) {
							AddGList(w, glist, 0, -1, nil);
							
							RefreshGadgets(glist, w, nil);
							GT_RefreshWindow(w, nil);
							
							w->UserData = (void *)glist;
							
							FreeVisualInfo(vi);
							UnlockPubScreen(nil, s);
							
							return w;
						}
						FreeGadgets(glist);
					}
					CloseWindow(w);
				}
			}
		}
		FreeVisualInfo(vi);
	}
	
	UnlockPubScreen(nil, s);
	
	return nil;
}

void close_main_window(struct Window *w, struct IntuitionBase *IntuitionBase, struct Library *GadToolsBase)
{
	struct Gadget *glist;
	struct IntuiMessage *im;
	
	Forbid();
	while (im = GT_GetIMsg(w->UserPort)) {
		if (im->Class == IDCMP_REFRESHWINDOW) {
			GT_BeginRefresh(w);
			GT_EndRefresh(w, true);
		}
		GT_ReplyIMsg(im);
	}
	
	glist = (struct Gadget *)w->UserData;
	
	CloseWindow(w);
	FreeGadgets(glist);
	Permit();
}

#define V_List 19561
#define V_Node 20079

struct List *site_list(void)
{
	struct List *l;
	struct Node *n;
	site *sp;
	
	l = (struct List *)allocate(sizeof(*l), V_List);
	if (!l) return l;
	
	NewList(l);
	
	Forbid();
	sp = sites;
	while (sp) {
		n = (struct Node *)allocate(sizeof(*n), V_Node);
		if (!n) {
			Permit();
			return l;
		}
		
		n->ln_Name = (b8 *)allocate(strlen(sp->name) + 1, V_cstr);
		if (!n->ln_Name) {
			Permit();
			deallocate(n, V_Node);
			return l;
		}
		
		strcpy(n->ln_Name, sp->name);
		
		n->ln_Pri = 0;
		AddTail(l, n);
		sp = sp->next;
	}
	Permit();
	
	return l;
}

void free_labels(struct List *l)
{
	struct Node *n;
	
	while (n = RemHead(l)) {
		deallocate(n->ln_Name, V_cstr);
		deallocate(n, V_Node);
	}
	
	deallocate(l, V_List);
}

void draw_fill_bar(site *sp, struct IntuitionBase *IntuitionBase, struct GfxBase *GfxBase)
{
	struct Window *w;
	struct RastPort *rp;
	b32 x1, y1, x2, y2, gap;
	
	verify(sp, V_site);
	
	w = sp->status_window;
	if (!w) return;
	
	rp = w->RPort;
	
	x1 = w->BorderLeft;
	y1 = w->BorderTop;
	
	x2 = w->Width - w->BorderRight - 1;
	y2 = sp->abort_gadget->TopEdge - 1;
	
	gap = (y2 - y1) / 4;
	
	x2 = x1 + (x2 - x1) * 3 / 4;
	
	x1 += gap;
	y1 += gap;
	y2 -= gap;
	
	SetDrMd(rp, JAM2);
	
	SetAPen(rp, lightpen);
	Move(rp, x2, y1);
	Draw(rp, x2, y2);
	Draw(rp, x1, y2);
	SetAPen(rp, darkpen);
	Draw(rp, x1, y1);
	Draw(rp, x2, y1);
	
	SetAPen(rp, fillpen);
	
	x1++;
	y1++;
	y2--;
	x2--;
	
	if (sp->file_list) {
		verify(sp->file_list, V_file_info);
		
		x2 = x1 + ((x2 - x1) * sp->file_list->rpos) / sp->file_list->end;
		RectFill(rp, x1, y1, x2, y2);
	}
}

void update_fill_bar(site *sp, struct IntuitionBase *IntuitionBase, struct GfxBase *GfxBase)
{
	struct Window *w;
	struct RastPort *rp;
	b32 x1, y1, x2, y2, gap;
	struct IntuiText txt;
	b8 buffer[20], *z;
	file_info *fip;
	
	verify(sp, V_site);
	
	w = sp->status_window;
	if (!w) return;
	
	Forbid();
	fip = sp->file_list;
	if (!fip) {
		Permit();
		return;
	}
	verify(fip, V_file_info);
	
	z = fip->fname + strlen(fip->fname) - 1;
	while (z > fip->fname && *z != '/') z--;
	if (*z == '/') z++;
	
	rp = w->RPort;
	
	x1 = w->BorderLeft;
	y1 = w->BorderTop;
	
	x2 = w->Width - w->BorderRight - 1;
	y2 = sp->abort_gadget->TopEdge - 1;
	
	gap = (y2 - y1) / 4;
	
	x2 = x1 + (x2 - x1) * 3 / 4 - 1;
	
	x1 += gap + 1;
	y1 += gap + 1;
	y2 -= gap + 1;
	
	if (sp->site_state == SS_WRITING || !fip->end) {	/* just print how many k we have transferred */
		txt.FrontPen = textpen;
		txt.BackPen = 0;
		txt.DrawMode = JAM2;
		txt.LeftEdge = x1;
		txt.TopEdge = y1;
		txt.ITextFont = sp->status_window->WScreen->Font;
		txt.NextText = nil;
		txt.IText = z;
		PrintIText(rp, &txt, 0, 0);
		
		txt.LeftEdge += IntuiTextLength(&txt);
		txt.FrontPen = textpen;
		txt.IText = buffer;

		buffer[0] = ':';
		buffer[1] = ' ';
		x1 = 2;
		y1 = fip->rpos / 1024;
		buffer[x1] = '0';
		while (y1 >= 100000) {
			buffer[x1]++;
			y1 -= 100000;
		}
		if (buffer[x1] > '0') x1++;
		buffer[x1] = '0';
		while (y1 >= 10000) {
			buffer[x1]++;
			y1 -= 10000;
		}
		if (buffer[x1] > '0' || x1 != 2) x1++;
		buffer[x1] = '0';
		while (y1 >= 1000) {
			buffer[x1]++;
			y1 -= 1000;
		}
		if (buffer[x1] > '0' || x1 != 2) x1++;
		buffer[x1] = '0';
		while (y1 >= 100) {
			buffer[x1]++;
			y1 -= 100;
		}
		if (buffer[x1] > '0' || x1 != 2) x1++;
		buffer[x1] = '0';
		while (y1 >= 10) {
			buffer[x1]++;
			y1 -= 10;
		}
		if (buffer[x1] > '0' || x1 != 2) x1++;
		buffer[x1++] = y1 + '0';
		buffer[x1++] = ' ';
		buffer[x1++] = 'k';
		buffer[x1++] = ' ';
		buffer[x1++] = ' ';
		buffer[x1] = 0;

		PrintIText(rp, &txt, 0, 0);
		
		Permit();
		
		return;
	}
	
	/* reading ... can do a filler bar */
	
	SetDrMd(rp, JAM2);
	SetAPen(rp, fillpen);

	txt.FrontPen = lightpen;
	txt.BackPen = 0;
	txt.DrawMode = JAM1;
	txt.TopEdge = y1;
	txt.ITextFont = sp->status_window->WScreen->Font;
	txt.NextText = nil;
	txt.IText = z;
	
	txt.LeftEdge = (x1 + x2) / 2 - IntuiTextLength(&txt) / 2;
	
	x2 = x1 + ((x2 - x1) * sp->file_list->rpos) / sp->file_list->end;
	RectFill(rp, x1, y1, x2, y2);

	PrintIText(rp, &txt, 0, 0);
	
	Permit();
	
	return;
}

void draw_state(site *sp, struct IntuitionBase *IntuitionBase, struct GfxBase *GfxBase)
{
	struct IntuiText txt;
	
	/* gadget states */
	switch (sp->site_state) {
	case SS_DISCONNECTED:
	case SS_DISCONNECTING:
		if (!(sp->abort_gadget->Flags & GFLG_DISABLED)) {
			OffGadget(sp->abort_gadget, sp->status_window, nil);
		}
		if (!(sp->disconnect_gadget->Flags & GFLG_DISABLED)) {
			OffGadget(sp->disconnect_gadget, sp->status_window, nil);
		}
		break;
	case SS_CONNECTING:
	case SS_IDLE:
	case SS_LOGIN:
	case SS_ABORTING:
		if (!(sp->abort_gadget->Flags & GFLG_DISABLED)) {
			OffGadget(sp->abort_gadget, sp->status_window, nil);
		}
		if (sp->disconnect_gadget->Flags & GFLG_DISABLED) {
			OnGadget(sp->disconnect_gadget, sp->status_window, nil);
		}
		break;
	default:
		if (sp->abort_gadget->Flags & GFLG_DISABLED) {
			OnGadget(sp->abort_gadget, sp->status_window, nil);
		}
		if (sp->disconnect_gadget->Flags & GFLG_DISABLED) {
			OnGadget(sp->disconnect_gadget, sp->status_window, nil);
		}
		break;
	}
	
	txt.FrontPen = textpen;
	txt.BackPen = 0;
	txt.DrawMode = JAM2;
	txt.LeftEdge = 2;
	txt.TopEdge = 0;
	txt.ITextFont = sp->status_window->WScreen->Font;
	txt.NextText = nil;
	txt.IText = strings[MSG_STATE_UNKNOWN + sp->site_state];
	
	PrintIText(sp->status_window->RPort, &txt, sp->status_window->BorderLeft,
		sp->abort_gadget->TopEdge + sp->abort_gadget->Height / 4);
	
	if (sp->quick) {
		txt.LeftEdge += IntuiTextLength(&txt);
		txt.IText = strings[MSG_QUICK_FLAG];
		PrintIText(sp->status_window->RPort, &txt, sp->status_window->BorderLeft,
			sp->abort_gadget->TopEdge + sp->abort_gadget->Height / 4);
	}
	
	if (sp->site_state == SS_READING && sp->file_list && sp->file_list->end > 0) {
		draw_fill_bar(sp, IntuitionBase, GfxBase);
	}
}

void clear_state(site *sp, struct IntuitionBase *IntuitionBase, struct GfxBase *GfxBase)
{
	verify(sp, V_site);
	truth(sp->status_window != nil);

	SetAPen(sp->status_window->RPort, 0);
	SetDrMd(sp->status_window->RPort, JAM1);
	
	RectFill(sp->status_window->RPort, sp->status_window->BorderLeft, sp->status_window->BorderTop,
		sp->abort_gadget->LeftEdge - 1, sp->status_window->Height - sp->status_window->BorderBottom - 1);
	
	RectFill(sp->status_window->RPort, sp->abort_gadget->LeftEdge, sp->status_window->BorderTop,
		sp->status_window->Width - sp->status_window->BorderRight - 1, 
		sp->abort_gadget->TopEdge - 1);
}

void open_status_window(site *sp, struct MsgPort *wport, struct IntuitionBase *IntuitionBase, struct GfxBase *GfxBase)
{
	struct Screen *s;
	struct Rectangle rect;
	b32 screen_modeID;
	struct Gadget *aborg, *disg;
	b16 swidth, sheight, fheight;
	struct Window *w;

	verify(sp, V_site);
	
	if (sp->status_window) {
		WindowToFront(sp->status_window);
		ActivateWindow(sp->status_window);
		return;
	}
	
	s = LockPubScreen(nil);
	if (!s) return;
	
	if (s->Font) fheight = s->Font->ta_YSize;
	else fheight = 8;
	
	screen_modeID = GetVPModeID(&s->ViewPort);
	if (screen_modeID != INVALID_ID) {
		if (QueryOverscan(screen_modeID, &rect, OSCAN_TEXT)) {
			aborg = make_gadget(abort_gim);
			if (aborg) {
				aborg->GadgetID = 1;
				disg = make_gadget(disconnect_gim);
				if (disg) {
					disg->GadgetID = 2;
					swidth = rect.MaxX - rect.MinX + 1;
					sheight = rect.MaxY - rect.MinY + 1;
					
					w = OpenWindowTags(nil,
						WA_Left,	swidth / 4 - s->LeftEdge,
						WA_Top,		sheight / 3 - s->TopEdge,
						WA_Width,	swidth / 2,
						WA_Height,	fheight * 4 + disg->Height,
						WA_Flags,	WFLG_DEPTHGADGET | WFLG_DRAGBAR |
								WFLG_SMART_REFRESH | 
								WFLG_CLOSEGADGET |
								WFLG_NOCAREREFRESH,
						WA_IDCMP,	0,
						WA_PubScreen,	s,
						WA_Title,	sp->name,
						TAG_END,	0
					);
					if (w) {
						UnlockPubScreen(nil, s);
						w->UserPort = wport;
						ModifyIDCMP(w, IDCMP_CLOSEWINDOW | IDCMP_GADGETUP);
						
						aborg->NextGadget = disg;
						disg->NextGadget = nil;
						
						sp->abort_gadget = aborg;
						sp->disconnect_gadget = disg;
						
						disg->LeftEdge = w->Width - w->BorderRight - disg->Width - fheight / 2;
						disg->TopEdge = w->Height - w->BorderBottom - disg->Height - fheight / 3;
						
						aborg->TopEdge = disg->TopEdge;
						aborg->LeftEdge = disg->LeftEdge - aborg->Width - fheight / 2;
						
						AddGList(w, aborg, (b32)-1, 2, nil);
						RefreshGadgets(aborg, w, nil);
						
						sp->status_window = w;
						w->UserData = (void *)sp;
						
						draw_state(sp, IntuitionBase, GfxBase);
						
						return;
					}
					free_gadget(disg);
				}
				free_gadget(aborg);
			}
		}
	}
	
	UnlockPubScreen(nil, s);
	return;
}

void close_status_window(site *sp, struct MsgPort *wport, struct IntuitionBase *IntuitionBase)
{
	struct IntuiMessage *im;
	struct Node *succ;
	struct Window *w;
	
	verify(sp, V_site);
	
	w = sp->status_window;
	
	if (!w) return;
	
	Forbid();
	im = (struct IntuiMessage *)wport->mp_MsgList.lh_Head;
	while (succ = im->ExecMessage.mn_Node.ln_Succ) {
		if (im->IDCMPWindow == w) {
			Remove((struct Node *)im);
			ReplyMsg((struct Message *)im);
		}
		im = (struct IntuiMessage *)succ;
	}
	
	w->UserPort = nil;
	ModifyIDCMP(w, 0);
	Permit();
	
	CloseWindow(w);
	
	free_gadget(sp->abort_gadget);
	free_gadget(sp->disconnect_gadget);
	
	sp->status_window = nil;
	
	return;
}

void __saveds status_handler(void)
{
	struct Process *me;
	struct MsgPort *rank, *sync, *myport, *cxport, *winport;
	status_message *reserve, *startup, *sm;
	struct Library *GadToolsBase, *CxBase;
	struct IntuitionBase *IntuitionBase;
	struct GfxBase *GfxBase;
	CxObj *broker, *filter, *sender, *translate;
	CxMsg *cxmsg;
	b32 signals;
	b32 msgid, msgtype;
	struct Window *mainw;
	struct NewBroker nb;
	struct List *site_labels;
	struct IntuiMessage *imsg;
	struct Node *n;
	site *sp;
	
	mainw = nil;
	site_labels = nil;
	
	me = (struct Process *)FindTask(0l);
	myport = &me->pr_MsgPort;
	
	WaitPort(myport);
	startup = (status_message *)GetMsg(myport);
	
	mem_tracking_on();
	
	IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 36);
	if (IntuitionBase) {
		GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 36);
		if (GfxBase) {
			GadToolsBase = OpenLibrary("gadtools.library", 0);
			if (GadToolsBase) {
				CxBase = OpenLibrary("commodities.library", 0);
				if (CxBase) {
					rank = CreatePort(0, 0);
					if (rank) {
						sync = CreatePort(0, 0);
						if (sync) {
							cxport = CreatePort(0, 0);
							if (cxport) {
								winport = CreatePort(0, 0);
								if (winport) {
									reserve = (status_message *)allocate_flags(sizeof(*reserve), MEMF_PUBLIC, V_status_message);
									if (reserve) {
										ensure(reserve, V_status_message);
	
										reserve->header.mn_Length = sizeof(*reserve);
										reserve->header.mn_ReplyPort = sync;
										reserve->header.mn_Node.ln_Name = "ftpstatus reserve message";
										reserve->header.mn_Node.ln_Type = NT_MESSAGE;
										reserve->header.mn_Node.ln_Pri = 0;
								
										nb.nb_Version = NB_VERSION;
										nb.nb_Name = strings[MSG_BROKER_NAME];
										nb.nb_Title = "FTPMount v" VERSION "." REVISION;
										nb.nb_Descr = strings[MSG_BROKER_DESCR];
										nb.nb_Unique = NBU_DUPLICATE;
										nb.nb_Flags = COF_SHOW_HIDE;
										nb.nb_Pri = 0;
										nb.nb_Port = cxport;
										nb.nb_ReservedChannel = 0;
				
										broker = CxBroker(&nb, nil);
										if (broker) {
											/* this hotkey stuff taken directly from RKM example */
											if (filter = CxFilter(strings[MSG_HOTKEY])) {
												/* if we can't add the hotkey stuff, don't fail */
												if (sender = CxSender(cxport, 1)) {
													if (translate = (CxTranslate(nil))) {
														AttachCxObj(broker, filter);
														AttachCxObj(filter, sender);
														AttachCxObj(filter, translate);
													}
												}
											}
											ActivateCxObj(broker, 1l);
									
											startup->command = 0;
											ReplyMsg(&startup->header);
											goto begin_listening;
										}
										deallocate(reserve, V_status_message);
									}
									DeletePort(winport);
								}
								DeletePort(cxport);
							}
							DeletePort(sync);
						}
						DeletePort(rank);
					}
					CloseLibrary(CxBase);
				}
				CloseLibrary(GadToolsBase);
			}
			CloseLibrary((struct Library *)GfxBase);
		}
		CloseLibrary((struct Library *)IntuitionBase);
	}
	
	startup->command = SM_KILL;
	Forbid();
	ReplyMsg(&startup->header);
	return;

begin_listening:
	signals = (1 << myport->mp_SigBit) | (1 << cxport->mp_SigBit) | (1 << winport->mp_SigBit);

	while (1) {
		Wait(signals);
		
		while (cxmsg = (CxMsg *)GetMsg(cxport)) {
			msgid = CxMsgID(cxmsg);
			msgtype = CxMsgType(cxmsg);
			
			ReplyMsg((struct Message *)cxmsg);
			
			switch (msgtype) {
			case CXM_IEVENT:	/* copied from CXCMD_APPEAR below */
				if (mainw != nil) {
					close_main_window(mainw, IntuitionBase, GadToolsBase);
					mainw = nil;
					free_labels(site_labels);
				}
				
				site_labels = site_list();
				mainw = open_main_window(site_labels, IntuitionBase, GadToolsBase, GfxBase);
				signals = (1 << cxport->mp_SigBit) |
						(1 << myport->mp_SigBit) |
						(1 << winport->mp_SigBit);
				if (mainw != nil) {
					signals |= (1 << mainw->UserPort->mp_SigBit);
				}
				
				break;
			case CXM_COMMAND:
				switch (msgid) {
				case CXCMD_DISABLE:
					reserve->command = SM_SUSPEND;
					PutMsg(status_control, &reserve->header);
					WaitPort(sync); GetMsg(sync);
					
					ActivateCxObj(broker, 0l);
					break;
				case CXCMD_ENABLE:
					reserve->command = SM_RESUME;
					PutMsg(status_control, &reserve->header);
					WaitPort(sync); GetMsg(sync);
					
					ActivateCxObj(broker, 1l);
					break;
				case CXCMD_KILL:
					reserve->command = SM_KILL;
					PutMsg(status_control, &reserve->header);
					WaitPort(sync); GetMsg(sync);
					
					break;
				case CXCMD_APPEAR:
					if (mainw != nil) {
						close_main_window(mainw, IntuitionBase, GadToolsBase);
						mainw = nil;
						free_labels(site_labels);
					}
					
					site_labels = site_list();
					mainw = open_main_window(site_labels, IntuitionBase, GadToolsBase, GfxBase);
					signals = (1 << cxport->mp_SigBit) |
							(1 << myport->mp_SigBit) |
							(1 << winport->mp_SigBit);
					if (mainw != nil) {
						signals |= (1 << mainw->UserPort->mp_SigBit);
					}
					
					break;
				case CXCMD_DISAPPEAR:
					if (mainw != nil) {
						close_main_window(mainw, IntuitionBase, GadToolsBase);
						mainw = nil;
						free_labels(site_labels);
						
						signals = (1 << cxport->mp_SigBit) |
								(1 << myport->mp_SigBit) |
								(1 << winport->mp_SigBit);
					}
					break;
				}
			}
		}
		
		while (mainw && (imsg = GT_GetIMsg(mainw->UserPort))) {
			switch (imsg->Class) {
			case IDCMP_CLOSEWINDOW:
				GT_ReplyIMsg(imsg);
				close_main_window(mainw, IntuitionBase, GadToolsBase);
				mainw = nil;
				free_labels(site_labels);
				
				signals = (1 << cxport->mp_SigBit) |
						(1 << myport->mp_SigBit) |
						(1 << winport->mp_SigBit);
				continue;
			case IDCMP_REFRESHWINDOW:
				GT_BeginRefresh(mainw);
				GT_EndRefresh(mainw, true);
				break;
			case IDCMP_GADGETUP:
				msgid = imsg->Code;
				GT_ReplyIMsg(imsg);
				close_main_window(mainw, IntuitionBase, GadToolsBase);
				mainw = nil;
				
				n = site_labels->lh_Head;
				while (msgid-- && (n->ln_Succ)) {
					n = n->ln_Succ;
				}
				
				if (n->ln_Succ) {
					sp = sites;
					while (sp) {
						if (strcmp(sp->name, n->ln_Name) == 0) {
							break;
						}
						sp = sp->next;
					}
					
					if (sp) {
						open_status_window(sp, winport, IntuitionBase, GfxBase);
					}
				}
				
				free_labels(site_labels);
				
				signals = (1 << cxport->mp_SigBit) |
						(1 << myport->mp_SigBit) |
						(1 << winport->mp_SigBit);
				continue;
			default:
				break;
			}
			GT_ReplyIMsg(imsg);
		}
		
		while (imsg = (struct IntuiMessage *)GetMsg(winport)) {
			sp = (site *)imsg->IDCMPWindow->UserData;
			verify(sp, V_site);
			
			switch (imsg->Class) {
			case IDCMP_CLOSEWINDOW:
				ReplyMsg((struct Message *)imsg);
				close_status_window(sp, winport, IntuitionBase);
				continue;
			case IDCMP_GADGETUP:
				if (((struct Gadget *)imsg->IAddress)->GadgetID == 1) {
					/* abort */
					Signal(sp->port->mp_SigTask, sp->abort_signals);
				} else {
					/* disconnect */
					Signal(sp->port->mp_SigTask, sp->disconnect_signals);
				}
				break;
			}
		
			ReplyMsg((struct Message *)imsg);
		}
		
		while (sm = (status_message *)GetMsg(myport)) {
			verify(sm, V_status_message);
			
			switch (sm->command) {
			case SM_KILL:
				if (mainw != nil) {
					close_main_window(mainw, IntuitionBase, GadToolsBase);
					free_labels(site_labels);
				}
				
				Forbid();
				while (cxmsg = (CxMsg *)GetMsg(cxport)) {
					ReplyMsg((struct Message *)cxmsg);
				}
				
				DeleteCxObjAll(broker);
				Permit();
				
				deallocate(reserve, V_status_message);
				
				DeletePort(winport);
				DeletePort(cxport);
				DeletePort(sync);
				DeletePort(rank);
				
				check_memory();

				CloseLibrary(CxBase);
				CloseLibrary(GadToolsBase);
				CloseLibrary((struct Library *)GfxBase);
				CloseLibrary((struct Library *)IntuitionBase);
				
				Forbid();
				ReplyMsg(&sm->header);
				return;
			case SM_NEW_SITE:
				if (sm->data) {
					open_status_window(sm->this_site, winport, IntuitionBase, GfxBase);
				}
				break;
			case SM_DEAD_SITE:
				close_status_window(sm->this_site, winport, IntuitionBase);
				break;
			case SM_STATE_CHANGE:
				if (sm->this_site->status_window) {
					clear_state(sm->this_site, IntuitionBase, GfxBase);
					draw_state(sm->this_site, IntuitionBase, GfxBase);
				}
				break;
			case SM_PROGRESS:
				if (sm->this_site->status_window) {
					update_fill_bar(sm->this_site, IntuitionBase, GfxBase);
				}
				break;
			}
			ReplyMsg(&sm->header);
		}
	}
}

