/* STDFILE -- Standard File Requestor. Version 2.0a 15 June 1987
 *
 * AUTHOR -- Peter da Silva      US (713) 497-4372
 *
 * Copyright (c) 1987 Peter da Silva, all rights reserved.
 *
 *	This module may be freely used in any product, commercial or
 *	otherwise, provided credit is given for this module and
 *	and provided this notice remains intact in the source. The
 *	intent of this module is to provide a standard file requestor
 *	such as is available on the Macintosh, in GEM on the IBM-PC
 *	and Atari ST, and in the Microsoft Windows software on the
 *	IBM-PC. The advantage this module has over other requestors
 *	is that it minimises disk accesses: an important consideration
 *	given the structure of AmigaDos directories. If you need to
 *	modify it for your needs, by all means go ahead... but please
 *	conform to the intent of this program as stated above. If you
 *	have suggestions for improvements, by all means call me at
 *	the number listed above.
 *
 * Enhancements in the current version:
 *
 *	Gadgets now boxed. Display generally cleaned up.
 *
 *	True "dictionary order" for searches.
 *
 *	Default pattern can now be specified. Default file name now
 *	specified in a single argument.
 *
 *	Directories always match.
 *
 *	Null pattern converted to "#?" universal wildcard.
 *
 *	If you attempt to build a file name longer than 128 characters the
 *	screen will flash and the operation will be aborted.
 *
 *	"Volumes" gadget, using the device list code in "mounted". This
 *	gadget brings up a list of all currently mounted volumes for
 *	selection. Volumes leaves the directory specification intact, so
 *	you can quickly return to where you left off.
 *
 *	"Parent" gadget, displays parent directory.
 *
 *	With these enhancements it is now possible to select any file on
 *	any device without touching the keyboard. This is now release 2.0,
 *	as it is significantly better than 1.0.
 *
 * Acknowledgements:
 *
 *	Thanks to Jeff Lydiatt for the pattern matching code in PatMatch.c
 *	Thanks to Jay Miner, =RJ= and the whole Amiga team for the Amiga
 *	itself.
 *
 * Environment:
 *
 *	IntuitionBase and GfxBase must be open. dos.library must be open
 *	under the name "DOSBase". Link with PatMatch.o and VolList.o.
 *
 * Usage:
 *
 *	#define MAXFILENAME 128
 *
 *	int stdfile(title, default_file, default_pat, name);
 *	char *title;
 *	char *default_file;
 *	char *default_pattern;
 *	char name[MAXFILENAME];
 *
 *	struct Screen *stdscreen;
 *
 *	STDFILE puts up a file requestor (actually, it's a plain window)
 *	in stdscreen. If stdscreen is NULL, the workbench screen is used.
 *	The requestor looks like this (allowing for the limitations of
 *	text):
 *
 *	+-----------------------------------+
 *	|o| Title ------------------- |  |  | title parameter, or "File Name"
 *	|-----------------------------------|
 *	| Directory: [                    ] | Directory parameter, or current.
 *	| File name: [                    ] | Default parameter, or empty.
 *	| Pattern:   [                    ] | Initially empty, if the user
 *	| +-------------------------------+ | enters anything here it will
 *	| | [Filename]                 |  | | be used to select files. The
 *	| | [Filename]                 |  | | file display will also be empty
 *	| | [Filename]                 |@@| | to start with to avoid excess
 *	| | [Filename]                 |@@| | disk I/O. If the user selects
 *	| |                            |@@| | here the directory will be
 *	| |                            |@@| | scanned looking for files
 *	| |                            |  | | matching the specified pattern,
 *	| |                            |  | | or "#?" if no pattern is given.
 *	| |                            |  | |
 *	| +-------------------------------+ | ACCEPT returns 1. CANCEL
 *	| [ACCEPT]    [VOLUMES]    [CANCEL] | or the close gadget return 0.
 *	+-----------------------------------+ VOLUMES displays volume names.
 *
 *	The number of filenames displayed is specified at compile time in the
 *	constant MAXFILES. The maximum size of a filename is specified in the
 *	constant MAXNAME. The parameter "Default file" will be broken into
 *	directory and file parts.
 */
char *Copyright =
"stdfile V2.0a. Copyright (c) 1987 Peter da Silva. All rights reserved.";
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <exec/memory.h>

/* These four to keep away those bloody warnings: */
void *malloc();
void *OpenWindow();
void *GetMsg();
void *AllocMem();

#define MAXFILES 8
#define MAXNAME 32
#define MAXFULL (MAXNAME*4)

/* SIZING PARAMS */
#define Z NULL
#define INDENT 6
#define LEFTMAR (INDENT-1)
#define BORDER 3
#define CHSIZ 8
#define HT CHSIZ
#define BASELINE 6
#define BUTWID (6*CHSIZ+INDENT*2)

/* GADGET BORDERS */
#define IN1 LEFTMAR+10*CHSIZ
#define IN3 LEFTMAR+3
#define IN4 -(INDENT+6*CHSIZ+1)
#define IN5 -(INDENT+CHSIZ*2)
#define IN6 ((WINWD-BUTWID)/3+INDENT)
#define IN7 (((WINWD-BUTWID)*2)/3+INDENT)
#define WD1 -(INDENT+IN1)
#define WD3 (6*CHSIZ)
#define WD4 (6*CHSIZ)
#define WD5 (CHSIZ*2+2)
#define WD6 (6*CHSIZ)
#define WD7 (6*CHSIZ)
#define TP1 (CHSIZ+BORDER)/*11*/
#define TP2 (TP1+HT+1)/*20*/
#define TP3 (TP2+HT+1)/*29*/
#define TP4 -(BORDER+HT4-1)/*-11*/
#define TP5 (TP3+HT+BORDER)/*40*/
#define HT4 (HT+1)
#define HT5 CHSIZ*MAXFILES+INDENT

#define WINHT (TP5 + HT5 + (-TP4) + BORDER) /*40+64+6+11+3*/
#define WINWD (INDENT*	4 + (MAXNAME+2)*CHSIZ) /*24+(32+2)*8 = 296*/
#define WININ 10
#define WINTP 20

#define HOMEX (INDENT+LEFTMAR)
#define HOMEY (TP5+BORDER)
#define LASTX (HOMEX+MAXNAME*CHSIZ)
#define LASTY (HOMEY+MAXFILES*CHSIZ)

#define BTP TP5
#define BIN LEFTMAR
#define BWD (WINWD-INDENT-BIN)
#define BHT (WINHT-BTP-(-TP4+BORDER+1))

#define SF GADGHCOMP|GRELWIDTH
#define SEL SELECTED
#define BF1 GADGHCOMP|GRELBOTTOM
#define BF2 GADGHCOMP|GRELBOTTOM|GRELRIGHT
#define PF GRELRIGHT

#define SA RELVERIFY
#define CEN STRINGCENTER
#define BA RELVERIFY
#define PA RELVERIFY

#define SI(n) (APTR)&STD_String[n]
#define G(n) &STD_Gadget[n]
#define IMAG (APTR)&STD_Image
#define PROP (APTR)&STD_Prop

#define SG STRGADGET
#define BG BOOLGADGET
#define PG PROPGADGET

#define FP AUTOBACKPEN
#define BP AUTOFRONTPEN

#define OKTEXT &STD_OK
#define NOTEXT &STD_CANCEL
#define VLTEXT &STD_VOLUME
#define PRTEXT &STD_PARENT

static int DoneFlag;

#define DirName SBuffer[0]
#define FileName SBuffer[1]
#define PatName SBuffer[2]
#define STRINGS 3

static UBYTE SBuffer[STRINGS][MAXFULL];
static UBYTE Undo[MAXFULL];

static struct StringInfo STD_String[STRINGS] = {
	{SBuffer[0],Undo,0,MAXFULL,0},
	{SBuffer[1],Undo,0,MAXFULL,0},
	{SBuffer[2],Undo,0,MAXFULL,0}
};

static struct PropInfo STD_Prop = { AUTOKNOB|FREEVERT, 0, 0, 0, 0 };

static struct IntuiText STD_OK =
	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"ACCEPT", Z };
static struct IntuiText STD_CANCEL =
	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"CANCEL", Z };
static struct IntuiText STD_VOLUME =
	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"VOLUME", Z };
static struct IntuiText STD_PARENT =
	{ FP, BP, JAM2, 0, 1, Z, (UBYTE *)"PARENT", Z };

#define BUTTONS 4
#define BUTVEC 8

static SHORT butvecs[BUTTONS][BUTVEC*2] = {
	{
		-2, HT4,
		-2, -1,
		WD3+1,-1,
		WD3+1,HT4,
		-3, HT4,
		-3,-1,
		WD3+2,-1,
		WD3+2, HT4
	},
	{
		-2, HT4,
		-2, -1,
		WD4+1,-1,
		WD4+1,HT4,
		-3, HT4,
		-3,-1,
		WD4+2,-1,
		WD4+2, HT4
	},
	{
		-2, HT4,
		-2, -1,
		WD6+1,-1,
		WD6+1,HT4,
		-3, HT4,
		-3,-1,
		WD6+2,-1,
		WD6+2, HT4
	},
	{
		-2, HT4,
		-2, -1,
		WD7+1,-1,
		WD7+1,HT4,
		-3, HT4,
		-3,-1,
		WD7+2,-1,
		WD7+2, HT4
	}
};
static struct Border ButBorder[BUTTONS] = {
	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[0], NULL},
	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[1], NULL},
	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[2], NULL},
	{0, 0, FP, BP, JAM1, BUTVEC, butvecs[3], NULL}
};
#define BB(n) &ButBorder[n]

static struct Image STD_Image;

#define DIRID 0
#define FILID 1
#define PATID 2
#define YESID 3
#define CANID 4
#define VOLID 5
#define PARID 6
#define BARID 7
#define GADGETS 8

static struct Gadget STD_Gadget[GADGETS] = {
/*NEXT, LFT, TP,WDTH, H, FLAG,  ACT, TYP, REND, Z, TXT, Z, SPEC, ID, Z */
{ G(1), IN1,TP1, WD1,HT, SF,     SA,  SG,    Z, Z,   Z, Z, SI(0), 0, 0 },
{ G(2), IN1,TP2, WD1,HT, SF|SEL, SA,  SG,    Z, Z,   Z, Z, SI(1), 1, 0 },
{ G(3), IN1,TP3, WD1,HT, SF,     SA,  SG,    Z, Z,   Z, Z, SI(2), 2, 0 },
{ G(4), IN3,TP4, WD3,HT4,BF1,    BA,  BG,(APTR)BB(0), Z, OKTEXT, Z,  Z, 3, 0 },
{ G(5), IN4,TP4, WD4,HT4,BF2,    BA,  BG,(APTR)BB(1), Z, NOTEXT, Z,  Z, 4, 0 },
{ G(6), IN6,TP4, WD6,HT4,BF1,    BA,  BG,(APTR)BB(2), Z, VLTEXT, Z,  Z, 5, 0 },
{ G(7), IN7,TP4, WD7,HT4,BF1,    BA,  BG,(APTR)BB(3), Z, PRTEXT, Z,  Z, 6, 0 },
{ NULL, IN5,TP5, WD5,HT5,PF,     PA,  PG, IMAG, Z,   Z, Z,  PROP, 7, 0 }
};

static struct NewWindow STD_NewWindow = {
	WININ, WINTP, WINWD, WINHT, -1, -1,
	REFRESHWINDOW|MOUSEBUTTONS|GADGETUP|CLOSEWINDOW,
	WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|SIMPLE_REFRESH|ACTIVATE,
	G(0), NULL, (UBYTE *)"File Name Requestor",
	NULL, NULL, 0, 0, 0, 0, CUSTOMSCREEN
};
static struct Window *STD_Window;

#define NVEC 6

static SHORT Vectors[NVEC*2] = {
	BIN+1, BTP,
	BIN+1, BTP+BHT,
	BIN+BWD, BTP+BHT,
	BIN+BWD, BTP,
	BIN, BTP,
	BIN, BTP+BHT
};

static struct Border STD_FileBox = {
	0, 0, FP, BP, JAM1, NVEC, Vectors, NULL
};

static struct IntuiText STD_Text[3] = {
	{ FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"Directory:", NULL },
	{ FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"File Name:", NULL },
	{ FP, BP, JAM2, 0, 0, NULL, (UBYTE *)"Pattern:", NULL }
};

static OpenFileWindow()
{
	extern struct IntuitionBase *IntuitionBase;
	int i;

	/* Rebuild gadget list */
	STD_NewWindow.FirstGadget = &STD_Gadget[0];
	for(i = 0; i < GADGETS; i++) {
		STD_Gadget[i].NextGadget = (i==GADGETS-1)?(0):(&STD_Gadget[i+1]);
	}
	for(i = 0; i < STRINGS; i++) {
		STD_String[i].BufferPos = strlen(SBuffer[i]);
		STD_String[i].DispPos = 0;
	}
	STD_Prop.VertBody = 0xFFFF;
	STD_Prop.VertPot = 0;
	STD_NewWindow.Screen = stdscreen;

	if(!(STD_Window = OpenWindow(&STD_NewWindow))) {
	        printf("Couldn't open file requester\n");
		return 0;
	}

	/* This  line will activate a string gadget	*/
   ActivateGadget(G(1),STD_Window,0L);

	CalcPropGadget();
	PaintFileWindow();
	return 1;
}

static CloseFileWindow()
{
	STD_NewWindow.LeftEdge = STD_Window->LeftEdge;
	STD_NewWindow.TopEdge = STD_Window->TopEdge;
	if(STD_Window)
		CloseWindow(STD_Window);
}

static int State;

#define INITIAL 0
#define DIRECTORY 1

static PaintFileWindow()
{
	DrawBorder(STD_Window->RPort, &STD_FileBox, 0, 0);
	PrintIText(STD_Window->RPort, &STD_Text[0], LEFTMAR, TP1);
	PrintIText(STD_Window->RPort, &STD_Text[1], LEFTMAR, TP2);
	PrintIText(STD_Window->RPort, &STD_Text[2], LEFTMAR, TP3);
	if(State == DIRECTORY) PrintFileNames();
}

static int FirstFile;
static int Selected;
static int NumFiles;
static struct dirent {
	struct dirent *nextfile;
	SHORT filetype;
	char *filename;
} *NameList, **NameTable;

#define FILETYPE 0
#define DIRTYPE 1
#define VOLTYPE 2

static PrintFileNames()
{
	int i;

	for(i = 0; i < MAXFILES; i++) {
		SetBPen(STD_Window->RPort, BP);
		SetAPen(STD_Window->RPort, BP);
		RectFill(STD_Window->RPort,
			HOMEX, HOMEY+i*CHSIZ,
			LASTX, HOMEY+(i+1)*CHSIZ);
		if(i+FirstFile < NumFiles)
			PrintName(i+FirstFile, i+FirstFile==Selected);
	}
}

static PrintName(file, hilite)
int file;
int hilite;
{
	int i;

	i = file - FirstFile;

	Move(STD_Window->RPort, HOMEX, HOMEY+i*CHSIZ+BASELINE);
	if(hilite == 0) {
		SetBPen(STD_Window->RPort, BP);
		if(NameTable[file]->filetype == FILETYPE)
			SetAPen(STD_Window->RPort, FP);
		else
			SetAPen(STD_Window->RPort, 3);
	} else {
		SetAPen(STD_Window->RPort, BP);
		if(NameTable[file]->filetype == FILETYPE)
			SetBPen(STD_Window->RPort, FP);
		else
			SetBPen(STD_Window->RPort, 3);
	}
	Text(STD_Window->RPort,
		NameTable[file]->filename,
		strlen(NameTable[file]->filename));
}

static CalcPropGadget()
{
	int VertPot, VertBody;

	if(State == INITIAL) return;

	if(NumFiles<=MAXFILES) {
		VertBody = 0xFFFF;
		VertPot = 0;
		FirstFile = 0;
	} else {
		VertBody = ((MAXFILES<<16)-1) / NumFiles;
		VertPot = 0;
		FirstFile = 0;
	}

	ModifyProp(&STD_Gadget[BARID], STD_Window, NULL,
		STD_Prop.Flags, 0, VertPot, 0, VertBody);
}

static CalcFilePosition()
{
	int old_pos;

	if(State == INITIAL) return;

	old_pos = FirstFile;
	if(NumFiles<=MAXFILES)
		FirstFile = 0;
	else {
		int VertPot = STD_Prop.VertPot;

		FirstFile = ((VertPot+1)*(NumFiles-MAXFILES))>>16;
	}
	if(old_pos != FirstFile)
		PrintFileNames();
}

FreeList(list)
struct dirent *list;
{
	struct dirent *ptr;

	while(list) {
		ptr = list->nextfile;
		if(list->filename) free(list->filename);
		free(list);
		list = ptr;
	}
}

static ReadNewDir()
{
	struct dirent *NewList, **NewTable, *ptr;
	int NewCount;
	struct FileInfoBlock *FIB;
	BPTR dirlock;

	if(State != DIRECTORY) {
		NameTable = 0;
		NameList = 0;
	}
	if(DirName[0])
		dirlock = Lock(DirName, ACCESS_READ);
	else {
		BPTR ram;
		ram = Lock("RAM:", ACCESS_READ);
		dirlock = CurrentDir(ram);
		CurrentDir(dirlock);
		UnLock(ram);
	}
	if(dirlock==0)
		return 0;

	/* FIB must be long word aligned, and aztec doesn't guarantee this, so: */
	if((FIB = AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC)) == 0) {
		UnLock(dirlock);
		return 0;
	}
	if(!Examine(dirlock, FIB)) {
		UnLock(dirlock);
		FreeMem(FIB, sizeof(struct FileInfoBlock));
		return 0;
	}
	if(FIB->fib_DirEntryType < 0) {
		UnLock(dirlock);
		FreeMem(FIB, sizeof(struct FileInfoBlock));
		return 0;
	}
	NewList = 0;
	NewCount = 0;
	while(ExNext(dirlock, FIB)) {
		NewCount += 1;
		ptr = (struct dirent *)malloc(sizeof(struct dirent));
		if(ptr==0) {
			FreeList(NewList);
			UnLock(dirlock);
			FreeMem(FIB, sizeof(struct FileInfoBlock));
			return 0;
		}
		ptr->nextfile = NewList;
		ptr->filetype = (FIB->fib_DirEntryType<0)?FILETYPE:DIRTYPE;
		ptr->filename = malloc(strlen(FIB->fib_FileName)+1);
		if(ptr->filename == 0) {
			FreeList(ptr);
			UnLock(dirlock);
			FreeMem(FIB, sizeof(struct FileInfoBlock));
			return 0;
		}
		strcpy(ptr->filename, FIB->fib_FileName);
		NewList = ptr;
	}
	FreeMem(FIB, sizeof(struct FileInfoBlock));
	if(DirName[0]) {
		UnLock(dirlock);
	}
	NewTable = malloc(sizeof(struct dirent *)*NewCount);
	if(NewTable==0) {
		FreeList(NewList);
		return 0;
	}
	FreeList(NameList);
	NameList = NewList;
	if(NameTable) free(NameTable);
	NameTable = NewTable;

	if(PatName[0]==0)
		SetPatName("#?");

	State = DIRECTORY;
	Selected = -1;

	ReCalcPattern();
}

static ReadVol()
{
	struct dirent *NewList, **NewTable, *ptr;
	int NewCount;
	char name[MAXNAME];

	if(State != DIRECTORY) {
		NameTable = 0;
		NameList = 0;
	}
	OpenVolList();
	NewList = 0;
	NewCount = 0;
	while(ReadVolList(name)) {
		NewCount += 1;
		ptr = (struct dirent *)malloc(sizeof(struct dirent));
		if(ptr==0) {
			FreeList(NewList);
			return 0;
		}
		ptr->nextfile = NewList;
		ptr->filetype = VOLTYPE;
		ptr->filename = malloc(strlen(name)+1);
		if(ptr->filename == 0) {
			FreeList(ptr);
			return 0;
		}
		strcpy(ptr->filename, name);
		NewList = ptr;
	}
	CloseVolList();
	NewTable = malloc(sizeof(struct dirent *)*NewCount);
	if(NewTable==0) {
		FreeList(NewList);
		return 0;
	}
	FreeList(NameList);
	NameList = NewList;
	if(NameTable) free(NameTable);
	NameTable = NewTable;

	if(PatName[0]==0)
		SetPatName("#?");

	State = DIRECTORY;
	Selected = -1;

	ReCalcPattern();
}

static WORD PatCode[128];

static patcomp()
{
	/* This is a judgement call: that no pattern should be equivalent
	   to "#?". Perhaps it should do this invisibly, by adding a
	   pointer to the real pattern name and making it PatName or "#?"
	   as appropriate. */

	if(!PatName[0])
		SetPatName("#?");
	return CmplPat(PatName, PatCode);
}

static patmatch(name)
{
	return Match(PatName, PatCode, name);
}

/* this routine does a true dictionary search:
 *
 *		Devs < devs but Devs > devices
 */
static table_compare(p1, p2)
struct dirent **p1, **p2;
{
	char *s1, *s2;
	char c1, c2;
	char firstdiff;

	s1 = (*p1)->filename;
	s2 = (*p2)->filename;
	firstdiff = 0;

	while(*s1 && *s2) {
		c1 = *s1++;
		c2 = *s2++;
		if(firstdiff==0)
			firstdiff = c1 - c2;
		if(c1>='A' && c1<='Z') c1 = c1+'@';
		if(c2>='A' && c2<='Z') c2 = c2+'@';
		if(c1 != c2)
			return c1 - c2;
	}
	return firstdiff;
}

static sort_table()
{
	qsort(NameTable, NumFiles, sizeof(struct dirent *), table_compare);
	return 1;
}

static ReCalcPattern()
{
	if(State != DIRECTORY)
		ReadNewDir();
	else {
		struct dirent *ptr;
		patcomp();

		NumFiles = 0;
		for(ptr = NameList; ptr; ptr=ptr->nextfile) {
			/* Directories always match. Is this good? */
			if(ptr->filetype == DIRTYPE ||
			   ptr->filetype == VOLTYPE ||
			   patmatch(ptr->filename)) {
				NameTable[NumFiles] = ptr;
				NumFiles++;
			}
		}
		sort_table();
		CalcPropGadget();
		Selected = -1;
		PrintFileNames();
	}
}

static SetGadgetText(id, text)
int id;
char *text;
{
	int position;

	position = RemoveGadget(STD_Window, G(id));
	if(position != -1) {
		strcpy(SBuffer[id], text);
		STD_String[id].BufferPos = strlen(text);
		position = AddGadget(STD_Window, G(id), -1);
		if(position != -1)
			RefreshGadgets(G(id), STD_Window, NULL);
	}
}

static SetDirName(name)
char *name;
{
	char buffer[MAXFULL+1], *ptr;
	int index;
	char lastchar;

	/* Can't enter a file name too long. */
	if(strlen(DirName) + strlen(name) + 1 > MAXFULL) {
		DisplayBeep();
		return 0;
	}
	index = 0;
	lastchar = 0;
	for(ptr = DirName; *ptr; ptr++)
		buffer[index++] = lastchar = *ptr;
	if(lastchar!='/' && lastchar!=':' && lastchar!=0)
		buffer[index++] = '/';
	strcpy(&buffer[index], name);
	SetGadgetText(DIRID, buffer);
	SetGadgetText(FILID, "");
	return 1;
}

static ReadParDir()
{
	int i;
	int ptr;

	ptr = -1;
	for(i = 0; DirName[i]; i++)
		if(DirName[i]==':' || DirName[i]=='/')
			ptr = i;
	if(ptr>=0) {
		SetGadgetText(FILID, &DirName[ptr+1]);
		if(ptr==0 || DirName[ptr]==':')
			ptr++;
		DirName[ptr] = 0;
		SetGadgetText(DIRID, DirName);
	} else {
		SetGadgetText(FILID, DirName);
		if(i)
			SetGadgetText(DIRID, "");
		else
			SetGadgetText(DIRID, "/");
	}
	ReadNewDir();
	return 1;
}

static SetFileName(name)
char *name;
{
	/* Can't enter a file name too long. */
	if(strlen(DirName) + strlen(name) + 1 > MAXFULL) {
		DisplayBeep();
		return 0;
	}
	SetGadgetText(FILID, name);
	return 1;
}

static SetPatName(name)
char *name;
{
	SetGadgetText(PATID, name);
}

static ProcessGadget(id)
int id;
{
	switch(id) {
		case DIRID: ReadNewDir(); break;
		case FILID: DoneFlag = 1; break;
		case PATID: ReCalcPattern(); break;
		case BARID: CalcFilePosition(); break;
		case YESID: DoneFlag = 1; break;
		case CANID: DoneFlag = -1; break;
		case VOLID: ReadVol(); break;
		case PARID: ReadParDir(); break;
	}
}

static ProcessMouse(x, y, code, seconds, micros)
int x, y, code;
{
	int NewSelected;
	static int oseconds = 0, omicros = 0;

	if(x<HOMEX || y<HOMEY || x>=LASTX || y>=LASTY)
		return;
	if((code&SELECTUP) == SELECTUP)
		return;
	if(State != DIRECTORY) {
		ReadNewDir();
		return;
	}
	NewSelected = (y-HOMEY)/CHSIZ + FirstFile;
	if(NewSelected == Selected) {
		if(Selected != -1) {
			if(DoubleClick(oseconds, omicros, seconds, micros)) {
				if(NameTable[Selected]->filetype == DIRTYPE) {
					if(SetDirName(NameTable[Selected]->filename))
						ReadNewDir();
				} else if(NameTable[Selected]->filetype == VOLTYPE) {
					SetGadgetText(DIRID, NameTable[Selected]->filename);
					SetGadgetText(FILID, "");
					ReadNewDir();
				} else {
					if(!SetFileName(NameTable[Selected]->filename))
						Selected = -1;
					DoneFlag = 1;
				}
			}
		}
	} else {
		if(Selected != -1 &&
		   Selected>=FirstFile && Selected<FirstFile+MAXFILES)
			PrintName(Selected, 0);
		Selected = NewSelected;
		if(Selected>=NumFiles)
			Selected = -1;
		else {
			if(SetFileName(NameTable[Selected]->filename))
				PrintName(Selected, 1);
			else
				Selected = -1;
		}
	}
	oseconds = seconds;
	omicros = micros;
}

stdfile(title, deffile, defpat, name)
char *title, *deffile, *defpat, *name;
{
	if(title)
		STD_NewWindow.Title = (UBYTE *)title;
	else
		STD_NewWindow.Title = (UBYTE *)"Enter File Name";
	if(deffile) {
		int i;
		for(i = strlen(deffile)-1; i>=0; i--) {
			if(deffile[i]==':' || deffile[i]=='/') {
				int hold;
				strcpy(FileName, &deffile[i+1]);
				if(deffile[i]==':')
					i++;
				hold = deffile[i];
				deffile[i] = 0;
				strcpy(DirName, deffile);
				deffile[i] = hold;
				break;
			}
		}
		if(i<0) {
			strcpy(FileName, deffile);
			DirName[0] = 0;
		}
	} else {
		DirName[0] = 0;
		FileName[0] = 0;
	}
	if(defpat)
		strcpy(PatName, defpat);
	else
		PatName[0] = 0;

	State = INITIAL;
	NameTable = 0;
	NameList = 0;

	if(OpenFileWindow()) {
		struct IntuiMessage *msg;
		DoneFlag = 0;
		while(!DoneFlag) {
			Wait(1<<STD_Window->UserPort->mp_SigBit);
			while(msg = GetMsg(STD_Window->UserPort)) {
				switch(msg->Class) {
					case CLOSEWINDOW:
						DoneFlag = -1;
						break;
					case MOUSEBUTTONS:
						ProcessMouse(msg->MouseX, msg->MouseY,
							msg->Code,
							msg->Seconds, msg->Micros);
						break;
					case GADGETUP:
						ProcessGadget(
							((struct Gadget *)msg->IAddress)->GadgetID
						);
						break;
					case REFRESHWINDOW:
						BeginRefresh(STD_Window);
						PaintFileWindow();
						EndRefresh(STD_Window, 1);
						break;
				}
				ReplyMsg(msg);
			}
		}

		CloseFileWindow();
	}
	else return 0;

	FreeList(NameList);
	if(NameTable) free(NameTable);

	if(DoneFlag==1) {
		int len;

		strcpy(name, DirName);
		if(FileName[0]) {
			if(len = strlen(name))
				if(name[len-1]!=':')
					strcat(name, "/");
			strcat(name, FileName);
			return 1;
		}

		/* Here the user has accepted the name without providing a file
		   name. I return true, but false may be more appropriate. What
		   do you think? */
		return 1;
	}
	return 0;
}
