/********************************************/
/*   ShowFont 3.3 - by Arthur Johnson Jr.   */
/* ======================================== */
/* Usage: ShowFont [font_name] [font_size]  */
/* ======================================== */
/*	Last modifications - 02/11/89	    */
/********************************************/

#include "intuition/intuition.h" /* this one includes heaps o' stuff */
#include "exec/memory.h"
#include "libraries/diskfont.h"

#define XAREA	    20	    /* no characters beyond (screenwidth - XAREA) */
#define FONTBUFFER  5000    /* hopefully enough for most people */
#define FONTNAMELEN 30	    /* maximum font name length */
#define MAXFLAGS    9	    /* total of nine style flags so far */

#define MAXDISPLAY  7	    /* max names, sizes, and styles to display */
#define NAMELEN     13	    /* maximum chars of name displayed */
#define NAMEXPOS    13
#define SIZEXPOS    145
#define STYLXPOS    197
#define TOPYPOS     7

#define BASELINE    6	/* for Topaz-8 font */
#define YINCR	    8

/* these are short little macro expanders to save me some typing effort */
#define SIZES	    fonts[namesel].sizes
#define SIZESEL     fonts[namesel].sizesel
#define STYLES	    fonts[namesel].size[SIZESEL].styles
#define STYLE	    fonts[namesel].size[SIZESEL].style
#define STYLESEL    fonts[namesel].size[SIZESEL].stylesel

extern struct NewScreen newscreen;
extern struct Gadget W_Prop;
extern struct Menu Menu1;
extern struct NewWindow newwindow;
extern struct Requester requester1;
extern struct Requester requester2;
extern struct Requester RequesterStructure4;
extern struct IntuiText IText19;
extern struct PropInfo W_PropSInfo;
extern struct PropInfo R_FontSInfo;
extern struct PropInfo R_SizeSInfo;
extern struct PropInfo R_StyleSInfo;
extern struct Gadget R_Font;
extern struct Gadget R_Size;
extern struct Gadget R_Style;
extern struct Border Outline1;

struct Screen *screen;
struct Window *window;
struct RastPort *rp;
struct Gadget *whichgad;
struct IntuiMessage *message;

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct DiskfontBase *DiskfontBase;

struct sizenode {
    UWORD ysize;
    int   styles;
    UBYTE style[MAXFLAGS];
    UBYTE flags;
    int   stylesel;
};

struct tempsizenode {
    UWORD ysize;
    int   styles;
    UBYTE style[MAXFLAGS];
    UBYTE flags;
    struct tempsizenode *prev;
    struct tempsizenode *next;
};

struct fontinfo {
    char    name[FONTNAMELEN + 1];
    int     sizes;
    struct  sizenode *size;
    int     sizesel;
};
struct fontinfo *fonts;

struct tempfontinfo {
    char    name[FONTNAMELEN + 1];
    int     sizes;
    struct  tempsizenode *size;
    struct  tempfontinfo *prev;
    struct  tempfontinfo *next;
};
struct tempfontinfo *tempfonts;

char fontname[FONTNAMELEN + 6]; /* include space for '.font\0' */
struct TextAttr myfont = { /* necessary structure for fonts */
    &fontname[0],   /* default is Topaz-8 fault */
    8,
    FS_NORMAL,
    FPF_ROMFONT };
struct TextFont *font;

struct {
    UBYTE *string;
    int length;
} fontline[256];    /* maximum of 256 lines (one char/line) */

/* I just don't want to pass the following values around */

int numfonts;

int screenwidth, screenheight;

int nameline, sizeline, styleline,
    namesel,
    rfontlastline, rsizelastline, rstylelastline;

USHORT fontpropsize, fontproppos, sizepropsize, sizeproppos,
	stylepropsize, styleproppos;

UBYTE qualifiers[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		       0, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0,
		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 2, 2,
		       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
		       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2,
		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 5,
		       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
		       3, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3, 3, 3, 3,
		       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 4,
		       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
		       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4,
		       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
		       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3 };

char whichkey[] = { '?', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '?', '?', '?', '?', '?',
		    's', '1', '\'', '3', '4', '5', '7', '\'',
		    '9', '0', '8', '=', ',', '-', '.', '/',
		    '0', '1', '2', '3', '4', '5', '6', '7',
		    '8', '9', ';', ';', ',', '=', '.', '/',
		    '2', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '[', '\\', ']', '6', '-',
		    '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '[', '\\', ']', '`', '?',
		    '?', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '?', '?', '?', '?', '?',
		    's', '1', '\'', '3', '4', '5', '7', '\'',
		    '9', '0', '8', '=', ',', '-', '.', '/',
		    '0', '1', '2', '3', '4', '5', '6', '7',
		    '8', '9', ';', ';', '-', '=', '.', '/',
		    '2', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '[', '\\', ']', '6', '-',
		    '\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
		    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
		    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
		    'X', 'Y', 'Z', '[', '\\', ']', '\'', 'm' };

void clearfonts()
{

    register i;

    for (i = 0; i < numfonts; i++)
	FreeMem(fonts[i].size, fonts[i].sizes * sizeof(struct sizenode));
    FreeMem(fonts, numfonts * sizeof(struct fontinfo));

    numfonts = 0;   /* there are no fonts */

}

void clearfontlines()
{

    register i = 0;

    while (fontline[i].length != 0) {
	FreeMem(fontline[i].string, fontline[i].length);
	fontline[i].length = 0;
	++i;
    }

}

void cleanup(text)
char *text;
{

    clearfontlines();
    clearfonts();
    if (font)
	CloseFont(font);
    if (window) {
	ClearMenuStrip(window);
	CloseWindow(window);
    }
    if (screen)
	CloseScreen(screen);
    if (DiskfontBase)
	CloseLibrary(DiskfontBase);
    if (IntuitionBase)
	CloseLibrary(IntuitionBase);
    if (GfxBase)
	CloseLibrary(GfxBase);

    if (text)
	puts(text);

    exit(0);

}

void cleartempsize(font)
struct tempfontinfo *font;
{

    struct tempsizenode *erase;

    while (font->size != NULL) {
	erase = font->size;
	font->size = font->size->next;
	FreeMem(erase, sizeof(struct tempsizenode));
    }

}

void cleartempfonts()
{

    struct tempfontinfo *erase;

    while (tempfonts != NULL) {
	erase = tempfonts;
	tempfonts = tempfonts->next;
	cleartempsize(erase);
	FreeMem(erase, sizeof(struct tempfontinfo));
    }

}

int stylenum(style)
UBYTE style;
{

    if (style == FS_NORMAL)
	return(0);
    if (style & FSF_UNDERLINED)
	return(1);
    if (style & FSF_BOLD)
	return(2);
    if (style & FSF_ITALIC)
	return(4);
    if (style & FSF_EXTENDED)
	return(8);

}

void addsizenode(font, ysize, style, flags)
struct tempfontinfo *font;
UWORD ysize;
UBYTE style;
UBYTE flags;
{

    struct tempsizenode *search, *prev, *newnode;

    int foundit = FALSE;

    prev = NULL;
    search = font->size;
    while (search != NULL) {
	if ((search->ysize == ysize) && (search->flags == flags)) {
	    search->style[search->styles] = stylenum(style);
	    ++(search->styles);
	    foundit = TRUE;
	    break;
	}
	else {
	    if (search->ysize >= ysize) /* should go before this size */
		break;
	    else {
		prev = search;
		search = search->next;
	    }
	}
    }
    if (!foundit) {
	newnode = (struct tempsizenode *)AllocMem(sizeof(struct tempsizenode), MEMF_CLEAR);
	if (newnode == NULL)
	    cleanup("ShowFont: couldn't allocate 'tempsizenode' memory!");
	newnode->ysize = ysize;
	newnode->styles = 0;
	newnode->flags = flags;
	newnode->style[newnode->styles] = stylenum(style);
	++(newnode->styles);
	++(font->sizes);
	if (font->size == NULL) {   /* list is empty */
	    newnode->prev = NULL;
	    newnode->next = NULL;
	    font->size = newnode;
	}
	else {
	    if (search == NULL) {   /* add at end o' list */
		prev->next = newnode;
		newnode->prev = prev;
		newnode->next = NULL;
	    }
	    else {
		if (search->prev == NULL) { /* add at beginning o' list */
		    newnode->prev = NULL;
		    newnode->next = search;
		    search->prev = newnode;
		    font->size = newnode;
		}
		else {	/* add in the middle o' the list */
		    search->prev->next = newnode;
		    newnode->prev = search->prev;
		    newnode->next = search;
		    search->prev = newnode;
		}
	    }
	}
    }

}

void readfonts()
{

    struct AvailFontsHeader *afh;
    struct AvailFonts *af;

    struct tempfontinfo *search, *prev, *newnode;
    struct tempsizenode *sizenode;

    char searchname[FONTNAMELEN + 6];

    int foundit;

    int mem = FONTBUFFER,
	moremem;

    register i, j, k;

    clearfonts();

    do {
	afh = (struct AvailFontsHeader *)AllocMem(mem, MEMF_CLEAR);
	if (afh == NULL)
	    cleanup("ShowFont: couldn't allocate 'AvailFontsHeader' memory!");
	moremem = AvailFonts(afh, mem, 0xFF);
	if (moremem != 0) {
	    FreeMem(afh, mem);
	    mem += moremem;
	    printf("ShowFont: allocating %d bytes of memory for the FONTS: info.\n", mem);
	}
    } while (moremem != 0);

    if (afh->afh_NumEntries == 0) {
	FreeMem(afh, mem);
	cleanup("ShowFont: couldn't find any fonts! (Is FONTS: set correctly?)");
    }

    tempfonts = NULL;

    af = (struct AvailFonts *)&afh[1];
    for (i = 0; i < afh->afh_NumEntries; i++) {
	if (!((af->af_Attr.ta_Flags & FPF_REMOVED) ||
	    (af->af_Attr.ta_Flags & FPF_REVPATH) ||
	    ((af->af_Type & AFF_MEMORY) &&
	     (af->af_Attr.ta_Flags & FPF_DISKFONT)))) {

	    prev = NULL;
	    search = tempfonts;
	    foundit = FALSE;
	    while (search != NULL) {
		strcpy(searchname, search->name);
		strcat(searchname, ".font");
		if (stricmp(af->af_Attr.ta_Name, searchname) == NULL) {
		    addsizenode(search, af->af_Attr.ta_YSize, af->af_Attr.ta_Style, af->af_Attr.ta_Flags);
		    foundit = TRUE;
		    break;
		}
		else {
		    if (stricmp(af->af_Attr.ta_Name, searchname) < NULL)
			break;	/* should go before here */
		    else {
			prev = search;
			search = search->next;
		    }
		}
	    }
	    if (!foundit) {
		newnode = (struct tempfontinfo *)AllocMem(sizeof(struct tempfontinfo), MEMF_CLEAR);
		if (newnode == NULL)
		    cleanup("ShowFont: couldn't allocate 'tempfontinfo' memory!");
		stccpy(newnode->name, af->af_Attr.ta_Name, (strlen(af->af_Attr.ta_Name) - 4));
		newnode->sizes = 0;
		newnode->size = NULL;
		addsizenode(newnode, af->af_Attr.ta_YSize, af->af_Attr.ta_Style, af->af_Attr.ta_Flags);
		++numfonts;
		if (tempfonts == NULL) { /* list is empty */
		    newnode->prev = NULL;
		    newnode->next = NULL;
		    tempfonts = newnode;
		}
		else {
		    if (search == NULL) {   /* should add at end */
			prev->next = newnode;
			newnode->prev = prev;
			newnode->next = NULL;
		    }
		    else {
			if (search->prev == NULL) { /* add at beginning */
			    newnode->prev = NULL;
			    newnode->next = search;
			    search->prev = newnode;
			    tempfonts = newnode;
			}
			else { /* add in the middle o' the list */
			    search->prev->next = newnode;
			    newnode->prev = search->prev;
			    newnode->next = search;
			    search->prev = newnode;
			}
		    }
		}
	    }
	}
	af++;
    }

    FreeMem(afh, mem);

    if ((fonts = (struct fontinfo *)AllocMem(numfonts * sizeof(struct fontinfo), MEMF_CLEAR)) == NULL)
	cleanup("ShowFont: couldn't allocate 'fontinfo' memory!");

    search = tempfonts;

    for (i = 0; i < numfonts; i++) {
	strcpy(fonts[i].name, search->name);
	fonts[i].sizes = search->sizes;
	if ((fonts[i].size = (struct sizenode *)AllocMem(fonts[i].sizes * sizeof(struct sizenode), MEMF_CLEAR)) == NULL)
	    cleanup("ShowFont: couldn't allocate 'sizenode' memory!");
	sizenode = search->size;
	for (j = 0; j < fonts[i].sizes; j++) {
	    fonts[i].size[j].ysize = sizenode->ysize;
	    fonts[i].size[j].styles = sizenode->styles;
	    for (k = 0; k < fonts[i].size[j].styles; k++)
		fonts[i].size[j].style[k] = sizenode->style[k];
	    fonts[i].size[j].stylesel = 0;
	    fonts[i].size[j].flags = sizenode->flags;
	    sizenode = sizenode->next;
	}
	fonts[i].sizesel = 0;
	search = search->next;
    }

    cleartempfonts();

}

void setupscreen()
{

    if (window) {
	ClearMenuStrip(window);
	CloseWindow(window);
    }
    if (screen)
	CloseScreen(screen);

    newscreen.Width = screenwidth;
    newscreen.Height = screenheight;
    if (screenwidth == 320)
	if (screenheight == 200)
	    newscreen.ViewModes = NULL;
	else
	    newscreen.ViewModes = LACE;
    else
	if (screenheight == 200)
	    newscreen.ViewModes = HIRES;
	else
	    newscreen.ViewModes = HIRES | LACE;

    newwindow.Width = screenwidth;
    newwindow.Height = screenheight;

    if (screenwidth == 320) {
	requester1.LeftEdge = 0;
	requester2.LeftEdge = 56;
	RequesterStructure4.LeftEdge = 80;
    }
    else {
	requester1.LeftEdge = 160;
	requester2.LeftEdge = 216;
	RequesterStructure4.LeftEdge = 240;
    }

    if (screenheight == 200) {
	requester1.TopEdge = 50;
	requester2.TopEdge = 50;
	RequesterStructure4.TopEdge = 75;
    }
    else {
	requester1.TopEdge = 150;
	requester2.TopEdge = 150;
	RequesterStructure4.TopEdge = 175;
    }

    if ((screen = (struct Screen *)OpenScreen(&newscreen)) == NULL)
	cleanup("ShowFont: couldn't open the screen.");
    newwindow.Screen = screen;

    if ((window = (struct Window *)OpenWindow(&newwindow)) == NULL)
	cleanup("ShowFont: couldn't open the window.");
    rp = window->RPort;

    SetMenuStrip(window, &Menu1);

}

int findfont()
{

    char searchname[FONTNAMELEN + 6];

    int low, mid, high;

    low = 0;
    high = numfonts - 1;
    while (low <= high) {
	mid = (low + high) / 2;
	strcpy(searchname, fonts[mid].name);
	strcat(searchname, ".font");
	if (stricmp(fontname, searchname) < 0)
	    high = mid - 1;
	else
	    if (stricmp(fontname, searchname) > 0)
		low = mid + 1;
	    else
		return(mid);
    }

    return(-1);

}

USHORT proppos(line, maxline)
int line;
int maxline;
{

    if (maxline == 1)
	return((USHORT)MAXBODY);
    else
	return((USHORT)((MAXBODY * (line - 1)) / (maxline - 1)));

}

USHORT propsize(display, maxdisplay)
int display;
int maxdisplay;
{

    if (display >= maxdisplay)
	return((USHORT)MAXPOT);
    else
	return((USHORT)((MAXPOT * display) / maxdisplay));

}

int propline(proppos, maxline)
USHORT proppos;
int maxline;
{

    return((proppos * maxline) / MAXPOT);

}

void refresh1(rp)
struct RastPort *rp;
{

    register loop,
	     x = NAMEXPOS,
	     y = TOPYPOS + BASELINE;

    char namestring[NAMELEN + 1];

    SetDrMd(rp, JAM2);
    SetAPen(rp, 1);

    for (loop = 0; loop < MAXDISPLAY; loop++) {
	Move(rp, x, y);
	SetBPen(rp, 0);
	if ((nameline + loop) < numfonts) {
	    if ((nameline + loop) == namesel)
		SetBPen(rp, 2);
	    sprintf(namestring, "%-13s", fonts[nameline + loop].name);
	    Text(rp, namestring, NAMELEN);
	}
	else
	    Text(rp, "             ", NAMELEN);
	y += YINCR;
    }

}

void refresh2(rp)
struct RastPort *rp;
{

    register loop,
	     x = SIZEXPOS,
	     y = TOPYPOS + BASELINE;

    char sizestring[3 + 1];

    SetDrMd(rp, JAM2);

    for (loop = 0; loop < MAXDISPLAY; loop++) {
	Move(rp, x, y);
	SetBPen(rp, 0);
	if ((sizeline + loop) < SIZES) {
	    SetAPen(rp, 1);
	    if (fonts[namesel].size[sizeline + loop].flags & FPF_PROPORTIONAL)
		SetAPen(rp, 3);
	    if ((sizeline + loop) == SIZESEL)
		SetBPen(rp, 2);
	    sprintf(sizestring, "%3d", fonts[namesel].size[sizeline + loop].ysize);
	    Text(rp, sizestring, 3);
	}
	else
	    Text(rp, "   ", 3);
	y += YINCR;
    }

}

void refresh3(rp)
struct RastPort *rp;
{

    register x = STYLXPOS,
	     y = TOPYPOS + BASELINE,
	     loop;

    SetDrMd(rp, JAM2);
    SetAPen(rp, 1);

    for (loop = 0; loop < MAXDISPLAY; loop++) {
	Move(rp, x, y);
	if ((loop + styleline) == STYLESEL)
	    SetBPen(rp, 2);
	else
	    SetBPen(rp, 0);
	if ((loop + styleline) < STYLES) {
	    switch(STYLE[(loop + styleline)]) {
		case 0: Text(rp, "Normal      ", 12);
			break;
		case 1: Text(rp, "Underlined  ", 12);
			break;
		case 2: Text(rp, "Bold        ", 12);
			break;
		case 4: Text(rp, "Italic      ", 12);
			break;
		case 8: Text(rp, "Extended    ", 12);
			break;
	    }
	}
	else
	    Text(rp, "            ", 12);
	y += YINCR;
    }

}

void updateprop(prop, requester, proppos, propsize)
struct Gadget *prop;
struct Requester *requester;
USHORT proppos;
USHORT propsize;
{

    NewModifyProp(prop, window, requester, AUTOKNOB | FREEVERT, -1, proppos, -1, propsize, 1);

}

/* updatetype =  1 : refresh 1	    */
/*		     update 2, 3    */
/*		     refresh 2, 3   */
/*		 2 : refresh 2	    */
/*		     update 3	    */
/*		     refresh 3	    */
/*		 3 : refresh 3	    */
/*		10 : arrow 1, u/r 1 */
/*		20 : arrow 2, u/r 2 */
/*		30 : arrow 3, u/r 3 */
void duhprop(rp, updatetype)
struct RastPort *rp;
int updatetype;
{

    if (updatetype == 1) {
	rsizelastline = (SIZES - MAXDISPLAY) + 1;
	if (rsizelastline < 1)
	    rsizelastline = 1;
	sizeline = SIZESEL;
	if ((sizeline + 1) >= rsizelastline)
	    sizeline = rsizelastline - 1;
	sizepropsize = propsize(MAXDISPLAY, SIZES);
	sizeproppos = proppos((sizeline + 1), rsizelastline);
    }
    if (updatetype <= 3) {
	rstylelastline = (STYLES - MAXDISPLAY) + 1;
	if (rstylelastline < 1)
	    rstylelastline = 1;
	styleline = STYLESEL;
	if ((styleline + 1) >= rstylelastline)
	    styleline = rstylelastline - 1;
	stylepropsize = propsize(MAXDISPLAY, STYLES);
	styleproppos = proppos((styleline + 1), rstylelastline);
    }

    switch (updatetype) {
	 case 10: fontproppos = proppos((nameline + 1), rfontlastline);
		  break;
	 case 20: sizeproppos = proppos((sizeline + 1), rsizelastline);
		  break;
	 case 30: styleproppos = proppos((styleline + 1), rstylelastline);
		  break;
    }

    switch (updatetype) {
	case  1: refresh1(rp);
		 updateprop(&R_Size, &requester1, sizeproppos, sizepropsize);
		 updateprop(&R_Style, &requester1, styleproppos, stylepropsize);
		 refresh2(rp);
		 refresh3(rp);
		 break;
	case  2: refresh2(rp);
		 updateprop(&R_Style, &requester1, styleproppos, stylepropsize);
		 refresh3(rp);
		 break;
	case  3: refresh3(rp);
		 break;
	case 10: updateprop(&R_Font, &requester1, fontproppos, fontpropsize);
		 refresh1(rp);
		 break;
	case 20: updateprop(&R_Size, &requester1, sizeproppos, sizepropsize);
		 refresh2(rp);
		 break;
	case 30: updateprop(&R_Style, &requester1, styleproppos, stylepropsize);
		 refresh3(rp);
		 break;
    }

}

int selectfont()
{

    struct RastPort *rp;

    int returnvalue,
	refreshhow,
	x, y;

    ULONG  class;

    Request(&requester1, window);
    rp = requester1.ReqLayer->rp;

    namesel = findfont();

    rfontlastline = numfonts - MAXDISPLAY + 1;
    if (rfontlastline < 1)
	rfontlastline = 1;
    fontpropsize = propsize(MAXDISPLAY, numfonts);

    nameline = namesel;
    if ((nameline + 1) >= rfontlastline)
	nameline = rfontlastline - 1;

    duhprop(rp, 1);
    duhprop(rp, 10);

    DrawBorder(rp, &Outline1, 0, 0);

    refreshhow = 0;
    returnvalue = 0;

    while (returnvalue == 0) {
	switch (refreshhow) {
	    case  1: if (nameline > 0)
			 --nameline;
		     duhprop(rp, 10);
		     break;
	    case  2: nameline = propline(R_FontSInfo.VertPot, rfontlastline);
		     if ((nameline + 1) >= rfontlastline)
			 nameline = rfontlastline - 1;
		     refresh1(rp);
		     break;
	    case  3: if ((nameline + 1) < rfontlastline)
			 ++nameline;
		     duhprop(rp, 10);
		     break;
	    case  4: if (sizeline > 0)
			 --sizeline;
		     duhprop(rp, 20);
		     break;
	    case  5: sizeline = propline(R_SizeSInfo.VertPot, rsizelastline);
		     if ((sizeline + 1) >= rsizelastline)
			 sizeline = rsizelastline - 1;
		     refresh2(rp);
		     break;
	    case  6: if ((sizeline + 1) < rsizelastline)
			 ++sizeline;
		     duhprop(rp, 20);
		     break;
	    case  7: if (styleline > 0)
			 --styleline;
		     duhprop(rp, 30);
		     break;
	    case  8: styleline = propline(R_StyleSInfo.VertPot, rstylelastline);
		     if ((styleline + 1) >= rstylelastline)
			 styleline = rstylelastline - 1;
		     refresh3(rp);
		     break;
	    case  9: if ((styleline + 1) < rstylelastline)
			 ++styleline;
		     duhprop(rp, 30);
		     break;
	}

	message = (struct IntuiMessage *)GetMsg(window->UserPort);
	if (message != 0) {
	    class = message->Class;
	    x = message->MouseX - requester1.LeftEdge;
	    y = message->MouseY - requester1.TopEdge;
	    whichgad = (struct Gadget *)message->IAddress;
	    ReplyMsg(message);

	    if (class == GADGETDOWN) {
		refreshhow = whichgad->GadgetID;
	    }
	    if (class == GADGETUP) {
		switch (whichgad->GadgetID) {
		    case 101: returnvalue = 1;
			      break;
		    case 102: returnvalue = -1;
			      break;
		}
		refreshhow = 0; /* no more auto-scrolling */
	    }
	    if (class == MOUSEBUTTONS) {
		if ((y >= 0) && (y < 100) && (x >= 0) && (x < 320)) {
		    y -= TOPYPOS;
		    y = y / YINCR;
		    if (y < MAXDISPLAY) {
			if ((x >= NAMEXPOS) && (x < (NAMEXPOS + NAMELEN * 8))) {
			    if (namesel == (nameline + y)) {
				returnvalue = 1;
				EndRequest(&requester1, window);
			    }
			    else {
				namesel = nameline + y;
				if (namesel >= numfonts)
				    namesel = numfonts - 1;
				duhprop(rp, 1);
			    }
			}
			if ((x >= SIZEXPOS) && (x < (SIZEXPOS + 3 * 8))) {
			    if (SIZESEL == (sizeline + y)) {
				returnvalue = 1;
				EndRequest(&requester1, window);
			    }
			    else {
				SIZESEL = sizeline + y;
				if (SIZESEL >= SIZES)
				    SIZESEL = SIZES - 1;
				duhprop(rp, 2);
			    }
			}
			if ((x >= STYLXPOS) && (x < (STYLXPOS + 12 * 8))) {
			    if (STYLESEL == (styleline + y)) {
				returnvalue = 1;
				EndRequest(&requester1, window);
			    }
			    else {
				STYLESEL = styleline + y;
				if (STYLESEL >= STYLES)
				    STYLESEL = STYLES - 1;
				duhprop(rp, 3);
			    }
			}
		    }
		}
	    }
	}
    }

    if (returnvalue == 1) {
	strcpy(fontname, fonts[namesel].name);
	strcat(fontname, ".font");
	myfont.ta_YSize = fonts[namesel].size[SIZESEL].ysize;
	myfont.ta_Style = STYLE[STYLESEL];
	myfont.ta_Flags = fonts[namesel].size[SIZESEL].flags;
    }

    return(returnvalue);

}

void mycat(s, c)
char *s;
char c;
{

    while (*s++ != '\0') {}
    *(s - 1) = c;
    *s = '\0';

}

void main(argc,argv)
int argc;
char *argv[];
{

    int cont = TRUE,
	rethinkfont = TRUE;

    ULONG   class;
    USHORT  code;

    int line, maxline,
	yarea,
	startchar,
	len, pixels,
	whichmenu, whichitem,
	wproplastline,
	refreshhow,
	x, y;

    char windowtitle[81];

    USHORT numy, pos, size;

    register loop, i;

    UBYTE textline[256], character;

    if ((argc == 2) && (*argv[1] == '?'))
	cleanup("Usage: ShowFont [font_name] [font_size]");

    if ((IntuitionBase = (struct IntuitionBase *)
	    OpenLibrary("intuition.library", 0)) == NULL)
	cleanup("ShowFont: couldn't open 'intuition.library'.");

    if ((GfxBase = (struct GfxBase *)
	    OpenLibrary("graphics.library", 0)) == NULL)
	cleanup("ShowFont: couldn't open 'graphics.library'.");

    if ((DiskfontBase = (struct DiskfontBase *)
	    OpenLibrary("diskfont.library", 0)) == NULL)
	cleanup("ShowFont: couldn't open 'diskfont.library'.");

    screenwidth = 640;
    screenheight = 200;
    setupscreen();

    fonts = NULL;

    if (argc == 1)
	strcpy(fontname, "Topaz.font");
    else
	sprintf(fontname, "%s.font", argv[1]);
    if (argc < 3) {
	readfonts();
	selectfont();
    }
    else
	myfont.ta_YSize = atoi(argv[2]);

    for (i = 0; i < 256; ++i)
	fontline[i].length = 0;

    while (cont) {

	if (rethinkfont) {

	    if (font)
		CloseFont(font);

	    if ((font = (struct TextFont *)OpenDiskFont(&myfont)) == NULL)
		cleanup("ShowFont: couldn't find the font.");

	    if (myfont.ta_YSize > screenheight) {
		screenheight = 400;
		setupscreen();
	    }

	    SetFont(rp, font);

	    clearfontlines();

	    startchar = font->tf_LoChar;
	    line = 0;
	    len = 0;

	    for (loop = font->tf_LoChar; loop <= font->tf_HiChar; ++loop) {
		textline[len++] = loop;
		pixels = TextLength(rp, textline, len) + window->BorderLeft;
		if (pixels > (screenwidth - XAREA)) {
		    --len;
		    fontline[line].string = (UBYTE *)AllocMem(len, MEMF_CLEAR);
		    if (fontline[line].string == NULL)
			cleanup("ShowFont: couldn't allocate 'fontline' memory!");
		    for (i = startchar; i < loop; ++i)
			fontline[line].string[i - startchar] = i;
		    fontline[line].length = len;
		    startchar = loop;
		    ++line;
		    len = 0;
		    --loop; /* must go back and insert that character */
		}
		else
		    if (loop == font->tf_HiChar) {
			fontline[line].string = (UBYTE *)AllocMem(len, MEMF_CLEAR);
			if (fontline[line].string == NULL)
			    cleanup("ShowFont: couldn't allocate 'fontline' memory!");
			for (i = startchar; i <= font->tf_HiChar; ++i)
			    fontline[line].string[i - startchar] = i;
			fontline[line].length = len;
			++line;
		    }
	    }

	    maxline = line;
	    line = 0;
	    yarea = screenheight - (window->BorderTop + window->BorderBottom);
	    numy = yarea / font->tf_YSize;

	    wproplastline = (maxline - numy) + 1;
	    if (wproplastline < 1)
		wproplastline = 1;

	    size = propsize(numy, maxline);

	    stccpy(windowtitle, fontname, (strlen(fontname) - 4));
	    sprintf(windowtitle + strlen(windowtitle), " - %d", myfont.ta_YSize);
	    SetWindowTitles(window, windowtitle, -1);

	    rethinkfont = FALSE;
	    refreshhow = 99;	/* refresh it once only */
	}

	switch (refreshhow) {
	    case  1: if (line > 0)
			 --line;
		     break;
	    case  2: pos = W_PropSInfo.VertPot;
		     line = propline(pos, wproplastline);
		     if ((line + 1) > wproplastline)
			 line = wproplastline - 1;
		     break;
	    case  3: if ((line + 1) < wproplastline)
			 ++line;
		     break;
	}

	if (refreshhow != 0) {

	    pos = proppos((line + 1), wproplastline);

	    if (refreshhow != 2)    /* don't blink the slider */
		NewModifyProp(&W_Prop, window, NULL, AUTOKNOB | FREEVERT, -1, pos, -1, size, 1);

	    WaitTOF();  /* might just possibly reduce blinking */
	    SetAPen(rp, 0); /* clear the screen */
	    RectFill(rp, window->BorderLeft, window->BorderTop, (screenwidth - XAREA), (screenheight - window->BorderBottom));

	    SetAPen(rp, 1);
	    SetBPen(rp, 0);
	    SetDrMd(rp, JAM2);

	    for (i = line; i < (line + numy); ++i) {
		Move(rp, window->BorderLeft, ((i - line) * font->tf_YSize) + window->BorderTop + font->tf_Baseline);
		Text(rp, fontline[i].string, fontline[i].length);
	    }

	    if (refreshhow == 99)
		refreshhow = 0; /* for initial refreshment only */
	}

	message = (struct IntuiMessage *)GetMsg(window->UserPort);
	if (message != 0) {
	    class = message->Class;
	    code  = message->Code;
	    whichgad = (struct Gadget *)message->IAddress;
	    x = message->MouseX;
	    y = message->MouseY;
	    ReplyMsg(message);

	    if (class == CLOSEWINDOW)
		cont = FALSE;
	    if (class == GADGETDOWN)
		refreshhow = whichgad->GadgetID;
	    if (class == GADGETUP)
		refreshhow = 0; /* stop scrolling */
	    if (class == MOUSEBUTTONS) {
		y -= window->BorderTop;
		y /= font->tf_YSize;
		if (y < maxline) {
		    for (loop = 0; loop < fontline[(y + line)].length; loop++) {
			textline[loop] = fontline[(y + line)].string[loop];
			pixels = TextLength(rp, textline, (loop + 1)) + window->BorderLeft;
			if (x <= pixels)
			    break;
		    }
		    character = fontline[(y + line)].string[loop];
		    switch (qualifiers[character]) {
			case 0: textline[0] = '\0';
				break;
			case 1: strcpy(textline, "CONTROL-");
				break;
			case 2: strcpy(textline, "SHIFT-");
				break;
			case 3: strcpy(textline, "ALT-");
				break;
			case 4: strcpy(textline, "ALT-SHIFT-");
				break;
			case 5: strcpy(textline, "CONTROL-ALT-");
				break;
		    }
		    len = strlen(textline);
		    textline[len] = whichkey[character];
		    textline[len + 1] = '\0';
		    IText19.LeftEdge = 80 - (strlen(textline) * 4);
		    IText19.IText = textline;
		    Request(&RequesterStructure4, window);
		    while ((message = (struct IntuiMessage *)GetMsg(window->UserPort)) != 0)
			ReplyMsg(message);
		}
	    }
	    if (class == MENUPICK) {
		if (code != MENUNULL) {
		    whichmenu = MENUNUM(code);
		    whichitem = ITEMNUM(code);
		    switch (whichmenu) {
			case 0 : switch (whichitem) {
				    case 0: Request(&requester2, window); /* about */
					    while ((message = (struct IntuiMessage *)GetMsg(window->UserPort)) != 0)
						ReplyMsg(message);
					    break;
				    case 1: cont = FALSE; /* quit */
					    break;
				 }
				 break;
			case 1 : switch (whichitem) {
				    case 0: if (fonts == NULL)
						readfonts();
					    if (selectfont() == 1)
						rethinkfont = TRUE;
					    break; /* font selection */
				 }
				 break;
			case 2 : switch (whichitem) {
				    case 0: screenwidth = 320;
					    screenheight = 200;
					    setupscreen();
					    break;
				    case 1: screenwidth = 320;
					    screenheight = 400;
					    setupscreen();
					    break;
				    case 2: screenwidth = 640;
					    screenheight = 200;
					    setupscreen();
					    break;
				    case 3: screenwidth = 640;
					    screenheight = 400;
					    setupscreen();
					    break;
				 }
				 rethinkfont = TRUE;
				 break;
		    }
		}
	    }
	}
    }

    cleanup(NULL);

}
