/* INSTANT -- Instant Workbench Application   Version 0.0  5 Sep 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
 *	provided this notice remains intact in the source. The
 *	intent of this module is to provide a simple way to write
 *	a "standard" workbench application. This routine is passed
 *	an array of function names and associated routines, and puts
 *	up a list of files. Double-clicking a file name will pass
 *	it to the current routine.
 *
 *	The current routine is initially the first one in the list, and
 *	you can select a routine from the "Applications" menu. When a
 *	new routine is selected it will be called with 0 for the filename.
 *
 *	A routine should normally return 0... a non-zero return value will
 *	cause instant to close the window and terminate. If this isn't
 *	good enough... if you must blow out... call instant_cleanup()
 *	first.
 *
 *	Check all arguments to your routines. This version will pass
 *	a 0 for the lock parameter. Another version may pass a directory
 *	lock. Eventually I plan on having this sort of calling
 *	sequence:
 *
 *	application(argc, argv, functab);
 *
 *	If called from the cli (argc!=0), it will expand the filename args
 *	and call the appropriate parameter args based on the keywords in
 *	functab. If argc==0 it will look for WBArgs and pass them to the
 *	first routine in functab. If argc==0 and there are no WBArgs, it
 *	will call instant(). This way you'll end up with an AmigaDOS
 *	compatible calling sequence from the CLI, and it'll still work with
 *	shift-click and double-click from the workbench. It should also
 *	work as the tool for an application icon.
 *
 *	The index argument is to allow you to have the same function in
 *	more than one place in your table.
 *
 * 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:
 *
 *	#include "instant.h"
 *
 *	int instant(title, default_file, default_pat, table);
 *	char *title;
 *	char *default_file;
 *	char *default_pattern;
 *	struct FuncTable {
 *		char *FT_name;
 *		int (*FT_func)(BPTR, char *, int);
 *	} *table;
 *
 *	INSTANT puts up a file requestor (actually, it's a plain window)
 *	in the workbench screen.
 *
 *	The requestor looks like this (allowing for the limitations of
 *	text):
 *
 *	+-----------------------------------+
 *	|o| Title ------------------- |  |  | Currently selected function.
 *	|-----------------------------------|
 *	| 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.
 *	| |                            |  | |
 *	| +-------------------------------+ |
 *	+-----------------------------------+
 *
 *	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.
 *
 * Example:
 *
 *	pepperoni(lock, file, index)
 *	BPTR lock;
 *	char *file;
 *	int index;
 *	{
 *		return 0;
 *	}
 *	...
 *
 *	struct FuncTable *ft = {
 *		"Pepperoni", pepperoni,
 *		"Black Olive", blackolive,
 *		"Mozzarella", mozzarella,
 *		0, 0
 *	};
 *	
 *	instant("Pizza Processor", 0, "#?.pizza", ft);
 */
char *Copyright =
"Instant Application V0.0. (c) 1987 Peter da Silva. All rights reserved.";
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <exec/memory.h>
#include "instant.h"
#include "menu.h"

char *malloc(), *AllocMem();
struct Window *OpenWindow();

#define MAXFILES 8

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

/* GADGET BORDERS */
#define IN1 LEFTMAR+10*CHSIZ
#define IN3 LEFTMAR+3
#define IN5 -(INDENT+CHSIZ*2)
#define WD1 -(INDENT+IN1)
#define WD5 (CHSIZ*2+2)
#define TP1 (CHSIZ+BORDER)
#define TP2 (TP1+HT+1)
#define TP3 (TP2+HT+1)
#define TP5 (TP3+HT+BORDER)
#define HT5 (CHSIZ*MAXFILES+BORDER*2)

#define WINHT (TP5 + HT5 + BORDER-1)
#define WINWD (INDENT*4 + (MAXNAME+2)*CHSIZ)
#define WININ (640-WINWD)/2
#define WINTP (200-WINHT)/2

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

#define SF GADGHCOMP|GRELWIDTH
#define SEL SELECTED
#define PF GRELRIGHT

#define SA RELVERIFY
#define CEN STRINGCENTER
#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 PG PROPGADGET

#define FP AUTOBACKPEN
#define BP AUTOFRONTPEN

static int DoneFlag;
static int (*DefaultFunction)();
static int DefaultIndex;

#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 Image STD_Image;

#define DIRID 0
#define FILID 1
#define PATID 2
#define BARID 3
#define GADGETS 4

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 },
{ NULL, IN5,TP5, WD5,HT5,PF,     PA,  PG, IMAG, Z,   Z, Z,  PROP, 3, 0 }
};

#define INSTANTMENU 0
#define VOLITEM 0
#define PARITEM 1
#define CLOSITEM 2
#define APPLICMENU 1

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

#define NVEC 6

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

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 }
};

struct MenuPtr menuptr[1];

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;

	if(!(STD_Window = OpenWindow(&STD_NewWindow))) {
		return 0;
	}

	/* This optional line will activate a string gadget	*/
	if ( IntuitionBase->LibNode.lib_Version > 32 )
	{
		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 = (struct dirent *)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 = (struct dirent *)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)
UBYTE *name;
{
	UBYTE buffer[MAXFULL+1], *ptr;
	int index;
	UBYTE 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: break;
		case PATID: ReCalcPattern(); break;
		case BARID: CalcFilePosition(); break;
	}
}

SetApplication(functable, number)
struct FuncTable *functable;
int number;
{
	SetWindowTitles(STD_Window, functable[number].FT_name, -1);
	DefaultFunction = functable[number].FT_func;
	DefaultIndex = number;
	DoneFlag = (*DefaultFunction)(0, 0, number);
}

static ProcessMenu(functable, menunumber)
struct FuncTable *functable;
int menunumber;
{
	struct MenuItem *ItemAddress();

	while(menunumber != MENUNULL && DoneFlag==0) {
		switch(MENUNUM(menunumber)) {
			case INSTANTMENU:
				switch(ITEMNUM(menunumber)) {
					case VOLITEM:
						ReadVol(); break;
						break;
					case PARITEM:
						ReadParDir(); break;
						break;
					case CLOSITEM:
						DoneFlag = -1; break;
						break;
					default:
						break;
				}
				break;
			case APPLICMENU:
				SetApplication(functable, ITEMNUM(menunumber));
				break;
			default:
				break;
		}
		menunumber = ItemAddress(menuptr[0].MenuBar, menunumber)->NextSelect;
	}
}

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(Selected != -1 &&
					   Selected>=FirstFile && Selected<FirstFile+MAXFILES)
						PrintName(Selected, 0);
					SetFileName(NameTable[Selected]->filename);
					Selected = -1;
					if(DefaultFunction) {
						char buffer[MAXFILENAME];

						ExtractFileName(buffer);
						DoneFlag = (*DefaultFunction)(0, buffer, DefaultIndex);
					}
				}
			}
		}
	} 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;
}

CreateMenuBar(functable)
struct FuncTable *functable;
{
	int i;

	init_menus(menuptr);
	if(add_item(menuptr, "Instant", "Volumes", 0) == MENUNULL ||
	   add_item(menuptr, "Instant", "Parent", 0) == MENUNULL ||
	   add_item(menuptr, "Instant", "Close", 0) == MENUNULL) {
		trash_menu(menuptr);
		return 0;
	}
	for(i = 0; functable[i].FT_name; i++)
		if(add_item(menuptr, "Application",
			functable[i].FT_name, 0) == MENUNULL
		  ) {
			trash_menu(menuptr);
			return 0;
		}
}

ExtractFileName(name)
char *name;
{
	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;
}

instant(title, deffile, defpat, functable)
char *title;
char *deffile, *defpat;
struct FuncTable *functable;
{
	struct IntuiMsg *GetMsg();

	if(!CreateMenuBar(functable))
		return 0;
	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;

		SetApplication(functable, 0);
		SetMenuStrip(STD_Window, menuptr[0].MenuBar);
		SetWindowTitles(STD_Window, -1, title?title:"Instant Application");

		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 MENUPICK:
						ProcessMenu(functable, msg->Code);
						break;
					case REFRESHWINDOW:
						BeginRefresh(STD_Window);
						PaintFileWindow();
						EndRefresh(STD_Window, 1);
						break;
				}
				ReplyMsg(msg);
			}
		}

		ClearMenuStrip(STD_Window);
		CloseFileWindow();
	} else
		return 0;

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

	if(DoneFlag==1)
		return 1;
	else
		return 0;
}

instant_cleanup()	/* Call this routine if you must blow out. */
{
	FreeList(NameList);
	if(NameTable) free(NameTable);
	ClearMenuStrip(STD_Window);
	CloseFileWindow();
	trash_menu(menuptr);
}
