Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2 
      3 Copyright (c) 1985-1989  X Consortium
      4 
      5 Permission is hereby granted, free of charge, to any person obtaining
      6 a copy of this software and associated documentation files (the
      7 "Software"), to deal in the Software without restriction, including
      8 without limitation the rights to use, copy, modify, merge, publish,
      9 distribute, sublicense, and/or sell copies of the Software, and to
     10 permit persons to whom the Software is furnished to do so, subject to
     11 the following conditions:
     12 
     13 The above copyright notice and this permission notice shall be included
     14 in all copies or substantial portions of the Software.
     15 
     16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     17 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     19 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22 OTHER DEALINGS IN THE SOFTWARE.
     23 
     24 Except as contained in this notice, the name of the X Consortium shall
     25 not be used in advertising or otherwise to promote the sale, use or
     26 other dealings in this Software without prior written authorization
     27 from the X Consortium.
     28 
     29 Author:	Ralph R. Swick, DEC/MIT Project Athena
     30 	one weekend in November, 1989
     31 Modified: Mark Leisher <mleisher (at) 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 
    104 void GetFontNames(XtPointer closure);
    105 Boolean Matches(String pattern, String fontName, Boolean fields[], int *maxfields);
    106 Boolean DoWorkPiece(XtPointer closure);
    107 void Quit(Widget w, XtPointer closure, XtPointer callData) _X_NORETURN;
    108 void Reset(Widget w, XtPointer closure, XtPointer callData);
    109 void OwnSelection(Widget w, XtPointer closure, XtPointer callData);
    110 void SelectField(Widget w, XtPointer closure, XtPointer callData);
    111 void ParseFontNames(XtPointer closure);
    112 void SortFields(XtPointer closure);
    113 void FixScalables(XtPointer closure);
    114 void MakeFieldMenu(XtPointer closure);
    115 void SelectValue(Widget w, XtPointer closure, XtPointer callData);
    116 void AnyValue(Widget w, XtPointer closure, XtPointer callData);
    117 void EnableOtherValues(Widget w, XtPointer closure, XtPointer callData);
    118 void EnableMenu(XtPointer closure);
    119 void SetCurrentFont(XtPointer closure);
    120 void QuitAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
    121     _X_NORETURN;
    122 
    123 static XtActionsRec xfontsel_actions[] = {
    124     {"Quit",	    QuitAction}
    125 };
    126 
    127 static Atom wm_delete_window;
    128 
    129 Boolean IsXLFDFontName(String fontName);
    130 
    131 typedef void (*XtProc)(XtPointer closure);
    132 
    133 static 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 
    148 static 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 
    181 static 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 
    190 static 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 
    208 typedef struct FieldValue FieldValue;
    209 struct 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 
    220 typedef struct FieldValueList FieldValueList;
    221 struct FieldValueList {
    222     int count;			/* of values */
    223     int allocated;
    224     Boolean show_unselectable;
    225     FieldValue value[1];	/* really [allocated] */
    226 };
    227 
    228 
    229 typedef struct FontValues FontValues;
    230 struct FontValues {
    231     int value_index[FIELD_COUNT];
    232 };
    233 
    234 
    235 typedef struct FieldMenuRec FieldMenuRec;
    236 struct FieldMenuRec {
    237     int field;
    238     Widget button;
    239 };
    240 
    241 
    242 typedef struct Choice Choice;
    243 struct Choice {
    244     Choice *prev;
    245     FieldValue *value;
    246 };
    247 
    248 
    249 static XtResource menuResources[] = {
    250     { "showUnselectable", "ShowUnselectable", XtRBoolean, sizeof(Boolean),
    251 		XtOffsetOf( FieldValueList, show_unselectable ),
    252 		XtRImmediate, (XtPointer)True },
    253 };
    254 
    255 
    256 typedef enum {ValidateCurrentField, SkipCurrentField} ValidateAction;
    257 
    258 static void EnableAllItems(int field);
    259 static void EnableRemainingItems(ValidateAction current_field_action);
    260 static void FlushXqueue(Display *dpy);
    261 static void MarkInvalidFonts(Boolean *set, FieldValue *val);
    262 static void ScheduleWork(XtProc proc, XtPointer closure, int priority);
    263 static void SetCurrentFontCount(void);
    264 static void SetNoFonts(void);
    265 static void SetParsingFontCount(int count);
    266 static void reset_currentFontNameString(void);
    267 
    268 static XtAppContext appCtx;
    269 static int numFonts;
    270 static int numBadFonts;
    271 static FontValues *fonts;
    272 static int *scaledFonts;
    273 static int numScaledFonts;
    274 static FieldValueList *fieldValues[FIELD_COUNT];
    275 static FontValues currentFont;
    276 static int matchingFontCount;
    277 static Boolean anyDisabled = False;
    278 static Widget resetButton;
    279 static Widget ownButton;
    280 static Widget fieldBox;
    281 static Widget countLabel;
    282 static Widget currentFontName;
    283 static char *currentFontNameString;
    284 static int currentFontNameSize;
    285 static Widget sampleText;
    286 static int textEncoding = -1;
    287 static XFontStruct *sampleFont = NULL;
    288 static Boolean *fontInSet;
    289 static Choice *choiceList = NULL;
    290 static int enabledMenuIndex;
    291 static Boolean patternFieldSpecified[FIELD_COUNT]; /* = 0 */
    292 
    293 int
    294 main(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 
    445 typedef struct WorkPiece WorkPieceRec, *WorkPiece;
    446 struct WorkPiece {
    447     WorkPiece next;
    448     int priority;
    449     XtProc proc;
    450     XtPointer closure;
    451 };
    452 static 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 
    466 static 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 */
    493 Boolean 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 
    515 static void FinishWork(void)
    516 {
    517     while (workQueue && workQueue->priority < BACKGROUND)
    518 	DoWorkPiece(NULL);
    519 }
    520 
    521 
    522 typedef struct ParseRec ParseRec;
    523 struct ParseRec {
    524     char **fontNames;
    525     int num_fonts;
    526     int start, end;
    527     FontValues *fonts;
    528     FieldValueList **fieldValues;
    529 };
    530 
    531 
    532 void 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 
    616 void 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  */
    702 static 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  */
    755 static 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*/
    803 void 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 (at) cl.cam.ac.uk>
    870  */
    871 #define Xisdigit(c) ('\060' <= (c) && (c) <= '\071')
    872 
    873 static 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 */
    903 static 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 */
    925 static 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*/
    952 void 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 
    988 Boolean IsXLFDFontName(String fontName)
    989 {
    990     int f;
    991     for (f = 0; *fontName;) if (*fontName++ == DELIM) f++;
    992     return (f == FIELD_COUNT);
    993 }
    994 
    995 
    996 void 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 
   1035 static 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 
   1048 Boolean 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 */
   1097 void 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 */
   1127 void 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 
   1137 static 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 
   1152 static 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 */
   1166 static 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 */
   1191 void 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 
   1288 static 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 
   1306 static 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 
   1349 static 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 */
   1360 void 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  */
   1380 static 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 */
   1403 void 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 
   1466 void 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 
   1513 static void FlushXqueue(Display *dpy)
   1514 {
   1515     XSync(dpy, False);
   1516     while (XtAppPending(appCtx)) XtAppProcessEvent(appCtx, XtIMAll);
   1517 }
   1518 
   1519 
   1520 /* ARGSUSED */
   1521 void 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 
   1529 void 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 
   1542 static 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 
   1551 static Boolean
   1552 ConvertSelection(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 
   1576 static AtomPtr _XA_PRIMARY_FONT = NULL;
   1577 #define XA_PRIMARY_FONT XmuInternAtom(XtDisplay(w),_XA_PRIMARY_FONT)
   1578 
   1579 /* ARGSUSED */
   1580 static 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 */
   1592 static void DoneSelection(Widget w, Atom *selection, Atom *target)
   1593 {
   1594     /* do nothing */
   1595 }
   1596 
   1597 
   1598 /* ARGSUSED */
   1599 void 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*/
   1627 void
   1628 QuitAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
   1629 {
   1630     exit (0);
   1631 }
   1632