/*******************************************************************************
*									       *
* fontsel.c -- Nirvana Font Selector main program			       *
*									       *
* Copyright (c) 1993 Universities Research Association, Inc.		       *
* All rights reserved.							       *
* 									       *
* This material resulted from work developed under a Government Contract and   *
* is subject to the following license:  The Government retains a paid-up,      *
* nonexclusive, irrevocable worldwide license to reproduce, prepare derivative *
* works, perform publicly and display publicly by or for the Government,       *
* including the right to distribute to other Government contractors.  Neither  *
* the United States nor the United States Department of Energy, nor any of     *
* their employees, makes any warrenty, express or implied, or assumes any      *
* legal liability or responsibility for the accuracy, completeness, or         *
* usefulness of any information, apparatus, product, or process disclosed, or  *
* represents that its use would not infringe privately owned rights.           *
*                                        				       *
* Fermilab Nirvana GUI Library						       *
* June 2, 1993								       *
*									       *
* Written by Suresh Ravoor (assisted by Mark Edel)			       *
*									       *
*******************************************************************************/
static char SCCSID[] = "@(#)fontsel.c	1.12	10/3/96";

#include <stdio.h>
#include <stdlib.h>
#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/PushB.h>
#include <Xm/List.h>
#include <Xm/Label.h>
#include <Xm/Text.h>
#include <Xm/ToggleB.h>
#include <Xm/MessageB.h>
#include <Xm/DialogS.h>
#include "DialogF.h"
#include "misc.h"
#include "fontsel.h"

#define	MAX_ARGS			20
#define MAX_NUM_FONTS   		32767
#define MAX_FONT_NAME_LEN		256
#define MAX_ENTRIES_IN_LIST		5000
#define MAX_DISPLAY_SIZE		150
#define DELIM				'-'
#define NUM_COMPONENTS_FONT_NAME	14
#define TEMP_BUF_SIZE			256
#define DISPLAY_HEIGHT			90

enum	listSpecifier	{ NONE, FONT, STYLE, SIZE };


/*	local data structures and types */

typedef struct
{
	Widget		form;		/* widget id */
	Widget		okButton;	/* widget id */
	Widget		cancelButton;	/* widget id */
	Widget		fontList;	/* widget id */
	Widget		styleList;	/* widget id */
	Widget		sizeList;	/* widget id */
	Widget		fontNameField;	/* widget id */
	Widget		sizeToggle;	/* widget id */
	Widget		propFontToggle;	/* widget id */
	Widget		dispLabel;	/* widget id */
	char		**fontData;	/* font name info  */
	int		numFonts;	/* number of fonts */
	char		*sel1;		/* selection from list 1 */
	char		*sel2;		/* selection from list 2 */
	char		*sel3;		/* selection from list 3 */
	int		showPropFonts;	/* toggle state - show prop fonts */
	int		showSizeInPixels;/* toggle state - size in pixels  */
	char		*fontName;	/* current font name */
	XFontStruct	*oldFont;	/* font data structure for dispSample */
	XmFontList	oldFontList;	/* font data structure for dispSample */
	int	exitFlag;		/* used for program exit control */
}	xfselControlBlkType;


/*	local function prototypes */		

static void	getStringComponent(char *inStr, int pos, char *outStr);
static void	setupScrollLists(int dontChange, xfselControlBlkType ctrlBlk);
static int	notPropFont(char *font);
static int	styleMatch(xfselControlBlkType *ctrlBlk, char *font);
static int	sizeMatch(xfselControlBlkType *ctrlBlk, char *font);
static int	fontMatch(xfselControlBlkType *ctrlBlk, char *font);
static void	addItemToList(char **buf, char *item, int *count);
static void	getFontPart(char *font, char *buff1);
static void	getStylePart(char *font, char *buff1);
static void	getSizePart(char *font, char *buff1, int inPixels);
static void	propFontToggleAction(Widget widget, 
				     xfselControlBlkType *ctrlBlk, 
				     XmToggleButtonCallbackStruct *call_data);
static void	sizeToggleAction(Widget widget,
				 xfselControlBlkType *ctrlBlk, 
				 XmToggleButtonCallbackStruct *call_data);
static void	fontAction(Widget widget, xfselControlBlkType *ctrlBlk, 
				 XmListCallbackStruct *call_data);
static void	styleAction(Widget widget, xfselControlBlkType *ctrlBlk, 
				 XmListCallbackStruct *call_data);
static void	sizeAction(Widget widget, xfselControlBlkType *ctrlBlk, 
				 XmListCallbackStruct *call_data);
static void	choiceMade(xfselControlBlkType *ctrlBlk);
static void	dispSample(xfselControlBlkType *ctrlBlk);
static void	cancelAction(Widget widget, xfselControlBlkType *ctrlBlk,
				 XmListCallbackStruct *call_data);
static void	okAction(Widget widget, xfselControlBlkType *ctrlBlk,
				 XmPushButtonCallbackStruct *call_data);
static void	startupFont(xfselControlBlkType *ctrlBlk, char *font);
static void 	setFocus(Widget w, xfselControlBlkType *ctrlBlk, XEvent *event, 
						Boolean *continueToDispatch);
static void	FindBigFont(xfselControlBlkType *ctrlBlk, char *bigFont);

/*******************************************************************************
*                                                                              *
*     FontSel ()                                                               *
*                                                                              *
*                                                                              *
*            Function to put up a modal font selection dialog box. The purpose *
*            of this routine is to allow the user to interactively view sample *
*            fonts and to choose a font for current use.                       *
*                                                                              *
*     Arguments:                                                               *
*                                                                              *
*            Widget	parent 		- parent widget ID                     *
*                                                                              *
*            int        showPropFont    - ONLY_FIXED : shows only fixed fonts  *
*                                                      doesn't show prop font  *
*                                                      toggle button also.     *
*                                         PREF_FIXED : can select either fixed *
*                                                      or proportional fonts;  *
*                                                      but starting option is  *
*                                                      Fixed fonts.            *
*                                         PREF_PROP  : can select either fixed *
*                                                      or proportional fonts;  *
*                                                      but starting option is  *
*                                                      proportional fonts.     *
*                                                                              *
*           char *	currFont        - ASCII string that contains the name  *
*                                         of the currently selected font.      *
*                                                                              *
*     Returns:                                                                 *
*                                                                              *
*           pointer to an ASCII character string that contains the name of     *
*           the selected font (in X format for naming fonts); it is the users  *
*           responsibility to free the space allocated to this string.         *
*                                                                              *
*     Comments:                                                                *
*                                                                              *
*           The calling function has to call the appropriate routines to set   *
*           the current font to the one represented by the returned string.    *
*                                                                              *
*******************************************************************************/

char 	*FontSel(Widget parent, int showPropFonts, char *currFont)
{
	Widget			dialog, form, okButton, cancelButton;
	Widget			styleList, sizeList, fontName, fontList;
	Widget			sizeToggle, propFontToggle, dispLabel;
	Widget			nameLabel;
	Arg			args[MAX_ARGS];
	int			n;
	XmString		tempStr;
	char			bigFont[MAX_FONT_NAME_LEN];
	xfselControlBlkType	ctrlBlk;

	ctrlBlk.fontData	= XListFonts(XtDisplay(parent), 
					     "-*-*-*-*-*-*-*-*-*-*-*-*-*-*", 
					     MAX_NUM_FONTS, &ctrlBlk.numFonts);
	FindBigFont(&ctrlBlk, bigFont);
	ctrlBlk.oldFont = XLoadQueryFont(XtDisplay(parent), bigFont);
	ctrlBlk.oldFontList = XmFontListCreate(ctrlBlk.oldFont,
		XmSTRING_DEFAULT_CHARSET);

	dialog	= XmCreateDialogShell (parent, "Font Selector", args, 0);

	/* 	Set up window sizes for form widget */

	n = 0;
	XtSetArg(args[n], XmNautoUnmanage, FALSE); n++;
 	XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;

	/*	Create form popup dialog widget */

	form	= XtCreateWidget ("Font Selector", xmFormWidgetClass, dialog, 
								       args, n);

	/*	Create pushbutton widgets */

	n = 0;
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
 	XtSetArg(args[n], XmNbottomOffset, 4); n++;
 	XtSetArg(args[n], XmNtopOffset, 1); n++;
	XtSetArg(args[n], XmNrightPosition, 45); n++;
	XtSetArg(args[n], XmNwidth, 110); n++;
	XtSetArg(args[n], XmNheight, 28); n++;
	XtSetArg(args[n], XmNshowAsDefault, TRUE); n++;
	okButton = XtCreateManagedWidget("OK", xmPushButtonWidgetClass, form,
								      args, n);

	n = 0;
	XtSetArg(args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
	XtSetArg(args[n], XmNtopWidget, okButton); n++;
	XtSetArg(args[n], XmNbottomWidget, okButton); n++;
	XtSetArg(args[n], XmNleftPosition, 55); n++;
	XtSetArg(args[n], XmNwidth, 110); n++;
	XtSetArg(args[n], XmNheight, 28); n++;
	cancelButton = XtCreateManagedWidget("Cancel", xmPushButtonWidgetClass,
								form, args, n);

	/*	create font name text widget and the corresponding label */

	n = 0; 
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; 
	XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; 
	XtSetArg(args[n], XmNbottomWidget, okButton); n++; 
	XtSetArg(args[n], XmNleftPosition, 1); n++; 
	XtSetArg(args[n], XmNrightPosition, 99); n++; 
	XtSetArg(args[n], XmNeditable, True); n++;
	XtSetArg(args[n], XmNeditMode, XmSINGLE_LINE_EDIT); n++;
	XtSetArg(args[n], XmNmaxLength, MAX_FONT_NAME_LEN); n++;
	fontName = XtCreateManagedWidget("fontname", xmTextWidgetClass, form,
								     args, n);
	RemapDeleteKey(fontName);	/* kludge to handle delete and BS */

	n = 0; 
	tempStr = XmStringCreate("Font Name:", XmSTRING_DEFAULT_CHARSET);
	XtSetArg(args[n], XmNlabelString, tempStr); n++;
	XtSetArg(args[n], XmNmnemonic, 'N'); n++;
	XtSetArg(args[n], XmNuserData, fontName); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); n++; 
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 
	XtSetArg(args[n], XmNleftWidget, fontName); n++; 
	XtSetArg(args[n], XmNbottomWidget, fontName); n++;
	XtSetArg(args[n], XmNtopOffset, 1); n++;
	nameLabel = XtCreateManagedWidget("Font Name:", xmLabelWidgetClass,
		form, args, n);
	XmStringFree(tempStr);

	/*	create sample display label widget */

	n = 0;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; 
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 
	XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; 
	XtSetArg(args[n], XmNrightPosition, 99); n++;
	XtSetArg(args[n], XmNbottomWidget, nameLabel); n++;
	XtSetArg(args[n], XmNleftPosition, 1); n++; 
 	XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
       	XtSetArg(args[n], XmNrecomputeSize, FALSE); n++;
   	XtSetArg(args[n], XmNfontList, ctrlBlk.oldFontList); n++;
 	dispLabel = XtCreateManagedWidget(" ", xmLabelWidgetClass, form, 
								       args, n);

	/*	create toggle buttons */

	n = 0; 
	tempStr = XmStringCreate("Show Size in Points", 
						      XmSTRING_DEFAULT_CHARSET);
	XtSetArg(args[n], XmNlabelString, tempStr); n++;
	XtSetArg(args[n], XmNmnemonic, 'P'); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; 
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 
	XtSetArg(args[n], XmNleftPosition, 2); n++; 
	XtSetArg(args[n], XmNtopOffset, 1); n++;
	XtSetArg(args[n], XmNbottomWidget, dispLabel); n++;
	sizeToggle = XtCreateManagedWidget("sizetoggle", 
				      xmToggleButtonWidgetClass, form, args, n);
	XmStringFree(tempStr);

	if (showPropFonts != ONLY_FIXED)
	{
		n = 0; 
		tempStr = XmStringCreate("Show Proportional Width Fonts", 
						      XmSTRING_DEFAULT_CHARSET);
		XtSetArg(args[n], XmNlabelString, tempStr); n++;
		XtSetArg(args[n], XmNmnemonic, 'W'); n++;
		XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; 
		XtSetArg(args[n], XmNtopAttachment, 
						XmATTACH_OPPOSITE_WIDGET); n++; 
		XtSetArg(args[n], XmNbottomAttachment, 
						XmATTACH_OPPOSITE_WIDGET); n++; 
		XtSetArg(args[n], XmNrightPosition, 98); n++; 
		XtSetArg(args[n], XmNtopWidget, sizeToggle); n++;
		XtSetArg(args[n], XmNbottomWidget, sizeToggle); n++;
		XtSetArg(args[n], XmNtopOffset, 1); n++;
		propFontToggle = XtCreateManagedWidget("propfonttoggle", 
				      xmToggleButtonWidgetClass, form, args, n);
		XmStringFree(tempStr);
	}

	/*	create scroll list widgets */
	/*	"Font" list */

	n = 0;
	tempStr = XmStringCreate("Font:", XmSTRING_DEFAULT_CHARSET);
	XtSetArg(args[n], XmNlabelString, tempStr); n++;
	XtSetArg(args[n], XmNmnemonic, 'F'); n++; 
	XtSetArg(args[n], XmNtopOffset, 2); n++;
	XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
	XtSetArg(args[n], XmNleftPosition, 1); n++;
	nameLabel = XtCreateManagedWidget("Font:", xmLabelWidgetClass, form, 
								      args, n);
	XmStringFree(tempStr);

	n = 0;
 	XtSetArg(args[n], XmNvisibleItemCount, 15); n++;
	XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
	XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
	XtSetArg(args[n], XmNbottomWidget, sizeToggle); n++;
	XtSetArg(args[n], XmNtopWidget, nameLabel); n++;
	XtSetArg(args[n], XmNleftWidget, nameLabel); n++;
	XtSetArg(args[n], XmNrightPosition, 52); n++;
	fontList = XmCreateScrolledList(form, "fontlist", args, n);
	XtManageChild(fontList);
	XtVaSetValues(nameLabel, XmNuserData, fontList, 0);

	/*	"Style" list */

	n = 0;
	XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
	XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
	XtSetArg(args[n], XmNtopWidget, nameLabel); n++;
	XtSetArg(args[n], XmNleftOffset, 5); n++;
	XtSetArg(args[n], XmNleftWidget, fontList); n++;
	XtSetArg(args[n], XmNbottomWidget, fontList); n++;
	XtSetArg(args[n], XmNrightPosition, 85); n++;
	styleList = XmCreateScrolledList(form, "stylelist", args, n);
	XtManageChild(styleList);

	n = 0;
	tempStr = XmStringCreate("Style:", XmSTRING_DEFAULT_CHARSET);
	XtSetArg(args[n], XmNmnemonic, 'y'); n++; 
	XtSetArg(args[n], XmNuserData, styleList); n++;
	XtSetArg(args[n], XmNlabelString, tempStr); n++;
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
	XtSetArg(args[n], XmNbottomWidget, styleList); n++;
	XtSetArg(args[n], XmNleftWidget, styleList); n++;
	XtCreateManagedWidget("Style:", xmLabelWidgetClass, form, args, n);
	XmStringFree(tempStr);

	/*	"Size" list */

	n = 0;
	XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
	XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
	XtSetArg(args[n], XmNtopWidget, nameLabel); n++;
	XtSetArg(args[n], XmNleftWidget, styleList); n++;
	XtSetArg(args[n], XmNbottomWidget, fontList); n++;
	XtSetArg(args[n], XmNleftOffset, 5); n++;
	XtSetArg(args[n], XmNrightPosition, 99); n++;
	sizeList = XmCreateScrolledList(form, "sizelist", args, n);
	XtManageChild(sizeList);

	n = 0;
	tempStr = XmStringCreate("Size:", XmSTRING_DEFAULT_CHARSET);
	XtSetArg(args[n], XmNlabelString, tempStr); n++;
	XtSetArg(args[n], XmNmnemonic, 'z'); n++; 
	XtSetArg(args[n], XmNuserData, sizeList); n++;
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
	XtSetArg(args[n], XmNbottomWidget, sizeList); n++;
	XtSetArg(args[n], XmNleftWidget, sizeList); n++;
	XtCreateManagedWidget("Size:", xmLabelWidgetClass, form, args, n);
	XmStringFree(tempStr);

	/*	update form widgets cancel button */

	n = 0;
	XtSetArg(args[n], XmNcancelButton, cancelButton); n++;
	XtSetValues(form, args, n);


	/*	update application's control block structure */

	ctrlBlk.form		= form;
	ctrlBlk.okButton	= okButton;
	ctrlBlk.cancelButton	= cancelButton;
	ctrlBlk.fontList	= fontList;
	ctrlBlk.styleList	= styleList;
	ctrlBlk.sizeList	= sizeList;
	ctrlBlk.fontNameField	= fontName;
	ctrlBlk.sizeToggle	= sizeToggle;
	if (showPropFonts != ONLY_FIXED)
		ctrlBlk.propFontToggle	= propFontToggle;
	ctrlBlk.dispLabel	= dispLabel;
	ctrlBlk.exitFlag	= FALSE;
	ctrlBlk.showPropFonts	= showPropFonts;
	ctrlBlk.showSizeInPixels= TRUE;
	ctrlBlk.sel1		= NULL;
	ctrlBlk.sel2		= NULL;
	ctrlBlk.sel3		= NULL;
	ctrlBlk.fontName	= NULL;

	setupScrollLists(NONE, ctrlBlk);	/* update scroll lists */ 

	if (showPropFonts == PREF_PROP)
		XmToggleButtonSetState(propFontToggle, TRUE, FALSE); 
		
	/*	Register callback functions */

	if (showPropFonts != ONLY_FIXED)
		XtAddCallback(propFontToggle, XmNvalueChangedCallback, 
			(XtCallbackProc)propFontToggleAction, (char *)&ctrlBlk);
	XtAddCallback(sizeToggle, XmNvalueChangedCallback,
		(XtCallbackProc)sizeToggleAction, (char *)&ctrlBlk);
	XtAddCallback(fontList, XmNbrowseSelectionCallback,
		(XtCallbackProc)fontAction, (char *)&ctrlBlk);
	XtAddCallback(styleList, XmNbrowseSelectionCallback,
		(XtCallbackProc)styleAction, (char *)&ctrlBlk);
	XtAddCallback(sizeList, XmNbrowseSelectionCallback,
		(XtCallbackProc)sizeAction, (char *)&ctrlBlk);
	XtAddCallback(okButton, XmNactivateCallback,
		(XtCallbackProc)okAction, (char *)&ctrlBlk);
	XtAddCallback(cancelButton, XmNactivateCallback,
		(XtCallbackProc)cancelAction, (char *)&ctrlBlk);

	/* add event handler to setup input focus at start to scroll list */

	XtAddEventHandler(fontList, FocusChangeMask, FALSE,
		(XtEventHandler)setFocus, (char *)&ctrlBlk);
	XmProcessTraversal(fontList, XmTRAVERSE_CURRENT);

	/*	setup tabgroups */

	XmAddTabGroup(fontList);
	XmAddTabGroup(styleList);
	XmAddTabGroup(sizeList);
	XmAddTabGroup(sizeToggle);
	if (showPropFonts != ONLY_FIXED)
		XmAddTabGroup(propFontToggle);
	XmAddTabGroup(fontName);
	XmAddTabGroup(okButton);
	XmAddTabGroup(cancelButton);

	/*	Link Motif Close option to cancel action */

	AddMotifCloseCallback(dialog, (XtCallbackProc)cancelAction, &ctrlBlk);

	/*	Handle dialog mnemonics  */

	AddDialogMnemonicHandler(form);

	/*	Realize Widgets  */

	ManageDialogCenteredOnPointer(form);

	/* set up current font parameters */

	if (currFont[0] != '\0')
		startupFont(&ctrlBlk, currFont); 

	/*	enter event loop */

	while (! ctrlBlk.exitFlag)
		XtAppProcessEvent(XtWidgetToApplicationContext(form), XtIMAll);

	XtDestroyWidget(dialog);

	if (ctrlBlk.oldFont != NULL)
	{
 		XFreeFont(XtDisplay(form),  ctrlBlk.oldFont);
		XmFontListFree(ctrlBlk.oldFontList);
	}

	return(ctrlBlk.fontName);
}


/*	gets a specific substring from a string */

static void	getStringComponent(char *inStr, int pos, char *outStr)
{
	int	i, j;

	*outStr = '\0';

	if (pos > NUM_COMPONENTS_FONT_NAME)
	{
		fprintf(stderr, "Warning: getStringComponent being used for ");
		fprintf(stderr, "pos > %d\nIf such ", NUM_COMPONENTS_FONT_NAME);
		fprintf(stderr, "use is intended remove these warning lines\n");
	}
	
	for (i = 0; (pos > 0) && (inStr[i] != '\0'); i++)
		if (inStr[i] == DELIM)
			pos--;
	
	if (inStr[i] == '\0')
		return;

	for (j = 0; (inStr[i] != DELIM) && (inStr[i] != '\0'); i++, j++)
		outStr[j] = inStr[i];
	outStr[j] = '\0';
}


/* parse through the fontlist data and set up the three scroll lists */

static void	setupScrollLists(int dontChange, xfselControlBlkType ctrlBlk)
{
	char 		*itemBuf1[MAX_ENTRIES_IN_LIST];
	char 		*itemBuf2[MAX_ENTRIES_IN_LIST];
	char 		*itemBuf3[MAX_ENTRIES_IN_LIST];
	int		itemCount1, itemCount2, itemCount3;
	char		buff1[TEMP_BUF_SIZE];
	XmString	items[MAX_ENTRIES_IN_LIST];
	int		i;

	itemCount1 = 0;
	itemCount2 = 0;
	itemCount3 = 0;

	for (i = 0; i < ctrlBlk.numFonts && i < MAX_ENTRIES_IN_LIST; i++)
	{
		if ((dontChange != FONT) &&
		    (styleMatch(&ctrlBlk, ctrlBlk.fontData[i])) &&
		    (sizeMatch (&ctrlBlk, ctrlBlk.fontData[i])) &&
		    ((ctrlBlk.showPropFonts == PREF_PROP) || 
		     (notPropFont(ctrlBlk.fontData[i]))))
		{
			getFontPart(ctrlBlk.fontData[i], buff1);
			addItemToList(itemBuf1, buff1, &itemCount1);
		}

		if ((dontChange != STYLE) &&
		    (fontMatch(&ctrlBlk, ctrlBlk.fontData[i])) &&
		    (sizeMatch (&ctrlBlk, ctrlBlk.fontData[i])) &&
		    ((ctrlBlk.showPropFonts == PREF_PROP) || 
		     (notPropFont(ctrlBlk.fontData[i]))))
		{
			getStylePart(ctrlBlk.fontData[i], buff1);
			addItemToList(itemBuf2, buff1, &itemCount2);
		}

		if ((dontChange != SIZE) &&
		    (fontMatch(&ctrlBlk, ctrlBlk.fontData[i])) &&
		    (styleMatch (&ctrlBlk, ctrlBlk.fontData[i])) &&
		    ((ctrlBlk.showPropFonts == PREF_PROP) || 
		     (notPropFont(ctrlBlk.fontData[i]))))
		{
			getSizePart(ctrlBlk.fontData[i], buff1, 
					      ctrlBlk.showSizeInPixels);
			addItemToList(itemBuf3, buff1, &itemCount3);
		}
	}	/* end - for (i = 0; i < ctrlBlk.numFonts; i++) */

	/*	recreate all three scroll lists where necessary */
	if (dontChange != FONT)
	{
		for (i = 0; i < itemCount1; i++)
		{
			items[i] = XmStringCreate(itemBuf1[i],
						      XmSTRING_DEFAULT_CHARSET);
			XtFree(itemBuf1[i]);
		}
		XmListDeleteAllItems(ctrlBlk.fontList);
		XmListAddItems(ctrlBlk.fontList, items, itemCount1, 1);
		if (ctrlBlk.sel1 != NULL)
		{
			XmStringFree(items[0]);
			items[0] = XmStringCreate(ctrlBlk.sel1,
						      XmSTRING_DEFAULT_CHARSET);
			XmListSelectItem(ctrlBlk.fontList, items[0], FALSE);
			XmListSetBottomItem(ctrlBlk.fontList, items[0]);
		}
		for (i = 0; i < itemCount1; i++)
			XmStringFree(items[i]);
	}

	if (dontChange != STYLE)
	{
		for (i = 0; i < itemCount2; i++)
		{
			items[i] = XmStringCreate(itemBuf2[i],
						      XmSTRING_DEFAULT_CHARSET);
			XtFree(itemBuf2[i]);
		}
		XmListDeleteAllItems(ctrlBlk.styleList);
		XmListAddItems(ctrlBlk.styleList, items, itemCount2, 1);
		if (ctrlBlk.sel2 != NULL)
		{
			XmStringFree(items[0]);
			items[0] = XmStringCreate(ctrlBlk.sel2,
						      XmSTRING_DEFAULT_CHARSET);
			XmListSelectItem(ctrlBlk.styleList, items[0], FALSE);
			XmListSetBottomItem(ctrlBlk.styleList, items[0]);
		}
		for (i = 0; i < itemCount2; i++)
			XmStringFree(items[i]);
	}

	if (dontChange != SIZE)
	{
		for (i = 0; i < itemCount3; i++)
		{
			items[i] = XmStringCreate(itemBuf3[i],
						      XmSTRING_DEFAULT_CHARSET);
			XtFree(itemBuf3[i]);
		}
		XmListDeleteAllItems(ctrlBlk.sizeList);
		XmListAddItems(ctrlBlk.sizeList, items, itemCount3, 1);
		if (ctrlBlk.sel3 != NULL)
		{
			XmStringFree(items[0]);
			items[0] = XmStringCreate(ctrlBlk.sel3,
						      XmSTRING_DEFAULT_CHARSET);
			XmListSelectItem(ctrlBlk.sizeList, items[0], FALSE);
			XmListSetBottomItem(ctrlBlk.sizeList, items[0]);
		}
		for (i = 0; i < itemCount3; i++)
			XmStringFree(items[i]);
	}
}


/*	returns TRUE if argument is not name of a proportional font */

static int	notPropFont(char *font)
{
	char	buff1[TEMP_BUF_SIZE];

	getStringComponent(font, 11, buff1);
	if ((strcmp(buff1, "p") == 0) || (strcmp(buff1, "P") == 0))
		return(FALSE);
	else
		return(TRUE);
}


/*	returns TRUE if the style portion of the font matches the currently
	selected style */

static int	styleMatch(xfselControlBlkType *ctrlBlk, char *font)
{
	char	buff[TEMP_BUF_SIZE];

	if (ctrlBlk->sel2 == NULL)
		return(TRUE);

	getStylePart(font, buff);

	if (strcmp(buff, ctrlBlk->sel2) == 0)
		return(TRUE);
	else
		return(FALSE);
}


/*	returns TRUE if the size portion of the font matches the currently
	selected size */

static int	sizeMatch(xfselControlBlkType *ctrlBlk, char *font)
{
	char	buff[TEMP_BUF_SIZE];

	if (ctrlBlk->sel3 == NULL)
		return(TRUE);

	getSizePart(font, buff, ctrlBlk->showSizeInPixels);
	if (strcmp(buff, ctrlBlk->sel3) == 0)
		return(TRUE);
	else
		return(FALSE);
}


/*	returns TRUE if the font portion of the font matches the currently
	selected font */

static int	fontMatch(xfselControlBlkType *ctrlBlk, char *font)
{
	char	buff[TEMP_BUF_SIZE];

	if (ctrlBlk->sel1 == NULL)
		return(TRUE);

	getFontPart(font, buff);
	if (strcmp(buff, ctrlBlk->sel1) == 0)
		return(TRUE);
	else
		return(FALSE);
}


/*	inserts a string into correct sorted position in a list */

static void	addItemToList(char **buf, char *item, int *count)
{
	int	i, j;

 	if (*count == MAX_ENTRIES_IN_LIST)
	{
		fprintf(stderr, "Trying to add more than MAX_ENTRIES_IN_LIST ");
		fprintf(stderr, "(%d) entries to array\n", MAX_ENTRIES_IN_LIST);
		return;
	}	

	for (i = 0; i < *count; i++)
	{
		if (strcmp(buf[i], item) == 0)
			return;
		if (strcmp(buf[i], item) > 0)
			break;
	}

	for (j = *count; j > i; j--)
		buf[j] = buf[j-1];
	buf[i] = XtMalloc(strlen(item) + 1);
	strcpy(buf[i], item);
	(*count)++;
}


/*	given a font name this function returns the part used in the first 
	scroll list */

static void	getFontPart(char *font, char *buff1)
{
	char	buff2[TEMP_BUF_SIZE], buff3[TEMP_BUF_SIZE];
	char	buff4[TEMP_BUF_SIZE];

	getStringComponent(font, 2, buff1);
	getStringComponent(font, 1, buff2);

	sprintf(buff3, "%s (%s", buff1, buff2);

	getStringComponent(font, 13, buff1);
	getStringComponent(font, 14, buff4);

	if (((strncmp(buff1, "iso8859", 7) == 0) || 
	     (strncmp(buff1, "ISO8859", 7) == 0)) && (strcmp(buff4, "1") == 0))
		sprintf(buff1, "%s)", buff3);
	else
	{
		sprintf(buff2, "%s, %s,", buff3, buff1);
		sprintf(buff1, "%s %s)", buff2, buff4);
	}
}


/*	given a font name this function returns the part used in the second 
	scroll list */

static void	getStylePart(char *font, char *buff1)
{
	char	buff2[TEMP_BUF_SIZE], buff3[TEMP_BUF_SIZE];

	getStringComponent(font, 3, buff3);
	getStringComponent(font, 5, buff2);

	if ((strcmp(buff2, "normal") != 0) && (strcmp(buff2, "Normal") != 0) &&
	    (strcmp(buff2, "NORMAL") != 0))
		sprintf(buff1, "%s %s", buff3, buff2);
	else
		strcpy(buff1, buff3);

	getStringComponent(font, 6, buff2);

	if (buff2[0] != '\0')
		sprintf(buff3, "%s %s", buff1, buff2);
	else
		strcpy(buff3, buff1);

	getStringComponent(font, 4, buff2);

	if ((strcmp(buff2, "o") == 0) || (strcmp(buff2, "O") == 0))
		sprintf(buff1, "%s oblique", buff3);
	else if ((strcmp(buff2, "i") == 0) || (strcmp(buff2, "I") == 0))
		sprintf(buff1, "%s italic", buff3);

	if (strcmp(buff1, " ") == 0)
		strcpy(buff1, "-");
}


/*	given a font name this function returns the part used in the third 
	scroll list */

static void	getSizePart(char *font, char *buff1, int inPixels)
{
	int	size;
	float	temp;

	if (inPixels)
	{
		getStringComponent(font, 7, buff1);
		size = atoi(buff1);
		sprintf(buff1, "%2d", size);
	}
	else
	{
		getStringComponent(font, 8, buff1);
		size = atoi(buff1);
		temp = (float)size / 10.0;
		size = temp;
		if (buff1[strlen(buff1) - 1] == '0')
			sprintf(buff1, "%2d", size);
		else
			sprintf(buff1, "%4.1f", temp);
	}
}


/*	Call back functions start from here - suffix Action in the function name
	is for the callback function for the corresponding widget */

static void	propFontToggleAction(Widget widget, 
				     xfselControlBlkType *ctrlBlk, 
				     XmToggleButtonCallbackStruct *call_data)
{
	int		n;
	Arg		args[2];
	XmString	str;

	if (call_data->reason == XmCR_VALUE_CHANGED)
	{
		if (ctrlBlk->showPropFonts == PREF_FIXED)
			ctrlBlk->showPropFonts = PREF_PROP;
		else
			ctrlBlk->showPropFonts = PREF_FIXED;

		if (ctrlBlk->sel1 != NULL)
			XtFree(ctrlBlk->sel1);
		ctrlBlk->sel1 = NULL;

		if (ctrlBlk->sel2 != NULL)
			XtFree(ctrlBlk->sel2);
		ctrlBlk->sel2 = NULL;

		if (ctrlBlk->sel3 != NULL)
			XtFree(ctrlBlk->sel3);
		ctrlBlk->sel3 = NULL;

		setupScrollLists(NONE, *ctrlBlk);

		n = 0;
		str = XmStringCreate("", XmSTRING_DEFAULT_CHARSET);
		XtSetArg(args[n], XmNlabelString, str); n++;
		XtSetValues(ctrlBlk->dispLabel, args, n);
		XmTextSetString(ctrlBlk->fontNameField, "");
		XmStringFree(str);
	}
}


static void	sizeToggleAction(Widget widget,
				 xfselControlBlkType *ctrlBlk, 
				 XmToggleButtonCallbackStruct *call_data)
{
	int		i, makeSelection;
	char		newSize[10];
	XmString	str;

	if (call_data->reason == XmCR_VALUE_CHANGED)
	{
		makeSelection = (ctrlBlk->sel3 != NULL);

		for (i = 0; (makeSelection) && (i < ctrlBlk->numFonts); i++)
			if ((fontMatch(ctrlBlk, ctrlBlk->fontData[i])) &&
			    (styleMatch(ctrlBlk, ctrlBlk->fontData[i])) &&
			    (sizeMatch(ctrlBlk, ctrlBlk->fontData[i])))
			{
				getSizePart(ctrlBlk->fontData[i], newSize, 
						    !ctrlBlk->showSizeInPixels);
				break;
			}
			    
		if (ctrlBlk->showSizeInPixels)
			ctrlBlk->showSizeInPixels = FALSE;
		else
			ctrlBlk->showSizeInPixels = TRUE;

		if (ctrlBlk->sel3 != NULL)
			XtFree(ctrlBlk->sel3);

		ctrlBlk->sel3 = NULL;
		setupScrollLists(NONE, *ctrlBlk);

		if (makeSelection)
		{
			str = XmStringCreate(newSize, XmSTRING_DEFAULT_CHARSET);
			XmListSelectItem(ctrlBlk->sizeList, str, TRUE);
			XmListSetBottomItem(ctrlBlk->sizeList, str);
			XmStringFree(str);
		}
	}
}


static void	fontAction(Widget widget, xfselControlBlkType *ctrlBlk, 
				 XmListCallbackStruct *call_data)
{
	char	*sel;

	XmStringGetLtoR(call_data->item, XmSTRING_DEFAULT_CHARSET, &sel);

	if (ctrlBlk->sel1 == NULL)
	{
		ctrlBlk->sel1 = XtMalloc(strlen(sel) + 1);
		strcpy(ctrlBlk->sel1, sel);
	}
	else
	{
		if (strcmp(ctrlBlk->sel1, sel) == 0)
		{			/* Unselecting current selection */
			XtFree(ctrlBlk->sel1);
			ctrlBlk->sel1 = NULL;
			XmListDeselectItem(widget, call_data->item);
		}
		else
		{
			XtFree(ctrlBlk->sel1);
			ctrlBlk->sel1 = XtMalloc(strlen(sel) + 1);
			strcpy(ctrlBlk->sel1, sel);
		}
	}

	XtFree(sel);
	setupScrollLists(FONT, *ctrlBlk);
	if ((ctrlBlk->sel1 != NULL) && (ctrlBlk->sel2 != NULL) && 
	    (ctrlBlk->sel3 != NULL))
		choiceMade(ctrlBlk);
	else
	{
		int		n;
		XmString	str;
		Arg		args[2];

		n = 0;
		str = XmStringCreate("", XmSTRING_DEFAULT_CHARSET);
		XtSetArg(args[n], XmNlabelString, str); n++;
		XtSetValues(ctrlBlk->dispLabel, args, n);
		XmTextSetString(ctrlBlk->fontNameField, "");
		XmStringFree(str);
	}
}


static void	styleAction(Widget widget, xfselControlBlkType *ctrlBlk, 
				 XmListCallbackStruct *call_data)
{
	char	*sel;

	XmStringGetLtoR(call_data->item, XmSTRING_DEFAULT_CHARSET, &sel);

	if (ctrlBlk->sel2 == NULL)
	{
		ctrlBlk->sel2 = XtMalloc(strlen(sel) + 1);
		strcpy(ctrlBlk->sel2, sel);
	}
	else
	{
		if (strcmp(ctrlBlk->sel2, sel) == 0)
		{			/* unselecting current selection */
			XtFree(ctrlBlk->sel2);
			ctrlBlk->sel2 = NULL;
			XmListDeselectItem(widget, call_data->item);
		}
		else
		{
			XtFree(ctrlBlk->sel2);
			ctrlBlk->sel2 = XtMalloc(strlen(sel) + 1);
			strcpy(ctrlBlk->sel2, sel);
		}
	}

	XtFree(sel);
	setupScrollLists(STYLE, *ctrlBlk);
	if ((ctrlBlk->sel1 != NULL) && (ctrlBlk->sel2 != NULL) && 
	    (ctrlBlk->sel3 != NULL))
		choiceMade(ctrlBlk);
	else
	{
		int		n;
		XmString	str;
		Arg		args[2];

		n = 0;
		str = XmStringCreate("", XmSTRING_DEFAULT_CHARSET);
		XtSetArg(args[n], XmNlabelString, str); n++;
		XtSetValues(ctrlBlk->dispLabel, args, n);
		XmTextSetString(ctrlBlk->fontNameField, "");
		XmStringFree(str);
	}
}


static void	sizeAction(Widget widget, xfselControlBlkType *ctrlBlk, 
				 XmListCallbackStruct *call_data)
{
	char	*sel;

	XmStringGetLtoR(call_data->item, XmSTRING_DEFAULT_CHARSET, &sel);

	if (ctrlBlk->sel3 == NULL)
	{
		ctrlBlk->sel3 = XtMalloc(strlen(sel) + 1);
		strcpy(ctrlBlk->sel3, sel);
	}
	else
	{
		if (strcmp(ctrlBlk->sel3, sel) == 0)
		{			/* unselecting current selection */
			XtFree(ctrlBlk->sel3);
			ctrlBlk->sel3 = NULL;
			XmListDeselectItem(widget, call_data->item);
		}
		else
		{
			XtFree(ctrlBlk->sel3);
			ctrlBlk->sel3 = XtMalloc(strlen(sel) + 1);
			strcpy(ctrlBlk->sel3, sel);
		}
	}

	XtFree(sel);
	setupScrollLists(SIZE, *ctrlBlk);
	if ((ctrlBlk->sel1 != NULL) && (ctrlBlk->sel2 != NULL) && 
	    (ctrlBlk->sel3 != NULL))
		choiceMade(ctrlBlk);
	else
	{
		int		n;
		XmString	str;
		Arg		args[2];

		n = 0;
		str = XmStringCreate("", XmSTRING_DEFAULT_CHARSET);
		XtSetArg(args[n], XmNlabelString, str); n++;
		XtSetValues(ctrlBlk->dispLabel, args, n);
		XmTextSetString(ctrlBlk->fontNameField, "");
		XmStringFree(str);
	}
}

/*	function called when all three choices have been made; sets up font
	name and displays sample font */

static void	choiceMade(xfselControlBlkType *ctrlBlk)
{
	int	i;

	if (ctrlBlk->fontName != NULL)
		XtFree(ctrlBlk->fontName);
	ctrlBlk->fontName = NULL;

	for (i = 0; i < ctrlBlk->numFonts; i++)
	{
		if ((fontMatch(ctrlBlk, ctrlBlk->fontData[i])) &&
		    (styleMatch(ctrlBlk, ctrlBlk->fontData[i])) &&
		    (sizeMatch (ctrlBlk, ctrlBlk->fontData[i])))
		{
			ctrlBlk->fontName = XtMalloc(
					      strlen(ctrlBlk->fontData[i]) + 1);
			strcpy(ctrlBlk->fontName, ctrlBlk->fontData[i]);
			break;
		}
	}

	if (ctrlBlk->fontName != NULL)
	{
		XmTextSetString(ctrlBlk->fontNameField, ctrlBlk->fontName);
		dispSample(ctrlBlk);
	}
 	else
		DialogF (DF_ERR, ctrlBlk->form, 1, "Invalid Font Specification",
								     "Dismiss");
}


/*	loads selected font and displays sample text in that font */

static void	dispSample(xfselControlBlkType *ctrlBlk)
{
	Arg		args[2];
	int			n;
	XFontStruct		*font;
	XmFontList		fontList;
	Display			*display;
	XmString		dispStr;

	display		= XtDisplay(ctrlBlk->form);
	font		= XLoadQueryFont(display, ctrlBlk->fontName);
	fontList	= XmFontListCreate(font, XmSTRING_DEFAULT_CHARSET);

	n = 0;
	dispStr = XmStringCreate(
	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789",
	    XmSTRING_DEFAULT_CHARSET);
	XtSetArg(args[n], XmNlabelString, dispStr); n++;
	XtSetArg(args[n], XmNfontList, fontList); n++;

	XtSetValues(ctrlBlk->dispLabel, args, n);
	XmStringFree(dispStr);

	if (ctrlBlk->oldFont != NULL)
	{
 		XFreeFont(display, ctrlBlk->oldFont);
		XmFontListFree(ctrlBlk->oldFontList);
	}
	ctrlBlk->oldFont	= font;
	ctrlBlk->oldFontList	= fontList;
}


static void	cancelAction(Widget widget, xfselControlBlkType *ctrlBlk,
				 XmListCallbackStruct *call_data)
{
	if (ctrlBlk->sel1 != NULL) 
		XtFree(ctrlBlk->sel1);
	if (ctrlBlk->sel2 != NULL) 
		XtFree(ctrlBlk->sel2);
	if (ctrlBlk->sel3 != NULL) 
		XtFree(ctrlBlk->sel3);
	if (ctrlBlk->fontName != NULL) 
		XtFree(ctrlBlk->fontName);

	ctrlBlk->fontName = NULL;
	XFreeFontNames(ctrlBlk->fontData);

	ctrlBlk->exitFlag = TRUE;
}


static void	okAction(Widget widget, xfselControlBlkType *ctrlBlk,
				 XmPushButtonCallbackStruct *call_data)
{
	char	*fontPattern;
	char	**fontName;
	int	i;

	fontPattern = XmTextGetString(ctrlBlk->fontNameField);
	fontName    = XListFonts(XtDisplay(ctrlBlk->form), fontPattern, 1, &i);
	XtFree(fontPattern);
	
	if (i != 1)
	{
		DialogF (DF_ERR, ctrlBlk->okButton, 1, 
				"Invalid Font Specification", "Dismiss");
		XFreeFontNames(fontName);
	}
	else
	{
		if (ctrlBlk->fontName != NULL)
			XtFree(ctrlBlk->fontName);
		ctrlBlk->fontName = XtMalloc(strlen(fontName[0]) + 1);
		strcpy(ctrlBlk->fontName, fontName[0]);

		if (ctrlBlk->sel1 != NULL) 
			XtFree(ctrlBlk->sel1);
		if (ctrlBlk->sel2 != NULL) 
			XtFree(ctrlBlk->sel2);
		if (ctrlBlk->sel3 != NULL) 
			XtFree(ctrlBlk->sel3);
	
		XFreeFontNames(fontName);
		XFreeFontNames(ctrlBlk->fontData);

		ctrlBlk->exitFlag = TRUE;
	}
}


/*	if current font is passed as an argument then this function is
	invoked and sets up initial entries */

static void	startupFont(xfselControlBlkType *ctrlBlk, char *font)
{
	int		i;
	char		**fontName;
	char		part[TEMP_BUF_SIZE];
	XmString	str;

	fontName = XListFonts(XtDisplay(ctrlBlk->form), font, 1, &i);

	if (i == 0)
	{			/*	invalid font passed in at startup */
		XFreeFontNames(fontName);
		return;
	}

	ctrlBlk->fontName = XtMalloc(strlen(fontName[0]) + 1);
	strcpy(ctrlBlk->fontName, fontName[0]);

	getFontPart(fontName[0], part);
	XFreeFontNames(fontName);
	str = XmStringCreate(part, XmSTRING_DEFAULT_CHARSET);
	XmListSetBottomItem(ctrlBlk->fontList, str);
	XmListSelectItem(ctrlBlk->fontList, str, TRUE);
	XmListSelectItem(ctrlBlk->fontList, str, TRUE);
	XmStringFree(str);

	dispSample(ctrlBlk);
	XmTextSetString(ctrlBlk->fontNameField, ctrlBlk->fontName);
}


/*	hacked code to move initial input focus to first scroll list and at the 
	same time have the OK button as the default button */

static void 	setFocus(Widget w, xfselControlBlkType *ctrlBlk, XEvent *event, 
						Boolean *continueToDispatch)
{
	int	n;
	Arg	args[2];

	*continueToDispatch = TRUE;

	n = 0;
	XtSetArg(args[n], XmNdefaultButton, ctrlBlk->okButton); n++;
	XtSetValues(ctrlBlk->form, args, n);
}


/*	finds the name of the biggest font less than the given limit 
	MAX_DISPLAY_SIZE used to set up the initial height of the display widget
*/

static void	FindBigFont(xfselControlBlkType *ctrlBlk, char *bigFont)
{
	int 	i, maxSize, index, size;
	char	sizeStr[10];

	for (i = 0, maxSize = 0; i < ctrlBlk->numFonts; i++)
	{
		getStringComponent(ctrlBlk->fontData[i], 7, sizeStr);
		size = atoi(sizeStr);
		if ((size > maxSize) && (size < MAX_DISPLAY_SIZE))
		{
			index   = i;
			maxSize = size;
		}
	}
	strcpy(bigFont, ctrlBlk->fontData[index]);
}
