1/*
2
3Copyright (c) 1985-1989  X Consortium
4
5Permission is hereby granted, free of charge, to any person obtaining
6a copy of this software and associated documentation files (the
7"Software"), to deal in the Software without restriction, including
8without limitation the rights to use, copy, modify, merge, publish,
9distribute, sublicense, and/or sell copies of the Software, and to
10permit persons to whom the Software is furnished to do so, subject to
11the following conditions:
12
13The above copyright notice and this permission notice shall be included
14in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of the X Consortium shall
25not be used in advertising or otherwise to promote the sale, use or
26other dealings in this Software without prior written authorization
27from the X Consortium.
28
29Author:	Ralph R. Swick, DEC/MIT Project Athena
30	one weekend in November, 1989
31Modified: Mark Leisher <mleisher@crl.nmsu.edu> to deal with UCS sample text.
32*/
33
34/*
35 * Copyright (c) 2000, 2022, Oracle and/or its affiliates.
36 *
37 * Permission is hereby granted, free of charge, to any person obtaining a
38 * copy of this software and associated documentation files (the "Software"),
39 * to deal in the Software without restriction, including without limitation
40 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
41 * and/or sell copies of the Software, and to permit persons to whom the
42 * Software is furnished to do so, subject to the following conditions:
43 *
44 * The above copyright notice and this permission notice (including the next
45 * paragraph) shall be included in all copies or substantial portions of the
46 * Software.
47 *
48 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
51 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
53 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
54 * DEALINGS IN THE SOFTWARE.
55 *
56 * Modifications by Jay Hobson (Sun Microsystems) to internationalize messages
57 */
58
59#ifdef HAVE_CONFIG_H
60# include "config.h"
61#endif
62
63#include <stdio.h>
64#include <stdlib.h>
65#include <X11/Intrinsic.h>
66#include <X11/StringDefs.h>
67#include <X11/Xatom.h>
68#include <X11/Xaw/AsciiText.h>
69#include <X11/Xaw/Box.h>
70#include <X11/Xaw/Cardinals.h>
71#include <X11/Xaw/Command.h>
72#include <X11/Xaw/Form.h>
73#include <X11/Xaw/MenuButton.h>
74#include <X11/Xaw/Paned.h>
75#include <X11/Xaw/SimpleMenu.h>
76#include <X11/Xaw/SmeBSB.h>
77#include <X11/Xaw/Toggle.h>
78#include <X11/Xaw/Viewport.h>
79#include <X11/Xmu/Atoms.h>
80#include <X11/Xmu/StdSel.h>
81#include <X11/Xfuncs.h>
82#include <X11/Xlib.h>
83#include "ULabel.h"
84
85#ifdef USE_GETTEXT
86# include <locale.h>	/* setlocale()	*/
87# include <libintl.h>	/* gettext(), textdomain(), etc. */
88#else
89# define gettext(a) (a)
90#endif
91
92#define MIN_APP_DEFAULTS_VERSION 1
93#define FIELD_COUNT 14
94#define DELIM '-'
95
96/* number of font names to parse in each background iteration */
97#ifndef PARSE_QUANTUM
98#define PARSE_QUANTUM 25
99#endif
100
101#define NZ NULL,ZERO
102#define BACKGROUND 10
103
104void GetFontNames(XtPointer closure);
105Boolean Matches(String pattern, String fontName, Boolean fields[], int *maxfields);
106Boolean DoWorkPiece(XtPointer closure);
107void Quit(Widget w, XtPointer closure, XtPointer callData) _X_NORETURN;
108void Reset(Widget w, XtPointer closure, XtPointer callData);
109void OwnSelection(Widget w, XtPointer closure, XtPointer callData);
110void SelectField(Widget w, XtPointer closure, XtPointer callData);
111void ParseFontNames(XtPointer closure);
112void SortFields(XtPointer closure);
113void FixScalables(XtPointer closure);
114void MakeFieldMenu(XtPointer closure);
115void SelectValue(Widget w, XtPointer closure, XtPointer callData);
116void AnyValue(Widget w, XtPointer closure, XtPointer callData);
117void EnableOtherValues(Widget w, XtPointer closure, XtPointer callData);
118void EnableMenu(XtPointer closure);
119void SetCurrentFont(XtPointer closure);
120void QuitAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
121    _X_NORETURN;
122
123static XtActionsRec xfontsel_actions[] = {
124    {"Quit",	    QuitAction}
125};
126
127static Atom wm_delete_window;
128
129Boolean IsXLFDFontName(String fontName);
130
131typedef void (*XtProc)(XtPointer closure);
132
133static struct _appRes {
134    int app_defaults_version;
135    Cursor cursor;
136    String pattern;
137    char *pixelSizeList;
138    char *pointSizeList;
139    Boolean print_on_quit;
140    String sample_text;
141    String sample_text16;
142    String sample_textUCS;
143    Boolean scaled_fonts;
144} AppRes;
145
146#define DEFAULTPATTERN "-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
147
148static XtResource resources[] = {
149    { "cursor", "Cursor", XtRCursor, sizeof(Cursor),
150		XtOffsetOf( struct _appRes, cursor ),
151		XtRImmediate, NULL },
152    { "pattern", "Pattern", XtRString, sizeof(String),
153		XtOffsetOf( struct _appRes, pattern ),
154		XtRString, (XtPointer)DEFAULTPATTERN },
155    { "pixelSizeList", "PixelSizeList", XtRString, sizeof(String),
156                XtOffsetOf( struct _appRes, pixelSizeList ),
157                XtRString, (XtPointer)"" },
158    { "pointSizeList", "PointSizeList", XtRString, sizeof(String),
159                XtOffsetOf( struct _appRes, pointSizeList ),
160                XtRString, (XtPointer)"" },
161    { "printOnQuit", "PrintOnQuit", XtRBoolean, sizeof(Boolean),
162	  	XtOffsetOf( struct _appRes, print_on_quit ),
163      		XtRImmediate, (XtPointer)False },
164    { "appDefaultsVersion", "AppDefaultsVersion", XtRInt, sizeof(int),
165		XtOffsetOf( struct _appRes, app_defaults_version ),
166		XtRImmediate, (XtPointer)0 },
167    { "sampleText", "Text", XtRString, sizeof(String),
168		XtOffsetOf( struct _appRes, sample_text ),
169		XtRString, (XtPointer)"" },
170    { "sampleText16", "Text16", XtRString, sizeof(String),
171		XtOffsetOf( struct _appRes, sample_text16 ),
172		XtRString, (XtPointer)"" },
173    { "sampleTextUCS", "TextUCS", XtRString, sizeof(String),
174		XtOffsetOf( struct _appRes, sample_textUCS ),
175		XtRString, (XtPointer)"" },
176    { "scaledFonts", "ScaledFonts", XtRBoolean, sizeof(Boolean),
177		XtOffsetOf( struct _appRes, scaled_fonts ),
178		XtRImmediate, (XtPointer)False },
179};
180
181static XrmOptionDescRec options[] = {
182{"-pattern",	"pattern",	XrmoptionSepArg,	NULL},
183{"-print",	"printOnQuit",	XrmoptionNoArg,		"True"},
184{"-sample",	"sampleText",	XrmoptionSepArg,	NULL},
185{"-sample16",	"sampleText16",	XrmoptionSepArg,	NULL},
186{"-sampleUCS",	"sampleTextUCS",XrmoptionSepArg,	NULL},
187{"-scaled",	"scaledFonts",	XrmoptionNoArg,		"True"},
188};
189
190static void Syntax(const char *call, int exitval)
191{
192    fprintf (stderr, "usage:  %s [-options ...] -fn font\n\n%s\n", call,
193       gettext(
194	"where options include:\n"
195	"    -display dpy           X server to contact\n"
196	"    -geometry geom         size and location of window\n"
197	"    -pattern fontspec      font name pattern to match against\n"
198	"    -print                 print selected font name on exit\n"
199	"    -sample string         sample text to use for 1-byte fonts\n"
200	"    -sample16 string       sample text to use for 2-byte fonts\n"
201	"    -sampleUCS string      sample text to use for ISO10646 fonts\n"
202	"    -scaled                use scaled instances of fonts\n"
203	"plus any standard toolkit options\n"));
204    exit (exitval);
205}
206
207
208typedef struct FieldValue FieldValue;
209struct FieldValue {
210    int field;
211    String string;
212    Widget menu_item;
213    int count;			/* of fonts */
214    int allocated;
215    int *font;
216    Boolean enable;
217};
218
219
220typedef struct FieldValueList FieldValueList;
221struct FieldValueList {
222    int count;			/* of values */
223    int allocated;
224    Boolean show_unselectable;
225    FieldValue value[1];	/* really [allocated] */
226};
227
228
229typedef struct FontValues FontValues;
230struct FontValues {
231    int value_index[FIELD_COUNT];
232};
233
234
235typedef struct FieldMenuRec FieldMenuRec;
236struct FieldMenuRec {
237    int field;
238    Widget button;
239};
240
241
242typedef struct Choice Choice;
243struct Choice {
244    Choice *prev;
245    FieldValue *value;
246};
247
248
249static XtResource menuResources[] = {
250    { "showUnselectable", "ShowUnselectable", XtRBoolean, sizeof(Boolean),
251		XtOffsetOf( FieldValueList, show_unselectable ),
252		XtRImmediate, (XtPointer)True },
253};
254
255
256typedef enum {ValidateCurrentField, SkipCurrentField} ValidateAction;
257
258static void EnableAllItems(int field);
259static void EnableRemainingItems(ValidateAction current_field_action);
260static void FlushXqueue(Display *dpy);
261static void MarkInvalidFonts(Boolean *set, FieldValue *val);
262static void ScheduleWork(XtProc proc, XtPointer closure, int priority);
263static void SetCurrentFontCount(void);
264static void SetNoFonts(void);
265static void SetParsingFontCount(int count);
266static void reset_currentFontNameString(void);
267
268static XtAppContext appCtx;
269static int numFonts;
270static int numBadFonts;
271static FontValues *fonts;
272static int *scaledFonts;
273static int numScaledFonts;
274static FieldValueList *fieldValues[FIELD_COUNT];
275static FontValues currentFont;
276static int matchingFontCount;
277static Boolean anyDisabled = False;
278static Widget resetButton;
279static Widget ownButton;
280static Widget fieldBox;
281static Widget countLabel;
282static Widget currentFontName;
283static char *currentFontNameString;
284static int currentFontNameSize;
285static Widget sampleText;
286static int textEncoding = -1;
287static XFontStruct *sampleFont = NULL;
288static Boolean *fontInSet;
289static Choice *choiceList = NULL;
290static int enabledMenuIndex;
291static Boolean patternFieldSpecified[FIELD_COUNT]; /* = 0 */
292
293int
294main(int argc, char **argv)
295{
296    Widget topLevel, pane;
297
298    XtSetLanguageProc(NULL, (XtLanguageProc) NULL, NULL);
299
300    /* Handle args that don't require opening a display */
301    for (int n = 1; n < argc; n++) {
302	const char *argn = argv[n];
303	/* accept single or double dash for -help & -version */
304	if (argn[0] == '-' && argn[1] == '-') {
305	    argn++;
306	}
307	if (strcmp(argn, "-help") == 0) {
308	    Syntax(argv[0], 0);
309	}
310	if (strcmp(argn, "-version") == 0) {
311	    puts(PACKAGE_STRING);
312	    exit(0);
313	}
314    }
315
316    topLevel = XtAppInitialize(&appCtx, "XFontSel", options, XtNumber(options),
317			       &argc, argv, NULL, NULL, 0);
318
319#ifdef USE_GETTEXT
320    /*
321     * Set up internationalized messages    Jhobson 8/23/00
322     *
323     * Do this after the AppInitialize since setlocale is setup by
324     * XtSetLanguageProc, but does not occur until XtAppInitialize happens.
325     */
326    textdomain("xfontsel");
327
328    {
329	const char *domaindir;
330
331	if ((domaindir = getenv("TEXTDOMAINDIR")) == NULL) {
332	    domaindir = LOCALEDIR;
333	}
334	bindtextdomain("xfontsel", domaindir);
335    }
336#endif
337
338    if (argc != 1) {
339	fputs(gettext("Unknown argument(s):"), stderr);
340	for (int n = 1; n < argc; n++) {
341	    fprintf(stderr, " %s", argv[n]);
342	}
343	fputs("\n\n", stderr);
344	Syntax(argv[0], 1);
345    }
346
347    XtAppAddActions(appCtx, xfontsel_actions, XtNumber(xfontsel_actions));
348    XtOverrideTranslations
349	(topLevel, XtParseTranslationTable ("<Message>WM_PROTOCOLS: Quit()"));
350
351    XtGetApplicationResources( topLevel, (XtPointer)&AppRes,
352			       resources, XtNumber(resources), NZ );
353    if (AppRes.app_defaults_version < MIN_APP_DEFAULTS_VERSION) {
354	char full_message[300];
355	XrmDatabase rdb = XtDatabase(XtDisplay(topLevel));
356
357	XtWarning(gettext("app-defaults file not properly installed."));
358
359	snprintf(full_message, sizeof(full_message),
360		 "*sampleText*UCSLabel:%s",
361		 gettext("XFontSel app-defaults file not properly installed;\\n"
362			 "see 'xfontsel' manual page."));
363	XrmPutLineResource(&rdb, full_message);
364    }
365
366    ScheduleWork(GetFontNames, (XtPointer)topLevel, 0);
367
368    pane = XtCreateManagedWidget("pane",panedWidgetClass,topLevel,NZ);
369    {
370	Widget commandBox, /* fieldBox, currentFontName,*/ viewPort;
371
372	commandBox = XtCreateManagedWidget("commandBox",formWidgetClass,pane,NZ);
373	{
374	    Widget quitButton /*, resetButton, ownButton , countLabel*/;
375
376	    quitButton =
377		XtCreateManagedWidget("quitButton",commandWidgetClass,commandBox,NZ);
378
379	    resetButton =
380		XtCreateManagedWidget("resetButton",commandWidgetClass,commandBox,NZ);
381
382	    ownButton =
383		XtCreateManagedWidget("ownButton",toggleWidgetClass,commandBox,NZ);
384
385	    countLabel =
386		XtCreateManagedWidget("countLabel",labelWidgetClass,commandBox,NZ);
387
388	    XtAddCallback(quitButton, XtNcallback, Quit, NULL);
389	    XtAddCallback(resetButton, XtNcallback, Reset, NULL);
390	    XtAddCallback(ownButton,XtNcallback,OwnSelection,(XtPointer)True);
391	}
392
393	fieldBox = XtCreateManagedWidget("fieldBox", boxWidgetClass, pane, NZ);
394	{
395	    Widget /*dash,*/ field /*[FIELD_COUNT]*/;
396	    int f;
397
398	    for (f = 0; f < FIELD_COUNT; f++) {
399		char name[10];
400		FieldMenuRec *makeRec = XtNew(FieldMenuRec);
401		snprintf( name, sizeof(name), "field%d", f );
402		XtCreateManagedWidget("dash",labelWidgetClass,fieldBox,NZ);
403		field = XtCreateManagedWidget(name, menuButtonWidgetClass,
404			fieldBox, NZ);
405		XtAddCallback(field, XtNcallback, SelectField,
406			(XtPointer)(long)f);
407		makeRec->field = f;
408		makeRec->button = field;
409		ScheduleWork(MakeFieldMenu, (XtPointer)makeRec, 2);
410		ScheduleWork((XtProc)XtFree, (XtPointer)makeRec, 2);
411	    }
412	}
413
414	/* currentFontName = */
415	{
416	    Arg args[1];
417	    reset_currentFontNameString();
418	    XtSetArg(args[0], XtNlabel, currentFontNameString);
419	    currentFontName =
420		XtCreateManagedWidget("fontName",labelWidgetClass,pane,args,ONE);
421	}
422
423	viewPort =
424	    XtCreateManagedWidget("viewPort",viewportWidgetClass,pane,NZ);
425	sampleText =
426	    XtCreateManagedWidget("sampleText",ucsLabelWidgetClass,viewPort,NZ);
427    }
428
429    XtRealizeWidget(topLevel);
430    XDefineCursor( XtDisplay(topLevel), XtWindow(topLevel), AppRes.cursor );
431    {
432	int f;
433	for (f = 0; f < FIELD_COUNT; f++) currentFont.value_index[f] = -1;
434    }
435    wm_delete_window = XInternAtom(XtDisplay(topLevel), "WM_DELETE_WINDOW",
436				   False);
437    (void) XSetWMProtocols (XtDisplay(topLevel), XtWindow(topLevel),
438                            &wm_delete_window, 1);
439    XtAppMainLoop(appCtx);
440
441    exit(0);
442}
443
444
445typedef struct WorkPiece WorkPieceRec, *WorkPiece;
446struct WorkPiece {
447    WorkPiece next;
448    int priority;
449    XtProc proc;
450    XtPointer closure;
451};
452static WorkPiece workQueue = NULL;
453
454
455/*
456 * ScheduleWork( XtProc proc, XtPointer closure, int priority )
457 *
458 * Adds a WorkPiece to the workQueue in FIFO order by priority.
459 * Lower numbered priority work is completed before higher numbered
460 * priorities.
461 *
462 * If the workQueue was previously empty, then makes sure that
463 * Xt knows we have (background) work to do.
464 */
465
466static void ScheduleWork(XtProc proc, XtPointer closure, int priority)
467{
468    WorkPiece piece = XtNew(WorkPieceRec);
469
470    piece->priority = priority;
471    piece->proc = proc;
472    piece->closure = closure;
473    if (workQueue == NULL) {
474	piece->next = NULL;
475	workQueue = piece;
476	XtAppAddWorkProc(appCtx, DoWorkPiece, NULL);
477    } else {
478	if (workQueue->priority > priority) {
479	    piece->next = workQueue;
480	    workQueue = piece;
481	}
482	else {
483	    WorkPiece n;
484	    for (n = workQueue; n->next && n->next->priority <= priority;)
485		n = n->next;
486	    piece->next = n->next;
487	    n->next = piece;
488	}
489    }
490}
491
492/* ARGSUSED */
493Boolean DoWorkPiece(XtPointer closure)
494{
495    WorkPiece piece = workQueue;
496
497    if (piece) {
498	(*piece->proc)(piece->closure);
499	workQueue = piece->next;
500	XtFree((XtPointer)piece);
501	if (workQueue != NULL)
502	    return False;
503    }
504    return True;
505}
506
507
508/*
509 * FinishWork()
510 *
511 * Drains foreground tasks from the workQueue.
512 * Foreground == (priority < BACKGROUND)
513 */
514
515static void FinishWork(void)
516{
517    while (workQueue && workQueue->priority < BACKGROUND)
518	DoWorkPiece(NULL);
519}
520
521
522typedef struct ParseRec ParseRec;
523struct ParseRec {
524    char **fontNames;
525    int num_fonts;
526    int start, end;
527    FontValues *fonts;
528    FieldValueList **fieldValues;
529};
530
531
532void GetFontNames(XtPointer closure)
533{
534    Widget topLevel = (Widget)closure;
535    Display *dpy = XtDisplay(topLevel);
536    ParseRec *parseRec;
537    int count;
538    char **fontNames;
539    int work_priority = 0;
540
541    fontNames = XListFonts(dpy, AppRes.pattern, 32767, &numFonts);
542
543    fonts = (FontValues*)XtMalloc( numFonts*sizeof(FontValues) );
544    fontInSet = (Boolean*)XtMalloc( numFonts*sizeof(Boolean) );
545    {
546        int f;
547        Boolean *b;
548        for (f = numFonts, b = fontInSet; f; f--, b++)
549            *b = True;
550    }
551    for (int field = 0; field < FIELD_COUNT; field++) {
552	fieldValues[field] = (FieldValueList*)XtMalloc(sizeof(FieldValueList));
553	fieldValues[field]->allocated = 1;
554	fieldValues[field]->count = 0;
555    }
556    if (numFonts == 0) {
557	SetNoFonts();
558	return;
559    }
560    count = matchingFontCount = numFonts;
561    numBadFonts = 0;
562    parseRec = XtNew(ParseRec);
563    *parseRec = (ParseRec) {
564        .fontNames = fontNames,
565        .num_fonts = count,
566        .start = 0,
567        .fonts = fonts,
568        .fieldValues = fieldValues
569    };
570    /* this is bogus; the task should be responsible for quantizing...*/
571    while (count > PARSE_QUANTUM) {
572	ParseRec *prevRec = parseRec;
573	parseRec->end = parseRec->start + PARSE_QUANTUM;
574	ScheduleWork(ParseFontNames, (XtPointer)parseRec, work_priority);
575	ScheduleWork((XtProc)XtFree, (XtPointer)parseRec, work_priority);
576	parseRec = XtNew(ParseRec);
577	*parseRec = *prevRec;
578	parseRec->start += PARSE_QUANTUM;
579	parseRec->fonts += PARSE_QUANTUM;
580	parseRec->fontNames += PARSE_QUANTUM;
581	count -= PARSE_QUANTUM;
582	work_priority = 1;
583    }
584    parseRec->end = numFonts;
585    ScheduleWork(ParseFontNames,(XtPointer)parseRec,work_priority);
586    ScheduleWork((XtProc)XFreeFontNames,(XtPointer)fontNames,work_priority);
587    ScheduleWork((XtProc)XtFree, (XtPointer)parseRec, work_priority);
588    if (AppRes.scaled_fonts)
589	ScheduleWork(FixScalables,(XtPointer)topLevel, work_priority);
590    ScheduleWork(SortFields,(XtPointer)0,work_priority);
591    SetParsingFontCount(matchingFontCount);
592    if (strcmp(AppRes.pattern, DEFAULTPATTERN)) {
593	int maxField, f;
594	for (f = 0; f < numFonts && !IsXLFDFontName(fontNames[f]); f++);
595	if (f != numFonts) {
596	    if (Matches(AppRes.pattern, fontNames[f],
597			 patternFieldSpecified, &maxField)) {
598		for (f = 0; f <= maxField; f++) {
599		    if (patternFieldSpecified[f])
600			currentFont.value_index[f] = 0;
601		}
602	    }
603	    else
604		XtAppWarning( appCtx,
605		    gettext("internal error; pattern didn't match first font" ));
606	}
607	else {
608	    SetNoFonts();
609	    return;
610	}
611    }
612    ScheduleWork(SetCurrentFont, NULL, 1);
613}
614
615
616void ParseFontNames(XtPointer closure)
617{
618    ParseRec *parseRec = (ParseRec*)closure;
619    char **fontNames = parseRec->fontNames;
620    int num_fonts = parseRec->end;
621    FieldValueList **fValues = parseRec->fieldValues;
622    FontValues *fontValues = parseRec->fonts - numBadFonts;
623    int i, font;
624
625    for (font = parseRec->start; font < num_fonts; font++) {
626	char *p;
627	int f, len;
628	FieldValue *v;
629
630	if (!IsXLFDFontName(*fontNames)) {
631	    numFonts--;
632	    numBadFonts++;
633	    continue;
634	}
635
636	for (f = 0, p = *fontNames++; f < FIELD_COUNT; f++) {
637	    const char *fieldP;
638
639	    if (*p) ++p;
640	    if (*p == DELIM || *p == '\0') {
641		fieldP = "";
642		len = 0;
643	    } else {
644		fieldP = p;
645		while (*p && *++p != DELIM);
646		len = p - fieldP;
647	    }
648	    for (i=fValues[f]->count,v=fValues[f]->value; i;i--,v++) {
649		if (len == 0) {
650		    if (v->string == NULL) break;
651		}
652		else
653		    if (v->string &&
654			strncmp( v->string, fieldP, len ) == 0 &&
655			(v->string)[len] == '\0')
656			break;
657	    }
658	    if (i == 0) {
659		int count = fValues[f]->count++;
660		if (count == fValues[f]->allocated) {
661		    int allocated = (fValues[f]->allocated += 10);
662		    fValues[f] = (FieldValueList*)
663			XtRealloc( (char *) fValues[f],
664				   sizeof(FieldValueList) +
665					(allocated-1) * sizeof(FieldValue) );
666		}
667		v = &fValues[f]->value[count];
668		v->field = f;
669		if (len == 0)
670		    v->string = NULL;
671		else {
672		    char *s = XtMalloc(len + 1);
673		    strncpy( s, fieldP, len );
674		    s[len] = '\0';
675		    v->string = (String) s;
676		}
677		v->font = (int*)XtMalloc( 10*sizeof(int) );
678		v->allocated = 10;
679		v->count = 0;
680		v->enable = True;
681		i = 1;
682	    }
683	    fontValues->value_index[f] = fValues[f]->count - i;
684	    if ((i = v->count++) == v->allocated) {
685		int allocated = (v->allocated += 10);
686		v->font = (int*)XtRealloc( (char *) v->font,
687					  allocated * sizeof(int) );
688	    }
689	    v->font[i] = font - numBadFonts;
690	}
691	fontValues++;
692    }
693    SetParsingFontCount(numFonts - num_fonts);
694}
695
696
697/* Add the list of scalable fonts to the match-list of every value instance
698 * for field f.  Must produce sorted order.  Must deal with duplicates
699 * since we need to do this for resolution fields which can be nonzero in
700 * the scalable fonts.
701 */
702static void AddScalables(int f)
703{
704    int i;
705    int max = fieldValues[f]->count;
706    FieldValue *fval = fieldValues[f]->value;
707
708    for (i = 0; i < max; i++, fval++) {
709	int *oofonts, *ofonts, *nfonts, *sfonts;
710	int ocount, ncount, count;
711
712	if (fval->string && !strcmp(fval->string, "0"))
713	    continue;
714	count = numScaledFonts;
715	sfonts = scaledFonts;
716	ocount = fval->count;
717	ncount = ocount + count;
718	nfonts = (int *)XtMalloc( ncount * sizeof(int) );
719	oofonts = ofonts = fval->font;
720	fval->font = nfonts;
721	fval->count = ncount;
722	fval->allocated = ncount;
723	while (count && ocount) {
724	    if (*sfonts < *ofonts) {
725		*nfonts++ = *sfonts++;
726		count--;
727	    } else if (*sfonts == *ofonts) {
728		*nfonts++ = *sfonts++;
729		count--;
730		ofonts++;
731		ocount--;
732		fval->count--;
733	    } else {
734		*nfonts++ = *ofonts++;
735		ocount--;
736	    }
737	}
738	while (ocount) {
739	    *nfonts++ = *ofonts++;
740	    ocount--;
741	}
742	while (count) {
743	    *nfonts++ = *sfonts++;
744	    count--;
745	}
746	XtFree((char *)oofonts);
747    }
748}
749
750
751/* Merge in specific scaled sizes (specified in a comma-separated string)
752 * for field f.  Weed out duplicates.  The set of matching fonts is just
753 * the set of scalable fonts.
754 */
755static void NewScalables(int f, char *slist)
756{
757    char endc = 1;
758    char *str;
759    int i, count;
760    FieldValue *v;
761
762    while (endc) {
763	while (*slist == ' ' || *slist == ',')
764	    slist++;
765	if (!*slist)
766	    break;
767	str = slist;
768	while ((endc = *slist) && endc != ' ' && endc != ',')
769	    slist++;
770	*slist++ = '\0';
771	for (i=fieldValues[f]->count,v=fieldValues[f]->value; --i >= 0; v++) {
772	    if (v->string && !strcmp(v->string, str))
773		break;
774	}
775	if (i >= 0)
776	    continue;
777	count = fieldValues[f]->count++;
778	if (count == fieldValues[f]->allocated) {
779	    int allocated = (fieldValues[f]->allocated += 10);
780	    fieldValues[f] = (FieldValueList*)
781		XtRealloc( (char *) fieldValues[f],
782			   sizeof(FieldValueList) +
783				(allocated-1) * sizeof(FieldValue) );
784	}
785	v = &fieldValues[f]->value[count];
786	v->field = f;
787	v->string = str;
788	v->count = numScaledFonts;
789	v->font = scaledFonts;
790	v->allocated = 0;
791	v->enable = True;
792    }
793}
794
795
796/* Find all scalable fonts, defined as the set matching "0" in the pixel
797 * size field (field 6).  Augment the match-lists for all other fields
798 * that are scalable.  Add in new scalable pixel and point sizes given
799 * in resources, along with the current Screen's actual resX and resY
800 * values.
801 */
802/*ARGSUSED*/
803void FixScalables(XtPointer closure)
804{
805    int i;
806    FieldValue *fval = fieldValues[6]->value;
807    Widget topLevel = (Widget) closure;
808    Display *dpy = XtDisplay(topLevel);
809    int scr = XScreenNumberOfScreen(XtScreenOfObject(topLevel));
810    double xres, yres;
811    static char xreslist[21];		/* log10(UINT64_MAX) == 19 */
812    static char yreslist[21];
813
814    /* from xdpyinfo.c:
815     * there are 2.54 centimeters to an inch; so there are 25.4 millimeters.
816     *
817     *     dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
818     *         = N pixels / (M inch / 25.4)
819     *         = N * 25.4 pixels / M inch
820     */
821    xres = ((((double) DisplayWidth(dpy, scr)) * 25.4) /
822	    ((double) DisplayWidthMM(dpy, scr)));
823    yres = ((((double) DisplayHeight(dpy, scr)) * 25.4) /
824	    ((double) DisplayHeightMM(dpy, scr)));
825
826    /*
827     * xxx the "0" element is always added, so we can't force these here....
828     *
829     * However, what's interesting is that if the pattern contains '*' for these
830     * fields (i.e. instead of '0') then we end up with the menu containing "0,
831     * 100, xres", which makes for a really good demonstration of how scaling
832     * fonts without knowing the true screen resolution leads to very wonky
833     * results.
834     *
835     * xxx obviously these are static and related only to the screen of the
836     * Widget at the time this code executes and so you can't drag the Xfontsel
837     * winto to another screen with a different resolution and see things change
838     * dynamically -- you have to instantiate a new Xfontsel process on each
839     * different screen as desired.
840     */
841    sprintf(xreslist, "%d", (int) (xres + 0.5));
842    sprintf(yreslist, "%d", (int) (yres + 0.5));
843
844    for (i = fieldValues[6]->count; --i >= 0; fval++) {
845	if (fval->string && !strcmp(fval->string, "0")) {
846	    scaledFonts = fval->font;
847	    numScaledFonts = fval->count;
848	    AddScalables(6);
849	    NewScalables(6, AppRes.pixelSizeList);
850	    AddScalables(7);
851	    NewScalables(7, AppRes.pointSizeList);
852	    NewScalables(8, xreslist);
853	    NewScalables(9, yreslist);
854	    AddScalables(11);
855	    break;
856	}
857    }
858}
859
860
861/* A verbatim copy from xc/lib/font/fontfile/fontdir.c */
862
863/*
864 * Compare two strings just like strcmp, but preserve decimal integer
865 * sorting order, i.e. "2" < "10" or "iso8859-2" < "iso8859-10" <
866 * "iso10646-1". Strings are sorted as if sequences of digits were
867 * prefixed by a length indicator (i.e., does not ignore leading zeroes).
868 *
869 * Markus Kuhn <Markus.Kuhn@cl.cam.ac.uk>
870 */
871#define Xisdigit(c) ('\060' <= (c) && (c) <= '\071')
872
873static int strcmpn(const char *s1, const char *s2)
874{
875    int digits, predigits = 0;
876    const char *ss1, *ss2;
877
878    while (1) {
879	if (*s1 == 0 && *s2 == 0)
880	    return 0;
881	digits = Xisdigit(*s1) && Xisdigit(*s2);
882	if (digits && !predigits) {
883	    ss1 = s1;
884	    ss2 = s2;
885	    while (Xisdigit(*ss1) && Xisdigit(*ss2))
886		ss1++, ss2++;
887	    if (!Xisdigit(*ss1) && Xisdigit(*ss2))
888		return -1;
889	    if (Xisdigit(*ss1) && !Xisdigit(*ss2))
890		return 1;
891	}
892	if ((unsigned char)*s1 < (unsigned char)*s2)
893	    return -1;
894	if ((unsigned char)*s1 > (unsigned char)*s2)
895	    return 1;
896	predigits = digits;
897	s1++, s2++;
898    }
899}
900
901
902/* Order is *, (nil), rest */
903static int AlphabeticSort(_Xconst void *fval1, _Xconst void *fval2)
904{
905#   define fval1 ((_Xconst FieldValue *)fval1)
906#   define fval2 ((_Xconst FieldValue *)fval2)
907
908    if (fval1->string && !strcmp(fval1->string, "*"))
909	return -1;
910    if (fval2->string && !strcmp(fval2->string, "*"))
911	return 1;
912    if (!fval1->string)
913	return -1;
914    if (!fval2->string)
915	return 1;
916
917    return strcmpn(fval1->string, fval2->string);
918
919#   undef fval1
920#   undef fval2
921}
922
923
924/* Order is *, (nil), rest */
925static int NumericSort(_Xconst void *fval1, _Xconst void *fval2)
926{
927#   define fval1 ((_Xconst FieldValue *)fval1)
928#   define fval2 ((_Xconst FieldValue *)fval2)
929
930    if (fval1->string && !strcmp(fval1->string, "*"))
931	return -1;
932    if (fval2->string && !strcmp(fval2->string, "*"))
933	return 1;
934    if (!fval1->string)
935	return -1;
936    if (!fval2->string)
937	return 1;
938
939    return atoi(fval1->string) - atoi(fval2->string);
940
941#   undef fval1
942#   undef fval2
943}
944
945
946/* Resort each field, to get reasonable menus.  Sort alphabetically or
947 * numerically, depending on the field.  Since the fonts have indexes
948 * into the fields, we need to deal with updating those indexes after the
949 * sort.
950 */
951/*ARGSUSED*/
952void SortFields(XtPointer closure)
953{
954    int i, j, count;
955    FieldValue *vals;
956    int *indexes;
957    int *idx;
958
959    for (i = 0; i < FIELD_COUNT; i++) {
960	count = fieldValues[i]->count;
961	vals = fieldValues[i]->value;
962	indexes = (int *)XtMalloc(count * sizeof(int));
963	/* temporarily use the field component, will restore it below */
964	for (j = 0; j < count; j++)
965	    vals[j].field = j;
966	switch (i) {
967	case 6: case 7: case 8: case 9: case 11:
968	    qsort((char *)vals, count, sizeof(FieldValue), NumericSort);
969	    break;
970	default:
971	    qsort((char *)vals, count, sizeof(FieldValue), AlphabeticSort);
972	    break;
973	}
974	for (j = 0; j < count; j++) {
975	    indexes[vals[j].field] = j;
976	    vals[j].field = i;
977	}
978	for (j = 0; j < numFonts; j++) {
979	    idx = &fonts[j].value_index[i];
980	    if (*idx >= 0)
981		*idx = indexes[*idx];
982	}
983	XtFree((char *)indexes);
984    }
985}
986
987
988Boolean IsXLFDFontName(String fontName)
989{
990    int f;
991    for (f = 0; *fontName;) if (*fontName++ == DELIM) f++;
992    return (f == FIELD_COUNT);
993}
994
995
996void MakeFieldMenu(XtPointer closure)
997{
998    FieldMenuRec *makeRec = (FieldMenuRec*)closure;
999    Widget menu;
1000    FieldValueList *values = fieldValues[makeRec->field];
1001    FieldValue *val = values->value;
1002    int i;
1003    Arg args[1];
1004    register Widget item;
1005
1006    if (numFonts)
1007	menu =
1008	  XtCreatePopupShell("menu",simpleMenuWidgetClass,makeRec->button,NZ);
1009    else {
1010	SetNoFonts();
1011	return;
1012    }
1013    XtGetSubresources(menu, (XtPointer) values, "options", "Options",
1014		      menuResources, XtNumber(menuResources), NZ);
1015    XtAddCallback(menu, XtNpopupCallback, EnableOtherValues,
1016		  (XtPointer)(long)makeRec->field );
1017
1018    if (!patternFieldSpecified[val->field]) {
1019	XtSetArg( args[0], XtNlabel, "*" );
1020	item = XtCreateManagedWidget("any",smeBSBObjectClass,menu,args,ONE);
1021	XtAddCallback(item, XtNcallback, AnyValue, (XtPointer)(long)val->field);
1022    }
1023
1024    for (i = values->count; i; i--, val++) {
1025	XtSetArg( args[0], XtNlabel, val->string ? val->string : "(nil)" );
1026	item =
1027	    XtCreateManagedWidget(val->string ? val->string : "nil",
1028				  smeBSBObjectClass, menu, args, ONE);
1029	XtAddCallback(item, XtNcallback, SelectValue, (XtPointer)val);
1030	val->menu_item = item;
1031    }
1032}
1033
1034
1035static void SetNoFonts(void)
1036{
1037    matchingFontCount = 0;
1038    SetCurrentFontCount();
1039    XtSetSensitive(fieldBox, False);
1040    XtSetSensitive(resetButton, False);
1041    XtSetSensitive(ownButton, False);
1042    if (AppRes.app_defaults_version >= MIN_APP_DEFAULTS_VERSION) {
1043	XtUnmapWidget(sampleText);
1044    }
1045}
1046
1047
1048Boolean Matches(register String pattern, register String fontName,
1049		Boolean fields[/*FIELD_COUNT*/], int *maxField)
1050{
1051    register int field = (*fontName == DELIM) ? -1 : 0;
1052    register Boolean marked_this_field = False;
1053
1054    while (*pattern) {
1055	if (*pattern == *fontName || *pattern == '?') {
1056	    pattern++;
1057	    if (*fontName++ == DELIM) {
1058		field++;
1059		marked_this_field = False;
1060	    }
1061	    else if (!marked_this_field)
1062		fields[field] = marked_this_field = True;
1063	    continue;
1064	}
1065	if (*pattern == '*') {
1066	    if (*++pattern == '\0') {
1067		*maxField = field;
1068		return True;
1069	    }
1070	    while (*fontName) {
1071		Boolean field_bits[FIELD_COUNT];
1072		int max_field;
1073		if (*fontName == DELIM) field++;
1074		bzero( field_bits, sizeof(field_bits) );
1075		if (Matches(pattern, fontName++, field_bits, &max_field)) {
1076		    int f;
1077		    *maxField = field + max_field;
1078		    for (f = 0; f <= max_field; field++, f++)
1079			fields[field] = field_bits[f];
1080		    return True;
1081		}
1082	    }
1083	    return False;
1084	}
1085	else /* (*pattern != '*') */
1086	    return False;
1087    }
1088    if (*fontName)
1089	return False;
1090
1091    *maxField = field;
1092    return True;
1093}
1094
1095
1096/* ARGSUSED */
1097void SelectValue(Widget w, XtPointer closure, XtPointer callData)
1098{
1099    FieldValue *val = (FieldValue*)closure;
1100#ifdef LOG_CHOICES
1101    Choice *choice = XtNew(Choice);
1102#else
1103    static Choice pChoice;
1104    Choice *choice = &pChoice;
1105#endif
1106
1107#ifdef notdef
1108    Widget button = XtParent(XtParent(w));
1109    Arg args[1];
1110
1111    XtSetArg(args[0], XtNlabel, val->string);
1112    XtSetValues( button, args, ONE );
1113#endif
1114
1115    currentFont.value_index[val->field] = val - fieldValues[val->field]->value;
1116
1117    choice->prev = choiceList;
1118    choice->value = val;
1119    choiceList = choice;
1120
1121    SetCurrentFont(NULL);
1122    EnableRemainingItems(SkipCurrentField);
1123}
1124
1125
1126/* ARGSUSED */
1127void AnyValue(Widget w, XtPointer closure, XtPointer callData)
1128{
1129    int field = (long)closure;
1130    currentFont.value_index[field] = -1;
1131    SetCurrentFont(NULL);
1132    EnableAllItems(field);
1133    EnableRemainingItems(ValidateCurrentField);
1134}
1135
1136
1137static void SetCurrentFontCount(void)
1138{
1139    char label[80];
1140    Arg args[1];
1141    if (matchingFontCount == 1)
1142	strcpy( label, gettext("1 name matches") );
1143    else if (matchingFontCount)
1144	snprintf( label, sizeof(label), gettext("%d names match"), matchingFontCount);
1145    else
1146	strcpy( label, gettext("no names match") );
1147    XtSetArg( args[0], XtNlabel, label );
1148    XtSetValues( countLabel, args, ONE );
1149}
1150
1151
1152static void SetParsingFontCount(int count)
1153{
1154    char label[80];
1155    Arg args[1];
1156    if (count == 1)
1157	strcpy( label, gettext("1 name to parse") );
1158    else
1159	snprintf( label, sizeof(label), gettext("%d names to parse"), count );
1160    XtSetArg( args[0], XtNlabel, label );
1161    XtSetValues( countLabel, args, ONE );
1162    FlushXqueue(XtDisplay(countLabel));
1163}
1164
1165/* ARGSUSED */
1166static Boolean IsISO10646(Display *dpy, XFontStruct *font)
1167{
1168    Boolean ok;
1169    int i;
1170    char *regname;
1171    Atom registry;
1172    XFontProp *xfp;
1173
1174    ok = False;
1175    registry = XInternAtom(dpy, "CHARSET_REGISTRY", False);
1176
1177    for (i = 0, xfp = font->properties;
1178	 ok == False && i < font->n_properties; xfp++, i++) {
1179	if (xfp->name == registry) {
1180	    regname = XGetAtomName(dpy, (Atom) xfp->card32);
1181	    if (strcmp(regname, "ISO10646") == 0 ||
1182		strcmp(regname, "iso10646") == 0)
1183	      ok = True;
1184	    XFree(regname);
1185	}
1186    }
1187    return ok;
1188}
1189
1190/* ARGSUSED */
1191void SetCurrentFont(XtPointer closure)
1192{
1193    int f;
1194    Boolean *b;
1195
1196    if (numFonts == 0) {
1197	SetNoFonts();
1198	return;
1199    }
1200    for (f = numFonts, b = fontInSet; f; f--, b++) *b = True;
1201
1202    {
1203	int bytesLeft = currentFontNameSize;
1204	int pos = 0;
1205
1206	for (f = 0; f < FIELD_COUNT; f++) {
1207	    int len, i;
1208	    String str;
1209
1210	    currentFontNameString[pos++] = DELIM;
1211	    if ((i = currentFont.value_index[f]) != -1) {
1212		FieldValue *val = &fieldValues[f]->value[i];
1213		if ((str = val->string))
1214		    len = strlen(str);
1215		else {
1216		    str = "";
1217		    len = 0;
1218		}
1219		MarkInvalidFonts(fontInSet, val);
1220	    } else {
1221		str = "*";
1222		len = 1;
1223	    }
1224	    if (len+1 > --bytesLeft) {
1225		currentFontNameString =
1226		    XtRealloc(currentFontNameString, currentFontNameSize+=128);
1227		bytesLeft += 128;
1228	    }
1229	    strcpy( &currentFontNameString[pos], str );
1230	    pos += len;
1231	    bytesLeft -= len;
1232	}
1233    }
1234    {
1235	Arg args[1];
1236	XtSetArg( args[0], XtNlabel, currentFontNameString );
1237	XtSetValues( currentFontName, args, ONE );
1238    }
1239    matchingFontCount = 0;
1240    for (f = numFonts, b = fontInSet; f; f--, b++) {
1241	if (*b) matchingFontCount++;
1242    }
1243
1244    SetCurrentFontCount();
1245
1246    {
1247	Widget mapWidget = sampleText;
1248	Display *dpy = XtDisplay(mapWidget);
1249	XFontStruct *font = XLoadQueryFont(dpy, currentFontNameString);
1250	String sample_text;
1251	if (font == NULL)
1252	    XtSetSensitive(mapWidget, False);
1253	else {
1254	    int nargs = 1;
1255	    Arg args[3];
1256	    int encoding;
1257	    if (font->min_byte1 || font->max_byte1) {
1258		if (IsISO10646(dpy, font) == True) {
1259		    encoding = XawTextEncodingUCS;
1260		    sample_text = AppRes.sample_textUCS;
1261		} else {
1262		    encoding = XawTextEncodingChar2b;
1263		    sample_text = AppRes.sample_text16;
1264		}
1265	    } else {
1266		encoding = XawTextEncoding8bit;
1267		sample_text = AppRes.sample_text;
1268	    }
1269	    XtSetArg( args[0], XtNfont, font );
1270	    if (encoding != textEncoding) {
1271		XtSetArg(args[1], XtNencoding, encoding);
1272		XtSetArg(args[2], XtNlabel, sample_text);
1273		textEncoding = encoding;
1274		nargs = 3;
1275	    }
1276	    XtSetValues( sampleText, args, nargs );
1277	    XtSetSensitive(mapWidget, True);
1278	    XtMapWidget(mapWidget);
1279	    if (sampleFont) XFreeFont( dpy, sampleFont );
1280	    sampleFont = font;
1281	    OwnSelection( sampleText, (XtPointer)False, (XtPointer)True );
1282	}
1283	FlushXqueue(dpy);
1284    }
1285}
1286
1287
1288static void MarkInvalidFonts(Boolean *set, FieldValue *val)
1289{
1290    int fi = 0, vi;
1291    int *fp = val->font;
1292    for (vi = val->count; vi; vi--, fp++) {
1293	while (fi < *fp) {
1294	    set[fi] = False;
1295	    fi++;
1296	}
1297	fi++;
1298    }
1299    while (fi < numFonts) {
1300	set[fi] = False;
1301	fi++;
1302    }
1303}
1304
1305
1306static void EnableRemainingItems(ValidateAction current_field_action)
1307{
1308    if (matchingFontCount == 0 || matchingFontCount == numFonts) {
1309	if (anyDisabled) {
1310	    int field;
1311	    for (field = 0; field < FIELD_COUNT; field++) {
1312		EnableAllItems(field);
1313	    }
1314	    anyDisabled = False;
1315	}
1316    }
1317    else {
1318	int field;
1319	for (field = 0; field < FIELD_COUNT; field++) {
1320	    FieldValue *value = fieldValues[field]->value;
1321	    int count;
1322	    if (current_field_action == SkipCurrentField &&
1323		field == choiceList->value->field)
1324		continue;
1325	    for (count = fieldValues[field]->count; count; count--, value++) {
1326		int *fp = value->font;
1327		int fontCount;
1328		for (fontCount = value->count; fontCount; fontCount--, fp++) {
1329		    if (fontInSet[*fp]) {
1330			value->enable = True;
1331			goto NextValue;
1332		    }
1333		}
1334		value->enable = False;
1335	      NextValue:;
1336	    }
1337	}
1338	anyDisabled = True;
1339    }
1340    enabledMenuIndex = -1;
1341    {
1342	int f;
1343	for (f = 0; f < FIELD_COUNT; f++)
1344	    ScheduleWork(EnableMenu, (XtPointer)(long)f, BACKGROUND);
1345    }
1346}
1347
1348
1349static void EnableAllItems(int field)
1350{
1351    FieldValue *value = fieldValues[field]->value;
1352    int count;
1353    for (count = fieldValues[field]->count; count; count--, value++) {
1354	value->enable = True;
1355    }
1356}
1357
1358
1359/* ARGSUSED */
1360void SelectField(Widget w, XtPointer closure, XtPointer callData)
1361{
1362    int field = (long)closure;
1363    FieldValue *values = fieldValues[field]->value;
1364    int count = fieldValues[field]->count;
1365    printf(gettext("field %d:\n"), field );
1366    while (count--) {
1367	printf( gettext(" %s: %d fonts\n"), values->string, values->count );
1368	values++;
1369    }
1370    printf( "\n" );
1371}
1372
1373
1374/* When 2 out of 3 y-related scalable fields are set, we need to restrict
1375 * the third set to only match on exact matches, that is, ignore the
1376 * matching to scalable fonts.  Because choosing a random third value
1377 * will almost always produce an illegal font name, and it isn't worth
1378 * trying to compute which choices might be legal to the font scaler.
1379 */
1380static void DisableScaled(int f, int f1, int f2)
1381{
1382    int i, j;
1383    FieldValue *v;
1384    int *font;
1385
1386    for (i = fieldValues[f]->count, v = fieldValues[f]->value; --i >= 0; v++) {
1387	if (!v->enable || !v->string || !strcmp(v->string, "0"))
1388	    continue;
1389	for (j = v->count, font = v->font; --j >= 0; font++) {
1390	    if (fontInSet[*font] &&
1391		fonts[*font].value_index[f1] == currentFont.value_index[f1] &&
1392		fonts[*font].value_index[f2] == currentFont.value_index[f2])
1393		break;
1394	}
1395	if (j < 0) {
1396	    v->enable = False;
1397	    XtSetSensitive(v->menu_item, False);
1398	}
1399    }
1400}
1401
1402/* ARGSUSED */
1403void EnableOtherValues(Widget w, XtPointer closure, XtPointer callData)
1404{
1405    int field = (long)closure;
1406    Boolean *font_in_set = (Boolean*)XtMalloc(numFonts*sizeof(Boolean));
1407    Boolean *b;
1408    int f, count;
1409
1410    FinishWork();
1411    for (f = numFonts, b = font_in_set; f; f--, b++) *b = True;
1412    for (f = 0; f < FIELD_COUNT; f++) {
1413	int i;
1414	if (f != field && (i = currentFont.value_index[f]) != -1) {
1415	    MarkInvalidFonts( font_in_set, &fieldValues[f]->value[i] );
1416	}
1417    }
1418    if (scaledFonts)
1419    {
1420	/* Check for 2 out of 3 scalable y fields being set */
1421	const char *str;
1422	Bool specificPxl, specificPt, specificY;
1423
1424	f = currentFont.value_index[6];
1425	specificPxl = (f >= 0 &&
1426		       (str = fieldValues[6]->value[f].string) &&
1427		       strcmp(str, "0"));
1428	f = currentFont.value_index[7];
1429	specificPt = (f >= 0 &&
1430		      (str = fieldValues[7]->value[f].string) &&
1431		      strcmp(str, "0"));
1432	f = currentFont.value_index[9];
1433	specificY = (f >= 0 &&
1434		     (str = fieldValues[9]->value[f].string) &&
1435		     strcmp(str, "0"));
1436	if (specificPt && specificY)
1437	    DisableScaled(6, 7, 9);
1438	if (specificPxl && specificY)
1439	    DisableScaled(7, 6, 9);
1440	if (specificPxl && specificPt)
1441	    DisableScaled(9, 6, 7);
1442    }
1443    count = 0;
1444    for (f = numFonts, b = font_in_set; f; f--, b++) {
1445	if (*b) count++;
1446    }
1447    if (count != matchingFontCount) {
1448	Boolean *sp = fontInSet;
1449	FieldValueList *fieldValue = fieldValues[field];
1450	for (b = font_in_set, f = 0; f < numFonts; f++, b++, sp++) {
1451	    if (*b != *sp) {
1452		int i = fonts[f].value_index[field];
1453		FieldValue *val = &fieldValue->value[i];
1454		val->enable = True;
1455		XtSetSensitive(val->menu_item, True);
1456		if (++count == matchingFontCount) break;
1457	    }
1458	}
1459    }
1460    XtFree((char *)font_in_set);
1461    if (enabledMenuIndex < field)
1462	EnableMenu((XtPointer)(long)field);
1463}
1464
1465
1466void EnableMenu(XtPointer closure)
1467{
1468    int field = (long)closure;
1469    FieldValue *val = fieldValues[field]->value;
1470    int f;
1471    Widget *managed = NULL, *pManaged = NULL;
1472    Widget *unmanaged = NULL, *pUnmanaged = NULL;
1473    Boolean showUnselectable = fieldValues[field]->show_unselectable;
1474
1475    for (f = fieldValues[field]->count; f; f--, val++) {
1476	if (showUnselectable) {
1477	    if (val->enable != XtIsSensitive(val->menu_item))
1478		XtSetSensitive(val->menu_item, val->enable);
1479	}
1480	else {
1481	    if (val->enable != XtIsManaged(val->menu_item)) {
1482		if (val->enable) {
1483		    if (managed == NULL) {
1484			managed = (Widget*)
1485			    XtMalloc(fieldValues[field]->count*sizeof(Widget));
1486			pManaged = managed;
1487		    }
1488		    *pManaged++ = val->menu_item;
1489		}
1490		else {
1491		    if (unmanaged == NULL) {
1492			unmanaged = (Widget*)
1493			    XtMalloc(fieldValues[field]->count*sizeof(Widget));
1494			pUnmanaged = unmanaged;
1495		    }
1496		    *pUnmanaged++ = val->menu_item;
1497		}
1498	    }
1499	}
1500    }
1501    if (pManaged != managed) {
1502	XtManageChildren(managed, pManaged - managed);
1503	XtFree((char *) managed);
1504    }
1505    if (pUnmanaged != unmanaged) {
1506	XtUnmanageChildren(unmanaged, pUnmanaged - unmanaged);
1507	XtFree((char *) unmanaged);
1508    }
1509    enabledMenuIndex = field;
1510}
1511
1512
1513static void FlushXqueue(Display *dpy)
1514{
1515    XSync(dpy, False);
1516    while (XtAppPending(appCtx)) XtAppProcessEvent(appCtx, XtIMAll);
1517}
1518
1519
1520/* ARGSUSED */
1521void Quit(Widget w, XtPointer closure, XtPointer callData)
1522{
1523    XtCloseDisplay(XtDisplay(w));
1524    if (AppRes.print_on_quit) printf( "%s", currentFontNameString );
1525    exit(0);
1526}
1527
1528
1529void Reset(Widget w, XtPointer closure, XtPointer callData) {
1530  Arg args[1];
1531  reset_currentFontNameString();
1532  XtSetArg(args[0], XtNlabel, currentFontNameString);
1533  XtSetValues(currentFontName, args, ONE);
1534
1535  for (int f = 0; f < FIELD_COUNT; f++)
1536    currentFont.value_index[f] = patternFieldSpecified[f] ? 0 : -1;
1537
1538  SetCurrentFont(NULL);
1539  EnableRemainingItems(SkipCurrentField); /* menu */
1540}
1541
1542static void reset_currentFontNameString(void) {
1543  currentFontNameSize = strlen(AppRes.pattern);
1544  if (currentFontNameSize < 128) currentFontNameSize = 128;
1545  XtFree(currentFontNameString);
1546  currentFontNameString = XtMalloc(currentFontNameSize);
1547  strcpy(currentFontNameString, AppRes.pattern);
1548}
1549
1550
1551static Boolean
1552ConvertSelection(Widget w, Atom *selection, Atom *target, Atom *type,
1553		 XtPointer *value, unsigned long *length, int *format)
1554{
1555    /* XmuConvertStandardSelection will use the second parameter only when
1556     * converting to the target TIMESTAMP.  However, it will never be
1557     * called upon to perform this conversion, because Xt will handle it
1558     * internally.  CurrentTime will never be used.
1559     */
1560    if (XmuConvertStandardSelection(w, CurrentTime, selection, target, type,
1561				    (XPointer *) value, length, format))
1562	return True;
1563
1564    if (*target == XA_STRING) {
1565	*type = XA_STRING;
1566	*value = currentFontNameString;
1567	*length = strlen(*value);
1568	*format = 8;
1569	return True;
1570    }
1571    else {
1572	return False;
1573    }
1574}
1575
1576static AtomPtr _XA_PRIMARY_FONT = NULL;
1577#define XA_PRIMARY_FONT XmuInternAtom(XtDisplay(w),_XA_PRIMARY_FONT)
1578
1579/* ARGSUSED */
1580static void LoseSelection(Widget w, Atom *selection)
1581{
1582    Arg args[1];
1583    XtSetArg( args[0], XtNstate, False );
1584    XtSetValues( w, args, ONE );
1585    if (*selection == XA_PRIMARY_FONT) {
1586	XtSetSensitive(currentFontName, False);
1587    }
1588}
1589
1590
1591/* ARGSUSED */
1592static void DoneSelection(Widget w, Atom *selection, Atom *target)
1593{
1594    /* do nothing */
1595}
1596
1597
1598/* ARGSUSED */
1599void OwnSelection(Widget w, XtPointer closure, XtPointer callData)
1600{
1601    Time time = XtLastTimestampProcessed(XtDisplay(w));
1602    Boolean primary = (Boolean) (long) closure;
1603    Boolean own = (Boolean) (long) callData;
1604
1605    if (_XA_PRIMARY_FONT == NULL)
1606	_XA_PRIMARY_FONT = XmuMakeAtom("PRIMARY_FONT");
1607
1608    if (own) {
1609	XtOwnSelection( w, XA_PRIMARY_FONT, time,
1610			ConvertSelection, LoseSelection, DoneSelection );
1611	if (primary)
1612	    XtOwnSelection( w, XA_PRIMARY, time,
1613			   ConvertSelection, LoseSelection, DoneSelection );
1614	if (!XtIsSensitive(currentFontName)) {
1615	    XtSetSensitive(currentFontName, True);
1616	}
1617    }
1618    else {
1619	XtDisownSelection(w, XA_PRIMARY_FONT, time);
1620	if (primary)
1621	    XtDisownSelection(w, XA_PRIMARY, time);
1622	XtSetSensitive(currentFontName, False);
1623    }
1624}
1625
1626/*ARGSUSED*/
1627void
1628QuitAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
1629{
1630    exit (0);
1631}
1632