/****************************************************************************
r.c

This file contains the source code for a function called "get_fname" which
displays a typical requester for a file name.  (Several other supporting
functions are included, as well.)  It is intended as a direct replacement
for C. Heath's file requester by the same name.  Simply link your programs
with "r.o", "requester.o", and "requester2.o" instead of C. Heath's files.

The files in this archive are completely in the public domain, so you may
use them as you wish.  I would like to request, however, that you avoid
changing the files in the archive when redistributing.

This is the only file of the three which actually contains program
instructions.  "requester" contains all of the structures required to
make the main file requester window, and "requester2" contains the structures
required to make the error window.  They are the EXACT output produced by
"StrucGen", and you can use "StrucGen" to change them to a certain extent.
See GEnie for a demo file of "StrucGen."

Rick

Please excuse the condition of the following code.  It explains why I usually
do not release source code...
****************************************************************************/

#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <exec/memory.h>
#include <functions.h>

/* I'm not big on #define's */
#define GadgetList requester_scroll

/* A lot of external definitions for the structures in "requester" and
"requester2".  This is actually more than I need, but it was simpler to
copy them all from the previously mentioned files.  Incidently, I also copied
the #define from "requester". */
extern struct IntuitionBase *IntuitionBase;
extern struct NewWindow requester2_NewWindow;
extern struct IntuiText requester2_text;
extern struct NewWindow requester_NewWindow;
extern struct IntuiText requester_text0;
extern struct IntuiText requester_text1;
extern struct IntuiText requester_text2;
extern struct IntuiText requester_text3;
extern struct IntuiText requester_text4;
extern struct IntuiText requester_text5;
extern struct IntuiText requester_text6;
extern struct IntuiText requester_text7;
extern struct IntuiText requester_text8;
extern struct IntuiText requester_text9;
extern struct IntuiText requester_files;
extern struct IntuiText requester_text11;
extern struct IntuiText requester_text12;
extern struct IntuiText requester_text13;
extern struct IntuiText requester_text14;
extern struct IntuiText requester_text15;
extern struct IntuiText requester_text16;
extern struct IntuiText requester_text17;
extern struct Image requester_disk;
extern struct Image requester_image6;
extern struct Image requester_image7;
extern struct Image requester_image8;
extern struct Image requester_image10;
extern struct Image requester_image11;
extern struct Image requester_image19;
extern struct Image requester_image20;
extern struct Image requester_image21;
extern struct Image requester_image22;
extern struct Image requester_image23;
extern struct Image requester_image24;
extern struct Image requester_image25;
extern struct Image requester_image26;
extern struct Image requester_image27;
extern struct Image requester_image28;
extern struct Image requester_image29;
extern struct Image requester_image30;
extern struct Image requester_image32;
extern struct Gadget requester_gadget0;
extern struct Gadget requester_gadget1;
extern struct Gadget requester_gadget2;
extern struct Gadget requester_gadget3;
extern struct Gadget requester_gadget4;
extern struct Gadget requester_gadget5;
extern struct Gadget requester_path;
extern struct Gadget requester_file;
extern struct Gadget requester_gadget8;
extern struct Gadget requester_gadget9;
extern struct Gadget requester_gadget10;
extern struct Gadget requester_gadget11;
extern struct Gadget requester_gadget12;
extern struct Gadget requester_gadget13;
extern struct Gadget requester_gadget14;
extern struct Gadget requester_miniwindow;
extern struct Gadget requester_gadget16;
extern struct Gadget requester_gadget17;
extern struct Gadget requester_gadget18;
extern struct Gadget requester_gadget19;
extern struct Gadget requester_scroll;
extern struct StringInfo requester_pathi;
extern struct StringInfo requester_filei;
extern struct PropInfo requester_propi;

/* A couple of global variables were necessary.  Sorry. */
char xxfname[200];
char xxnull = 0;
char *strchr();

/*
Function: get_fname(Window,Screen,title,file_name,dir)
	struct Window *Window;	Calling program's window
	struct Screen *Screen;	Calling program's screen
	char *title;			Title for the requester window
	char *file_name;		Initial file name
	char *dir;				Initial directory

These parameters are EXACTLY the same as C. Heath's function by the same
name.  It was designed to be a direct replacement.  The returned value
may not be the same, however.  Mine returns a NULL pointer if the window
is closed, or CANCEL is clicked.  Otherwise it returns a pointer to
a string which is the path and file name selected.
*/
char * get_fname(Window,Screen,title,file_name,dir)
	struct Window *Window;
	struct Screen *Screen;
	char *title;
	char *file_name;
	char *dir;
	{
	struct Window *w;
	struct IntuiMessage *IntuiMessage;
	struct Gadget *g;
	short id;
	char buffer[200];
	short index;
	char *files[200];
	short filecount = 0;
	short line = 0;
	short sentinal = 0;
	ULONG oldPot = 0;
	char *parenthesis;

/* Custom Screen */
	if(Screen)
		{
		requester_NewWindow.Type = CUSTOMSCREEN;
		requester_NewWindow.Screen = Screen;
		}
/* Initialize string gadgets */
	requester_pathi.Buffer = (UBYTE *) dir;
	requester_pathi.UndoBuffer = (UBYTE *) buffer;
	requester_filei.Buffer = (UBYTE *) file_name;
	requester_filei.UndoBuffer = (UBYTE *) buffer;
/* Open window and add imagery */
	w = OpenWindow(&requester_NewWindow);
	SetWindowTitles(w,title,NULL);
	AddGList(w,&GadgetList,-1L,-1L,NULL);

/* read directory */
	xxreaddir(dir,files,&filecount,&line,w,&sentinal);
	oldPot = requester_propi.VertPot;

/* the rest is pretty much like the "xxreaddir" function, except that
the directory has already been read, so the IO routines are deleted. */
	while(! sentinal)
		{
		if(IntuiMessage = (struct IntuiMessage *) GetMsg(w->UserPort))
			{
			if(IntuiMessage->Class == CLOSEWINDOW)
				{
				ReplyMsg(IntuiMessage);
				sentinal = -1;
				}
			else if(IntuiMessage->Class == GADGETDOWN)
				{
				g = (struct Gadget *) IntuiMessage->IAddress;
				ReplyMsg(IntuiMessage);
				id = g->GadgetID;
				if(id == -2)
					sentinal = -1;
				else if(id == -3)
					sentinal = 1;
				else if(id > 0)
					{
					id += line - 1;
					if(parenthesis = strchr(files[id],')'))
						{
						*parenthesis = 0;
						strcat(requester_pathi.Buffer,&files[id][1]);
						strcat(requester_pathi.Buffer,"/");
						xxreaddir(dir,files,&filecount,&line,w,
							&sentinal);
						}
					else
						{
						strcpy(requester_filei.Buffer,files[id]);
						RefreshGList(&requester_file,w,NULL,1L);
						}
					}
				else if(id == -1)
					{
					strcpy(requester_pathi.Buffer,g->GadgetText->IText);
					xxreaddir(dir,files,&filecount,&line,w,
						&sentinal);
					}
				else if(id == -5)
					while((line > 0) && (g->Flags & SELECTED))
						{
						line--;
						xxrefresh(files,filecount,line,w);
						oldPot = line;
						oldPot = (oldPot << 16) / (filecount - 8);
						requester_propi.VertPot = oldPot;
						RefreshGList(&requester_scroll,w,NULL,1L);
						}
				else if(id == -6)
					while((line < filecount - 8) && (g->Flags & SELECTED))
						{
						line++;
						xxrefresh(files,filecount,line,w);
						oldPot = line;
						oldPot = (oldPot << 16) / (filecount - 8) - 1;
						requester_propi.VertPot = oldPot;
						RefreshGList(&requester_scroll,w,NULL,1L);
						}
				}
			else if(IntuiMessage->Class == GADGETUP)
				{
				g = (struct Gadget *) IntuiMessage->IAddress;
				ReplyMsg(IntuiMessage);
				id = g->GadgetID;
				if(id == -4)
					xxreaddir(dir,files,&filecount,&line,w,
						&sentinal);
				}
			else
				ReplyMsg(IntuiMessage);
			}
		while((requester_propi.VertPot != oldPot) && (filecount > 8))
			{
			oldPot = requester_propi.VertPot;
			oldPot++;
			line = ((ULONG) oldPot * (filecount - 8)) >> 16;
			xxrefresh(files,filecount,line,w);
			oldPot--;
			}
		}
	xxcleanup(files,&filecount);
	CloseWindow(w);
	if(sentinal < 0)
		return(NULL);
	else
		{
		strcpy(xxfname,dir);
		strcat(xxfname,file_name);
		return(xxfname);
		}
	}
/*
Function: xxreaddir(dir,files,filecount,line,w,sentinal)
	char *dir;			Directory name
	char *files[200];	List of files in directory
	short *filecount;	Number of files in directory
	short *line;		Top line displayed in files[];
	struct Window *w;	Requester window
	short *sentinal;	Sentinal Flag (1 = Load,0 = Nothing,-1 = Cancel)
*/
xxreaddir(dir,files,filecount,line,w,sentinal)
	char *dir;
	char *files[200];
	short *filecount;
	short *line;
	struct Window *w;
	short *sentinal;
	{
	struct IntuiMessage *IntuiMessage;
	struct FileInfoBlock *FileInfoBlock;
	struct FileLock *lock;
	struct Gadget *g;
	short id;
	short index;
	short index2;
	ULONG oldPot = 0;
	short newline;
	char temp[50];
	char *parenthesis;

/* Excuse the goto's.  Ocasionally the make things too simple to omit */
reread:
/* Delete any entries in files[] */
	xxcleanup(files,filecount);
/* Initialize gadgets and imagery */
	*line = 0;
	oldPot = requester_propi.VertPot = 0;
	requester_propi.VertBody = 65535;
	FileInfoBlock = (struct FileInfoBlock *) AllocMem((long)
		sizeof(struct FileInfoBlock),MEMF_PUBLIC);
	RefreshGList(&GadgetList,w,NULL,-1L);
/* Make lock and FileInfoBlock.  Give an error, otherwise */
	if(! (lock = Lock(dir,ACCESS_READ)))
		{
		xxerror();
		goto leave;
		}
	if(! Examine(lock,FileInfoBlock))
		{
		xxerror();
		goto leave;
		}
/* Keep going while there are still directory entries, there aren't too
many files, and the sentinal hasn't been triggered */
	while(ExNext(lock,FileInfoBlock) && (*filecount < 200) && (! *sentinal))
		{
/* Copy newest directory entry into files[] */
		files[*filecount] = (char *) AllocMem(31L,MEMF_PUBLIC);
		if(FileInfoBlock->fib_DirEntryType < 0)
			sprintf(files[*filecount],"%-30s",FileInfoBlock->fib_FileName);
		else
			{
			sprintf(temp,"(%s)",FileInfoBlock->fib_FileName);
			sprintf(files[*filecount],"%-30s",temp);
			}
/* Alphabetize */
		for(index = 0;strcmp(files[*filecount],files[index]) > 0;++index);
		for(index2 = *filecount;index2 >= index;--index2)
			files[index2 + 1] = files[index2];
		++(*filecount);
		files[index] = files[*filecount];
		files[*filecount] = &xxnull;
/* Redisplay if necessary */
		if((index >= *line) && (index < *line + 8))
			xxrefresh(files,*filecount,*line,w);
/* Adjust scroll bar size */
		if(*filecount > 8)
			{
			requester_propi.VertBody = (65535 / *filecount) * 8;
			RefreshGList(&requester_scroll,w,NULL,1L);
			}
/* Check on messages */
		if(IntuiMessage = (struct IntuiMessage *) GetMsg(w->UserPort))
			{
/* Close window clicked? */
			if(IntuiMessage->Class == CLOSEWINDOW)
				{
				ReplyMsg(IntuiMessage);
				*sentinal = -1;
				}
/* Gadget clicked? */
			else if(IntuiMessage->Class == GADGETDOWN)
				{
/* Extract GadgetID */
				g = (struct Gadget *) IntuiMessage->IAddress;
				ReplyMsg(IntuiMessage);
				id = g->GadgetID;
/* Cancel Gadget */
				if(id == -2)
					*sentinal = -1;
/* Load Gadget */
				else if(id == -3)
					*sentinal = 1;
/* A file or directory name was clicked on */
				else if(id > 0)
					{
					id += *line - 1;
					if(parenthesis = strchr(files[id],')'))
						{
						*parenthesis = 0;
						strcat(requester_pathi.Buffer,&files[id][1]);
						strcat(requester_pathi.Buffer,"/");
						UnLock(lock);
						FreeMem(FileInfoBlock,(long) 
							sizeof(struct FileInfoBlock));
						goto reread;
						}
					else
						{
						strcpy(requester_filei.Buffer,files[id]);
						RefreshGList(&requester_file,w,NULL,1L);
						}
					}
/* A device name was clicked on */
				else if(id == -1)
					{
					strcpy(requester_pathi.Buffer,g->GadgetText->IText);
					UnLock(lock);
					FreeMem(FileInfoBlock,(long) 
						sizeof(struct FileInfoBlock));
					goto reread;
					}
/* Up scroll */
				else if(id == -5)
					while((*line > 0) && (g->Flags & SELECTED))
						{
						(*line)--;
						xxrefresh(files,*filecount,*line,w);
						oldPot = *line;
						oldPot = (oldPot << 16) / ((*filecount) - 8);
						requester_propi.VertPot = oldPot;
						RefreshGList(&requester_scroll,w,NULL,1L);
						}
/* Down scroll */
				else if(id == -6)
					while((*line < (*filecount) -8) && (g->Flags & SELECTED))
						{
						(*line)++;
						xxrefresh(files,*filecount,*line,w);
						oldPot = *line;
						oldPot = (oldPot << 16) / ((*filecount) - 8) - 1;
						requester_propi.VertPot = oldPot;
						RefreshGList(&requester_scroll,w,NULL,1L);
						}
				}
/* New directory typed in? */
			else if(IntuiMessage->Class == GADGETUP)
				{
				g = (struct Gadget *) IntuiMessage->IAddress;
				ReplyMsg(IntuiMessage);
				id = g->GadgetID;
				if(id == -4)
					{
					UnLock(lock);
					FreeMem(FileInfoBlock,(long) 
						sizeof(struct FileInfoBlock));
					goto reread;
					}
				}
			else
				ReplyMsg(IntuiMessage);
			}
/* Adjust file name view if necessary.  (Scroll bar moved, etc.) */
		oldPot = requester_propi.VertPot;
		oldPot++;
		newline = ((ULONG) oldPot * ((*filecount) - 8)) >> 16;
		if((newline != *line) && (*filecount > 8))
			{
			*line = newline;
			xxrefresh(files,*filecount,*line,w);
			}
		}
/* Clean up and return */
leave:
	UnLock(lock);
	FreeMem(FileInfoBlock,(long) sizeof(struct FileInfoBlock));
	}

/*
Function: xxcleanup(files,filecount)
	char *files[200];	list of files
	short *filecount;	number of files

This function deletes any entries in files[], and initializes some of the 
file strings to be null strings.
*/
xxcleanup(files,filecount)
	char *files[200];
	short *filecount;
	{
	short index;

	for(index = 0;index < *filecount;++index)
		FreeMem(files[index],31L);
	for(index = 0;index < 10;++index)
		files[index] = &xxnull;
	*filecount = 0;
	}

/*
Function: xxrefresh(files,filecount,line,w)
	char *files[200];	Same as always
	short filecount;	Dido
	short line;			Top line of view
	struct Window *w;	Requester window
This function recalculates the file names and displays them.
*/
xxrefresh(files,filecount,line,w)
	char *files[200];
	short filecount;
	short line;
	struct Window *w;
	{
	short index;

	index = line;
	requester_files.IText = (UBYTE *) files[index];
	++index;
	requester_text11.IText = (UBYTE *) files[index];
	++index;
	requester_text12.IText = (UBYTE *) files[index];
	++index;
	requester_text13.IText = (UBYTE *) files[index];
	++index;
	requester_text14.IText = (UBYTE *) files[index];
	++index;
	requester_text15.IText = (UBYTE *) files[index];
	++index;
	requester_text16.IText = (UBYTE *) files[index];
	++index;
	requester_text17.IText = (UBYTE *) files[index];
	PrintIText(w->RPort,&requester_files,NULL,NULL);
	}

/*
Function xxerror()
This is called then we are having difficulty with disk IO.
*/
xxerror()
	{
	struct Window *w;
	struct IntuiMessage *IntuiMessage;

	w = OpenWindow(&requester2_NewWindow);
	PrintIText(w->RPort,&requester2_text,NULL,NULL);
	WaitPort(w->UserPort);
	IntuiMessage = (struct IntuiMessage *) GetMsg(w->UserPort);
	ReplyMsg(IntuiMessage);
	CloseWindow(w);
	}
