/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
/****************************************************************
 * File: fonts.c
 * Date: 03/13/91
 *
 * Description:
 *   This file contains the UI popup for font selection.
 *
 * Revisions:
 ****************************************************************/
#include <Xm/Xm.h>
#include <Xm/ScrolledW.h>
#include <Xm/Label.h>
#include <Xm/ToggleB.h>
#include <Xm/RowColumn.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <malloc.h>
#include <string.h>
#include <VList.h>

#include "VtP.h"

#define FMLY_ATTR 0
#define WGHT_ATTR 1
#define SLANT_ATTR 2
#define PXLSZ_ATTR 3

extern XtPointer ToggleGetCurrent();

/****************************************************************
 *                     Private Functions
 ****************************************************************/

/* Ignores font info field */
PRIVATE
char *
munch_field(s)
	char *s;
{
	while(*s != '-' && *s)
	{
		s++;
	}
	return *s ? (s + 1) : s;
}

/* Reads in font info field and places it into a buffer */
PRIVATE
char *
munch_and_copy(s,p)
	char *s;
	char **p;
{
	int n = 0;
	while(s[n] != '-' && s[n])
	{
		n++;
	}
	*p = malloc((unsigned)(n + 1));
	strncpy(*p,s,n);
	(*p)[n] = 0;
	return s[n] ? (s + n + 1) : (s + n);
}

/* Reads in font info field and places it into a buffer */
PRIVATE
char *
munch_and_copy2(s,p)
	char *s;
	char *p;
{
	int n = 0;
	while(s[n] != '-' && s[n])
	{
		*p++ = s[n++];
	}
	*p = 0;
	return s[n] ? (s + n + 1) : (s + n);
}

/* Compares 2 integer fields */
PRIVATE
int
cmp(a,b)
	char *a;
	char *b;
{
	return atoi(a) - atoi(b);
}

/* Quick sorts the fonts on a column */
PRIVATE
void
fqsort(names,fix,f,l,cmpop)
	Font_Desc *names;
	int (*cmpop)();
{
	if (f < l)
	{
		int i = f;
		int j = l;
		char *a = names[(f + l) / 2]->attributes[fix];
		while(i <= j)
		{
			while(j >= f && cmpop(names[j]->attributes[fix],a) >= 0) j--;
			while(i <= l && cmpop(names[i]->attributes[fix],a) <  0) i++;
			if (i < j)
			{
				Font_Desc t = names[i];
				names[i] = names[j];
				names[j] = t;
				i++;
				j--;
			}
		}
		{
			int k;
			for(k = j + 1; k <= l; k++)
			{
				if (!cmpop(names[k]->attributes[fix],a))
				{
					Font_Desc t = names[j + 1];
					names[j + 1] = names[k];
					names[k] = t;
					j++;
				}
			}
		}
		fqsort(names,fix,f,i - 1,cmpop);
		fqsort(names,fix,j + 1,l,cmpop);
	}
}

/* Sets to sensitive all attributes that are in the list, insensitive all that are not */
PRIVATE
void
find_font(w,fonts,nfonts,fix,op)
	Widget w;
	Font_Desc *fonts;
	int (*op)();
{
	Widget *children = ((CompositeWidget)w)->composite.children;
	Cardinal nchildren = ((CompositeWidget)w)->composite.num_children;
	int i;
	for(i = 0; i < nchildren; i++)
	{
		Widget child = children[i];
		char *label;
		int low = 0;
		int high = nfonts - 1;
		Boolean sens;
		int found = False;
		XtVaGetValues(child,
			XmNuserData,&label,
			XmNsensitive,&sens,
			NULL);
		while(low <= high)
		{
			int mid = (low + high) / 2;
			int cm = op(label,fonts[mid]->attributes[fix]);
			if (cm < 0)
			{
				high = mid - 1;
			}
			else if (cm > 0)
			{
				low = mid + 1;
			}
			else
			{
				found = True;
				break;
			}
		}
		XtVaSetValues(child,
			XmNsensitive,found,
			NULL);
	}
}

/* Compares 2 fields */
PRIVATE
int
cmp_field(a,b)
	char *a;
	char *b;
{
	return (!strcmp(a,"*") || !strcmp(a,b));
}

/* Finds all fonts that match a pattern and sets atributes that match that pattern to sensitive */
PRIVATE
int
set_sens(chooser,w,fix,i1,f1,i2,f2,i3,f3,op)
	FontChooser *chooser;
	Widget w;
	char *f1;
	char *f2;
	char *f3;
	int (*op)();
{
	int i;
	int j = 0;
	for(i = 0; i < chooser->nfonts; i++)
	{
		if (
			cmp_field(f1,chooser->fonts[i]->attributes[i1]) &&
			cmp_field(f2,chooser->fonts[i]->attributes[i2]) &&
			cmp_field(f3,chooser->fonts[i]->attributes[i3]))
		{
			Font_Desc t = chooser->fonts[i];
			chooser->fonts[i] = chooser->fonts[j];
			chooser->fonts[j++] = t;
		}
	}
	fqsort(chooser->fonts,fix,0,j - 1,op);
	find_font(w,chooser->fonts,j,fix,op);
	return j;
}

/* Sets to sensitivie only valid font families */
PRIVATE
void
validate_fmlys(chooser,w,pxlsz,wght,slant)
	FontChooser *chooser;
	Widget w;
	char *pxlsz;
	char *wght;
	char *slant;
{
	set_sens(chooser,w,FMLY_ATTR,WGHT_ATTR,wght,SLANT_ATTR,slant,PXLSZ_ATTR,pxlsz,strcmp);
}

/* Sets to sensitivie only valid font families */
PRIVATE
void
validate_wghts(chooser,w,pxlsz,fmly,slant)
	FontChooser *chooser;
	Widget w;
	char *pxlsz;
	char *fmly;
	char *slant;
{
	set_sens(chooser,w,WGHT_ATTR,FMLY_ATTR,fmly,SLANT_ATTR,slant,PXLSZ_ATTR,pxlsz,strcmp);
}

/* Sets to sensitivie only valid font families */
PRIVATE
void
validate_slants(chooser,w,pxlsz,fmly,wght)
	FontChooser *chooser;
	Widget w;
	char *pxlsz;
	char *fmly;
	char *wght;
{
	set_sens(chooser,w,SLANT_ATTR,FMLY_ATTR,fmly,WGHT_ATTR,wght,PXLSZ_ATTR,pxlsz,strcmp);
}

/* Sets to sensitivie only valid font sizes */
PRIVATE
void
validate_pxlszs(chooser,w,fmly,wght,slant)
	FontChooser *chooser;
	Widget w;
	char *fmly;
	char *wght;
	char *slant;
{
	set_sens(chooser,w,PXLSZ_ATTR,FMLY_ATTR,fmly,WGHT_ATTR,wght,SLANT_ATTR,slant,cmp);
}

/* Gets the currently selected attribute */
PRIVATE
char *
get_curr_tog(list)
	Widget list;
{
	char *val = ToggleGetCurrent(list);
	return (val == NULL) ? "*" : val;
}

PRIVATE
void
new_font(chooser,fmly,wght,slant,pxlsz)
	FontChooser *chooser;
	char *fmly;
	char *wght;
	char *slant;
	char *pxlsz;
{
	char font_name[256];
	sprintf(font_name,"-*-%s-%s-%c-*-*-%s-*-*-*-*-*-*-*",fmly,wght,slant[0],pxlsz);
	chooser->cb(chooser->ca,font_name);
}

/*ARGSUSED*/
PRIVATE
void
sel_pxlsz_revalidate(wd,chooser)
	Widget wd;
	FontChooser *chooser;
{
	char *fmly = get_curr_tog(chooser->fmly_list);
	char *pxlsz = get_curr_tog(chooser->pxlsz_list);
	char *wght = get_curr_tog(chooser->wght_list);
	char *slant = get_curr_tog(chooser->slant_list);
	validate_fmlys(chooser,chooser->fmly_list,pxlsz,wght,slant);
	validate_wghts(chooser,chooser->wght_list,pxlsz,fmly,slant);
	validate_slants(chooser,chooser->slant_list,pxlsz,fmly,wght);
	new_font(chooser,fmly,wght,slant,pxlsz);
}

/*ARGSUSED*/
PRIVATE
void
sel_fmly_revalidate(wd,chooser)
	Widget wd;
	FontChooser *chooser;
{
	char *fmly = get_curr_tog(chooser->fmly_list);
	char *pxlsz = get_curr_tog(chooser->pxlsz_list);
	char *wght = get_curr_tog(chooser->wght_list);
	char *slant = get_curr_tog(chooser->slant_list);
	validate_wghts(chooser,chooser->wght_list,pxlsz,fmly,slant);
	validate_slants(chooser,chooser->slant_list,pxlsz,fmly,wght);
	validate_pxlszs(chooser,chooser->pxlsz_list,fmly,wght,slant);
	new_font(chooser,fmly,wght,slant,pxlsz);
}

/*ARGSUSED*/
PRIVATE
void
sel_wght_revalidate(wd,chooser)
	Widget wd;
	FontChooser *chooser;
{
	char *fmly = get_curr_tog(chooser->fmly_list);
	char *pxlsz = get_curr_tog(chooser->pxlsz_list);
	char *wght = get_curr_tog(chooser->wght_list);
	char *slant = get_curr_tog(chooser->slant_list);
	validate_fmlys(chooser,chooser->fmly_list,pxlsz,wght,slant);
	validate_slants(chooser,chooser->slant_list,pxlsz,fmly,wght);
	validate_pxlszs(chooser,chooser->pxlsz_list,fmly,wght,slant);
	new_font(chooser,fmly,wght,slant,pxlsz);
}

/*ARGSUSED*/
PRIVATE
void
sel_slant_revalidate(wd,chooser)
	Widget wd;
	FontChooser *chooser;
{
	char *fmly = get_curr_tog(chooser->fmly_list);
	char *pxlsz = get_curr_tog(chooser->pxlsz_list);
	char *wght = get_curr_tog(chooser->wght_list);
	char *slant = get_curr_tog(chooser->slant_list);
	validate_fmlys(chooser,chooser->fmly_list,pxlsz,wght,slant);
	validate_wghts(chooser,chooser->wght_list,pxlsz,fmly,slant);
	validate_pxlszs(chooser,chooser->pxlsz_list,fmly,wght,slant);
	new_font(chooser,fmly,wght,slant,pxlsz);
}

/* Creates a list for a column */
PRIVATE
void
get_col_list(fix,cmpop,w,chooser,cb,match)
	int (*cmpop)();
	Widget w;
	FontChooser *chooser;
	void (*cb)();
	char *match;
{
	char *last = NULL;
	int i;
	Widget lwidget = NULL;
	XmString xm_str;

	fqsort(chooser->fonts,fix,0,chooser->nfonts - 1,cmpop);
	for(i = 0; i < chooser->nfonts; i++)
	{
		char *fld = chooser->fonts[i]->attributes[fix];
		if (!i || strcmp(fld,last))
		{
			last = fld;
			xm_str = XmStringCreateLtoR(fld,XmSTRING_DEFAULT_CHARSET);
			lwidget = XtVaCreateWidget("",xmToggleButtonWidgetClass,w,
				XmNlabelString,xm_str,
				XmNuserData,fld,
				XmNborderWidth,0,
				XmNset,!strcmp(match,fld),
				NULL);
			XmStringFree(xm_str);
			XtAddCallback(lwidget,XmNvalueChangedCallback,cb,(XtPointer)chooser);
		}
	}
}

/*ARGSUSED*/
PRIVATE
void
destroy_chooser(wd,chooser)
	Widget wd;
	FontChooser *chooser;
{
	int i;
	for(i = 0; i < chooser->nfonts; i++)
	{
		Font_Desc f = chooser->fonts[i];
		free(f->attributes[FMLY_ATTR]);
		free(f->attributes[WGHT_ATTR]);
		free(f->attributes[SLANT_ATTR]);
		free(f->attributes[PXLSZ_ATTR]);
		free((char *)f);
	}
	free((char *)chooser->fonts);
}

PRIVATE
char *
convert_slant(s)
	char *s;
{
	if (!strcmp(s,"i"))
	{
		return "italic";
	}
	else if (!strcmp(s,"o"))
	{
		return "oblique";
	}
	else if (!strcmp(s,"r"))
	{
		return "regular";
	}
	else
	{
		return s;
	}
}

PRIVATE
void
set_list(w,string,compare)
	Widget w;
	char *string;
	int (*compare)();
{
	Widget *children = ((CompositeWidget)w)->composite.children;
	Cardinal nchildren = ((CompositeWidget)w)->composite.num_children;
	int i;

	for(i = 0; i < nchildren; i++)
	{
		Widget child = children[i];
		char *label;
		int cm;

		XtVaGetValues(child,
			XmNuserData,&label,
			NULL);

		cm = compare(label,string);

		if (cm == 0)
		{
			XtVaSetValues(child,
				XmNset,True,
				NULL);
		}
		else
		{
			XtVaSetValues(child,
				XmNset,False,
				NULL);
		}
	}
}

/****************************************************************
 *                     Public Functions
 ****************************************************************/

PUBLIC
Widget
VtFontPanel(parent,chooser,cb,ca)
	Widget parent;
	FontChooser *chooser;
	int (*cb)();
	XtPointer ca;
{
	int nfonts;
	char **fonts;
	char orig_fmly[256];
	char orig_wght[256];
	char orig_slant[256];
	char orig_pxlsz[256];

	Widget sw = XtVaCreateWidget("FontViewport",xmScrolledWindowWidgetClass,parent,
		XmNscrollingPolicy,XmAUTOMATIC,
		XmNvisualPolicy,XmVARIABLE,
		XtNstretchHoriz,True,
		XtNstretchVert,True,
		NULL);
	Widget frm = XtVaCreateManagedWidget("Properties",vListWidgetClass,sw,
		XtNstretchHoriz,True,
		XtNstretchVert,True,
		NULL);
	XmString xm_str = XmStringCreateLtoR("Font Selection",XmSTRING_DEFAULT_CHARSET);
	Widget label = XtVaCreateManagedWidget("FontPropLabel",xmLabelWidgetClass,frm,
		XmNlabelString,xm_str,
		XtNhAlign,VL_CENTER,
		NULL);
	Widget frm2 = XtVaCreateManagedWidget("Lists",vListWidgetClass,frm,
		XtNborderWidth,0,
		XtNnumRows,1,
		NULL);
	Widget fmly_list = XtVaCreateManagedWidget("FontNameList",xmRowColumnWidgetClass,frm2,
		XmNradioBehavior,True,
		NULL);
	Widget pxlsz_list = XtVaCreateManagedWidget("PointList",xmRowColumnWidgetClass,frm2,
		XmNradioBehavior,True,
		NULL);
	Widget weights = XtVaCreateManagedWidget("Weights",xmRowColumnWidgetClass,frm2,
		XmNradioBehavior,True,
		NULL);
	Widget slants = XtVaCreateManagedWidget("Slants",xmRowColumnWidgetClass,frm2,
		XmNradioBehavior,True,
		NULL);
	XmStringFree(xm_str);

	/* obtain a list of available fonts */
	fonts = XListFonts(XtDisplay(parent),"-adobe-*-*-*-*-*-*-*-*-*-*-*-*-*",1000,&nfonts);

	chooser->fmly_list = fmly_list;
	chooser->pxlsz_list = pxlsz_list;
	chooser->wght_list = weights;
	chooser->slant_list = slants;
	chooser->fonts = (Font_Desc *)malloc((unsigned)(nfonts * sizeof(Font_Desc)));
	chooser->cb = cb;
	chooser->ca = ca;
	{
		int i;
		int j = 0;
		for(i = 0; i < nfonts; i++)
		{
			Font_Desc f = (Font_Desc)malloc(sizeof *f);
			char **attr = f->attributes;
			munch_and_copy(
			munch_field(
			munch_field(
			munch_and_copy(
			munch_and_copy(
			munch_and_copy(
			munch_field(fonts[i] + 1),
				attr + FMLY_ATTR),
				attr + WGHT_ATTR),
				attr + SLANT_ATTR))),
				attr + PXLSZ_ATTR);
			{
				char *s = convert_slant(attr[SLANT_ATTR]);
				char *t = malloc((unsigned)(strlen(s) + 1));
				strcpy(t,s);
				free(attr[SLANT_ATTR]);
				attr[SLANT_ATTR] = t;
			}
			if (!j ||
					strcmp(chooser->fonts[j - 1]->attributes[FMLY_ATTR],attr[FMLY_ATTR]) ||
					strcmp(chooser->fonts[j - 1]->attributes[WGHT_ATTR],attr[WGHT_ATTR]) ||
					strcmp(chooser->fonts[j - 1]->attributes[SLANT_ATTR],attr[SLANT_ATTR]) ||
					strcmp(chooser->fonts[j - 1]->attributes[PXLSZ_ATTR],attr[PXLSZ_ATTR]))
			{
				chooser->fonts[j++] = f;
			}
			else
			{
				free(f->attributes[FMLY_ATTR]);
				free(f->attributes[WGHT_ATTR]);
				free(f->attributes[SLANT_ATTR]);
				free(f->attributes[PXLSZ_ATTR]);
				free((char *)f);
			}
		}
		chooser->nfonts = j;
	}
	XFreeFontNames(fonts);
	get_col_list(FMLY_ATTR,strcmp,fmly_list,chooser,sel_fmly_revalidate,orig_fmly);
	get_col_list(PXLSZ_ATTR,cmp,pxlsz_list,chooser,sel_pxlsz_revalidate,orig_pxlsz);
	get_col_list(WGHT_ATTR,strcmp,weights,chooser,sel_wght_revalidate,orig_wght);
	get_col_list(SLANT_ATTR,strcmp,slants,chooser,sel_slant_revalidate,orig_slant);
	{
		char *fmly = get_curr_tog(chooser->fmly_list);
		char *pxlsz = get_curr_tog(chooser->pxlsz_list);
		char *wght = get_curr_tog(chooser->wght_list);
		char *slant = get_curr_tog(chooser->slant_list);
		validate_fmlys(chooser,chooser->fmly_list,pxlsz,wght,slant);
		validate_wghts(chooser,chooser->wght_list,pxlsz,fmly,slant);
		validate_slants(chooser,chooser->slant_list,pxlsz,fmly,wght);
		validate_pxlszs(chooser,chooser->pxlsz_list,fmly,wght,slant);
	}

	VtManageChildren(frm);

	return sw;
}

PUBLIC
void
VtSetFont(chooser,font)
	FontChooser *chooser;
	char *font;
{
	char family[256];
	char weight[256];
	char stmp[256];
	char point[256];
	char *slant;

	if (font == NULL)
	{
		return;
	}

	munch_and_copy2(
	munch_field(
	munch_field(
	munch_and_copy2(
	munch_and_copy2(
	munch_and_copy2(
	munch_field(font + 1),
		family),
		weight),
		stmp))),
		point);

	slant = convert_slant(stmp);

	set_list(chooser->fmly_list,family,strcmp);
	set_list(chooser->wght_list,weight,strcmp);
	set_list(chooser->slant_list,slant,strcmp);
	set_list(chooser->pxlsz_list,point,cmp);
}
