]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/gtkfont.c
Stop using GDK's wrapper on the X11 font functions (GdkFont). All X11
[PuTTY.git] / unix / gtkfont.c
1 /*
2  * Unified font management for GTK.
3  * 
4  * PuTTY is willing to use both old-style X server-side bitmap
5  * fonts _and_ GTK2/Pango client-side fonts. This requires us to
6  * do a bit of work to wrap the two wildly different APIs into
7  * forms the rest of the code can switch between seamlessly, and
8  * also requires a custom font selector capable of handling both
9  * types of font.
10  */
11
12 #include <assert.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <gtk/gtk.h>
16 #include <gdk/gdkkeysyms.h>
17 #include <gdk/gdkx.h>
18 #include <X11/Xlib.h>
19 #include <X11/Xutil.h>
20 #include <X11/Xatom.h>
21
22 #include "putty.h"
23 #include "gtkfont.h"
24 #include "tree234.h"
25
26 /*
27  * Future work:
28  * 
29  *  - it would be nice to have a display of the current font name,
30  *    and in particular whether it's client- or server-side,
31  *    during the progress of the font selector.
32  * 
33  *  - it would be nice if we could move the processing of
34  *    underline and VT100 double width into this module, so that
35  *    instead of using the ghastly pixmap-stretching technique
36  *    everywhere we could tell the Pango backend to scale its
37  *    fonts to double size properly and at full resolution.
38  *    However, this requires me to learn how to make Pango stretch
39  *    text to an arbitrary aspect ratio (for double-width only
40  *    text, which perversely is harder than DW+DH), and right now
41  *    I haven't the energy.
42  */
43
44 /*
45  * Ad-hoc vtable mechanism to allow font structures to be
46  * polymorphic.
47  * 
48  * Any instance of `unifont' used in the vtable functions will
49  * actually be the first element of a larger structure containing
50  * data specific to the subtype. This is permitted by the ISO C
51  * provision that one may safely cast between a pointer to a
52  * structure and a pointer to its first element.
53  */
54
55 #define FONTFLAG_CLIENTSIDE    0x0001
56 #define FONTFLAG_SERVERSIDE    0x0002
57 #define FONTFLAG_SERVERALIAS   0x0004
58 #define FONTFLAG_NONMONOSPACED 0x0008
59
60 #define FONTFLAG_SORT_MASK     0x0007 /* used to disambiguate font families */
61
62 typedef void (*fontsel_add_entry)(void *ctx, const char *realfontname,
63                                   const char *family, const char *charset,
64                                   const char *style, const char *stylekey,
65                                   int size, int flags,
66                                   const struct unifont_vtable *fontclass);
67
68 struct unifont_vtable {
69     /*
70      * `Methods' of the `class'.
71      */
72     unifont *(*create)(GtkWidget *widget, const char *name, int wide, int bold,
73                        int shadowoffset, int shadowalways);
74     void (*destroy)(unifont *font);
75     void (*draw_text)(GdkDrawable *target, GdkGC *gc, unifont *font,
76                       int x, int y, const char *string, int len, int wide,
77                       int bold, int cellwidth);
78     void (*enum_fonts)(GtkWidget *widget,
79                        fontsel_add_entry callback, void *callback_ctx);
80     char *(*canonify_fontname)(GtkWidget *widget, const char *name, int *size,
81                                int *flags, int resolve_aliases);
82     char *(*scale_fontname)(GtkWidget *widget, const char *name, int size);
83
84     /*
85      * `Static data members' of the `class'.
86      */
87     const char *prefix;
88 };
89
90 /* ----------------------------------------------------------------------
91  * X11 font implementation, directly using Xlib calls.
92  */
93
94 static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
95                               int x, int y, const char *string, int len,
96                               int wide, int bold, int cellwidth);
97 static unifont *x11font_create(GtkWidget *widget, const char *name,
98                                int wide, int bold,
99                                int shadowoffset, int shadowalways);
100 static void x11font_destroy(unifont *font);
101 static void x11font_enum_fonts(GtkWidget *widget,
102                                fontsel_add_entry callback, void *callback_ctx);
103 static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,
104                                        int *size, int *flags,
105                                        int resolve_aliases);
106 static char *x11font_scale_fontname(GtkWidget *widget, const char *name,
107                                     int size);
108
109 struct x11font {
110     struct unifont u;
111     /*
112      * Actual font objects. We store a number of these, for
113      * automatically guessed bold and wide variants.
114      * 
115      * The parallel array `allocated' indicates whether we've
116      * tried to fetch a subfont already (thus distinguishing NULL
117      * because we haven't tried yet from NULL because we tried and
118      * failed, so that we don't keep trying and failing
119      * subsequently).
120      */
121     XFontStruct *fonts[4];
122     int allocated[4];
123     /*
124      * `sixteen_bit' is true iff the font object is indexed by
125      * values larger than a byte. That is, this flag tells us
126      * whether we use XDrawString or XDrawString16, etc.
127      */
128     int sixteen_bit;
129     /*
130      * `variable' is true iff the font is non-fixed-pitch. This
131      * enables some code which takes greater care over character
132      * positioning during text drawing.
133      */
134     int variable;
135     /*
136      * Data passed in to unifont_create().
137      */
138     int wide, bold, shadowoffset, shadowalways;
139 };
140
141 static const struct unifont_vtable x11font_vtable = {
142     x11font_create,
143     x11font_destroy,
144     x11font_draw_text,
145     x11font_enum_fonts,
146     x11font_canonify_fontname,
147     x11font_scale_fontname,
148     "server",
149 };
150
151 static char *x11_guess_derived_font_name(XFontStruct *xfs, int bold, int wide)
152 {
153     Display *disp = GDK_DISPLAY();
154     Atom fontprop = XInternAtom(disp, "FONT", False);
155     unsigned long ret;
156     if (XGetFontProperty(xfs, fontprop, &ret)) {
157         char *name = XGetAtomName(disp, (Atom)ret);
158         if (name && name[0] == '-') {
159             char *strings[13];
160             char *dupname, *extrafree = NULL, *ret;
161             char *p, *q;
162             int nstr;
163
164             p = q = dupname = dupstr(name); /* skip initial minus */
165             nstr = 0;
166
167             while (*p && nstr < lenof(strings)) {
168                 if (*p == '-') {
169                     *p = '\0';
170                     strings[nstr++] = p+1;
171                 }
172                 p++;
173             }
174
175             if (nstr < lenof(strings))
176                 return NULL;           /* XLFD was malformed */
177
178             if (bold)
179                 strings[2] = "bold";
180
181             if (wide) {
182                 /* 4 is `wideness', which obviously may have changed. */
183                 /* 5 is additional style, which may be e.g. `ja' or `ko'. */
184                 strings[4] = strings[5] = "*";
185                 strings[11] = extrafree = dupprintf("%d", 2*atoi(strings[11]));
186             }
187
188             ret = dupcat("-", strings[ 0], "-", strings[ 1], "-", strings[ 2],
189                          "-", strings[ 3], "-", strings[ 4], "-", strings[ 5],
190                          "-", strings[ 6], "-", strings[ 7], "-", strings[ 8],
191                          "-", strings[ 9], "-", strings[10], "-", strings[11],
192                          "-", strings[12], NULL);
193             sfree(extrafree);
194             sfree(dupname);
195
196             return ret;
197         }
198     }
199     return NULL;
200 }
201
202 static int x11_font_width(XFontStruct *xfs, int sixteen_bit)
203 {
204     if (sixteen_bit) {
205         XChar2b space;
206         space.byte1 = 0;
207         space.byte2 = '0';
208         return XTextWidth16(xfs, &space, 1);
209     } else {
210         return XTextWidth(xfs, "0", 1);
211     }
212 }
213
214 static unifont *x11font_create(GtkWidget *widget, const char *name,
215                                int wide, int bold,
216                                int shadowoffset, int shadowalways)
217 {
218     struct x11font *xfont;
219     XFontStruct *xfs;
220     Display *disp = GDK_DISPLAY();
221     Atom charset_registry, charset_encoding, spacing;
222     unsigned long registry_ret, encoding_ret, spacing_ret;
223     int pubcs, realcs, sixteen_bit, variable;
224     int i;
225
226     xfs = XLoadQueryFont(disp, name);
227     if (!xfs)
228         return NULL;
229
230     charset_registry = XInternAtom(disp, "CHARSET_REGISTRY", False);
231     charset_encoding = XInternAtom(disp, "CHARSET_ENCODING", False);
232
233     pubcs = realcs = CS_NONE;
234     sixteen_bit = FALSE;
235     variable = TRUE;
236
237     if (XGetFontProperty(xfs, charset_registry, &registry_ret) &&
238         XGetFontProperty(xfs, charset_encoding, &encoding_ret)) {
239         char *reg, *enc;
240         reg = XGetAtomName(disp, (Atom)registry_ret);
241         enc = XGetAtomName(disp, (Atom)encoding_ret);
242         if (reg && enc) {
243             char *encoding = dupcat(reg, "-", enc, NULL);
244             pubcs = realcs = charset_from_xenc(encoding);
245
246             /*
247              * iso10646-1 is the only wide font encoding we
248              * support. In this case, we expect clients to give us
249              * UTF-8, which this module must internally convert
250              * into 16-bit Unicode.
251              */
252             if (!strcasecmp(encoding, "iso10646-1")) {
253                 sixteen_bit = TRUE;
254                 pubcs = realcs = CS_UTF8;
255             }
256
257             /*
258              * Hack for X line-drawing characters: if the primary
259              * font is encoded as ISO-8859-1, and has valid glyphs
260              * in the first 32 char positions, it is assumed that
261              * those glyphs are the VT100 line-drawing character
262              * set.
263              * 
264              * Actually, we'll hack even harder by only checking
265              * position 0x19 (vertical line, VT100 linedrawing
266              * `x'). Then we can check it easily by seeing if the
267              * ascent and descent differ.
268              */
269             if (pubcs == CS_ISO8859_1) {
270                 int dir, asc, desc;
271                 XCharStruct cs;
272                 XChar2b text;
273
274                 text.byte1 = '\0';
275                 text.byte2 = '\x12';
276                 XTextExtents16(xfs, &text, 1, &dir, &asc, &desc, &cs);
277                 if (asc != desc)
278                     realcs = CS_ISO8859_1_X11;
279             }
280
281             sfree(encoding);
282         }
283     }
284
285     spacing = XInternAtom(disp, "SPACING", False);
286     if (XGetFontProperty(xfs, spacing, &spacing_ret)) {
287         char *spc;
288         spc = XGetAtomName(disp, (Atom)spacing_ret);
289
290         if (spc && strchr("CcMm", spc[0]))
291             variable = FALSE;
292     }
293
294     xfont = snew(struct x11font);
295     xfont->u.vt = &x11font_vtable;
296     xfont->u.width = x11_font_width(xfs, sixteen_bit);
297     xfont->u.ascent = xfs->ascent;
298     xfont->u.descent = xfs->descent;
299     xfont->u.height = xfont->u.ascent + xfont->u.descent;
300     xfont->u.public_charset = pubcs;
301     xfont->u.real_charset = realcs;
302     xfont->fonts[0] = xfs;
303     xfont->allocated[0] = TRUE;
304     xfont->sixteen_bit = sixteen_bit;
305     xfont->variable = variable;
306     xfont->wide = wide;
307     xfont->bold = bold;
308     xfont->shadowoffset = shadowoffset;
309     xfont->shadowalways = shadowalways;
310
311     for (i = 1; i < lenof(xfont->fonts); i++) {
312         xfont->fonts[i] = NULL;
313         xfont->allocated[i] = FALSE;
314     }
315
316     return (unifont *)xfont;
317 }
318
319 static void x11font_destroy(unifont *font)
320 {
321     Display *disp = GDK_DISPLAY();
322     struct x11font *xfont = (struct x11font *)font;
323     int i;
324
325     for (i = 0; i < lenof(xfont->fonts); i++)
326         if (xfont->fonts[i])
327             XFreeFont(disp, xfont->fonts[i]);
328     sfree(font);
329 }
330
331 static void x11_alloc_subfont(struct x11font *xfont, int sfid)
332 {
333     Display *disp = GDK_DISPLAY();
334     char *derived_name = x11_guess_derived_font_name
335         (xfont->fonts[0], sfid & 1, !!(sfid & 2));
336     xfont->fonts[sfid] = XLoadQueryFont(disp, derived_name);
337     xfont->allocated[sfid] = TRUE;
338     sfree(derived_name);
339     /* Note that xfont->fonts[sfid] may still be NULL, if XLQF failed. */
340 }
341
342 #if !GTK_CHECK_VERSION(2,0,0)
343 #define GDK_DRAWABLE_XID(d) GDK_WINDOW_XWINDOW(d) /* GTK1's name for this */
344 #endif
345
346 static void x11font_really_draw_text_16(GdkDrawable *target, XFontStruct *xfs,
347                                         GC gc, int x, int y,
348                                         const XChar2b *string, int nchars,
349                                         int shadowoffset,
350                                         int fontvariable, int cellwidth)
351 {
352     Display *disp = GDK_DISPLAY();
353     int step, nsteps, centre;
354
355     if (fontvariable) {
356         /*
357          * In a variable-pitch font, we draw one character at a
358          * time, and centre it in the character cell.
359          */
360         step = 1;
361         nsteps = nchars;
362         centre = TRUE;
363     } else {
364         /*
365          * In a fixed-pitch font, we can draw the whole lot in one go.
366          */
367         step = nchars;
368         nsteps = 1;
369         centre = FALSE;
370     }
371
372     while (nsteps-- > 0) {
373         int X = x;
374         if (centre)
375             X += (cellwidth - XTextWidth16(xfs, string, step)) / 2;
376
377         XDrawString16(disp, GDK_DRAWABLE_XID(target), gc,
378                       X, y, string, step);
379         if (shadowoffset)
380             XDrawString16(disp, GDK_DRAWABLE_XID(target), gc,
381                           X + shadowoffset, y, string, step);
382
383         x += cellwidth;
384         string += step;
385     }
386 }
387
388 static void x11font_really_draw_text(GdkDrawable *target, XFontStruct *xfs,
389                                      GC gc, int x, int y,
390                                      const char *string, int nchars,
391                                      int shadowoffset,
392                                      int fontvariable, int cellwidth)
393 {
394     Display *disp = GDK_DISPLAY();
395     int step, nsteps, centre;
396
397     if (fontvariable) {
398         /*
399          * In a variable-pitch font, we draw one character at a
400          * time, and centre it in the character cell.
401          */
402         step = 1;
403         nsteps = nchars;
404         centre = TRUE;
405     } else {
406         /*
407          * In a fixed-pitch font, we can draw the whole lot in one go.
408          */
409         step = nchars;
410         nsteps = 1;
411         centre = FALSE;
412     }
413
414     while (nsteps-- > 0) {
415         int X = x;
416         if (centre)
417             X += (cellwidth - XTextWidth(xfs, string, step)) / 2;
418
419         XDrawString(disp, GDK_DRAWABLE_XID(target), gc,
420                     X, y, string, step);
421         if (shadowoffset)
422             XDrawString(disp, GDK_DRAWABLE_XID(target), gc,
423                         X + shadowoffset, y, string, step);
424
425         x += cellwidth;
426         string += step;
427     }
428 }
429
430 static void x11font_draw_text(GdkDrawable *target, GdkGC *gdkgc, unifont *font,
431                               int x, int y, const char *string, int len,
432                               int wide, int bold, int cellwidth)
433 {
434     Display *disp = GDK_DISPLAY();
435     struct x11font *xfont = (struct x11font *)font;
436     GC gc = GDK_GC_XGC(gdkgc);
437     int sfid;
438     int shadowoffset = 0;
439     int mult = (wide ? 2 : 1);
440
441     wide -= xfont->wide;
442     bold -= xfont->bold;
443
444     /*
445      * Decide which subfont we're using, and whether we have to
446      * use shadow bold.
447      */
448     if (xfont->shadowalways && bold) {
449         shadowoffset = xfont->shadowoffset;
450         bold = 0;
451     }
452     sfid = 2 * wide + bold;
453     if (!xfont->allocated[sfid])
454         x11_alloc_subfont(xfont, sfid);
455     if (bold && !xfont->fonts[sfid]) {
456         bold = 0;
457         shadowoffset = xfont->shadowoffset;
458         sfid = 2 * wide + bold;
459         if (!xfont->allocated[sfid])
460             x11_alloc_subfont(xfont, sfid);
461     }
462
463     if (!xfont->fonts[sfid])
464         return;                        /* we've tried our best, but no luck */
465
466     XSetFont(disp, gc, xfont->fonts[sfid]->fid);
467
468     if (xfont->sixteen_bit) {
469         /*
470          * This X font has 16-bit character indices, which means
471          * we expect our string to have been passed in UTF-8.
472          */
473         XChar2b *xcs;
474         wchar_t *wcs;
475         int nchars, maxchars, i;
476
477         /*
478          * Convert the input string to wide-character Unicode.
479          */
480         maxchars = 0;
481         for (i = 0; i < len; i++)
482             if ((unsigned char)string[i] <= 0x7F ||
483                 (unsigned char)string[i] >= 0xC0)
484                 maxchars++;
485         wcs = snewn(maxchars+1, wchar_t);
486         nchars = charset_to_unicode((char **)&string, &len, wcs, maxchars,
487                                     CS_UTF8, NULL, NULL, 0);
488         assert(nchars <= maxchars);
489         wcs[nchars] = L'\0';
490
491         xcs = snewn(nchars, XChar2b);
492         for (i = 0; i < nchars; i++) {
493             xcs[i].byte1 = wcs[i] >> 8;
494             xcs[i].byte2 = wcs[i];
495         }
496
497         x11font_really_draw_text_16(target, xfont->fonts[sfid], gc, x, y,
498                                     xcs, nchars, shadowoffset,
499                                     xfont->variable, cellwidth * mult);
500         sfree(xcs);
501         sfree(wcs);
502     } else {
503         x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y,
504                                  string, len, shadowoffset,
505                                  xfont->variable, cellwidth * mult);
506     }
507 }
508
509 static void x11font_enum_fonts(GtkWidget *widget,
510                                fontsel_add_entry callback, void *callback_ctx)
511 {
512     char **fontnames;
513     char *tmp = NULL;
514     int nnames, i, max, tmpsize;
515
516     max = 32768;
517     while (1) {
518         fontnames = XListFonts(GDK_DISPLAY(), "*", max, &nnames);
519         if (nnames >= max) {
520             XFreeFontNames(fontnames);
521             max *= 2;
522         } else
523             break;
524     }
525
526     tmpsize = 0;
527
528     for (i = 0; i < nnames; i++) {
529         if (fontnames[i][0] == '-') {
530             /*
531              * Dismember an XLFD and convert it into the format
532              * we'll be using in the font selector.
533              */
534             char *components[14];
535             char *p, *font, *style, *stylekey, *charset;
536             int j, weightkey, slantkey, setwidthkey;
537             int thistmpsize, fontsize, flags;
538
539             thistmpsize = 4 * strlen(fontnames[i]) + 256;
540             if (tmpsize < thistmpsize) {
541                 tmpsize = thistmpsize;
542                 tmp = sresize(tmp, tmpsize, char);
543             }
544             strcpy(tmp, fontnames[i]);
545
546             p = tmp;
547             for (j = 0; j < 14; j++) {
548                 if (*p)
549                     *p++ = '\0';
550                 components[j] = p;
551                 while (*p && *p != '-')
552                     p++;
553             }
554             *p++ = '\0';
555
556             /*
557              * Font name is made up of fields 0 and 1, in reverse
558              * order with parentheses. (This is what the GTK 1.2 X
559              * font selector does, and it seems to come out
560              * looking reasonably sensible.)
561              */
562             font = p;
563             p += 1 + sprintf(p, "%s (%s)", components[1], components[0]);
564
565             /*
566              * Charset is made up of fields 12 and 13.
567              */
568             charset = p;
569             p += 1 + sprintf(p, "%s-%s", components[12], components[13]);
570
571             /*
572              * Style is a mixture of quite a lot of the fields,
573              * with some strange formatting.
574              */
575             style = p;
576             p += sprintf(p, "%s", components[2][0] ? components[2] :
577                          "regular");
578             if (!g_strcasecmp(components[3], "i"))
579                 p += sprintf(p, " italic");
580             else if (!g_strcasecmp(components[3], "o"))
581                 p += sprintf(p, " oblique");
582             else if (!g_strcasecmp(components[3], "ri"))
583                 p += sprintf(p, " reverse italic");
584             else if (!g_strcasecmp(components[3], "ro"))
585                 p += sprintf(p, " reverse oblique");
586             else if (!g_strcasecmp(components[3], "ot"))
587                 p += sprintf(p, " other-slant");
588             if (components[4][0] && g_strcasecmp(components[4], "normal"))
589                 p += sprintf(p, " %s", components[4]);
590             if (!g_strcasecmp(components[10], "m"))
591                 p += sprintf(p, " [M]");
592             if (!g_strcasecmp(components[10], "c"))
593                 p += sprintf(p, " [C]");
594             if (components[5][0])
595                 p += sprintf(p, " %s", components[5]);
596
597             /*
598              * Style key is the same stuff as above, but with a
599              * couple of transformations done on it to make it
600              * sort more sensibly.
601              */
602             p++;
603             stylekey = p;
604             if (!g_strcasecmp(components[2], "medium") ||
605                 !g_strcasecmp(components[2], "regular") ||
606                 !g_strcasecmp(components[2], "normal") ||
607                 !g_strcasecmp(components[2], "book"))
608                 weightkey = 0;
609             else if (!g_strncasecmp(components[2], "demi", 4) ||
610                      !g_strncasecmp(components[2], "semi", 4))
611                 weightkey = 1;
612             else
613                 weightkey = 2;
614             if (!g_strcasecmp(components[3], "r"))
615                 slantkey = 0;
616             else if (!g_strncasecmp(components[3], "r", 1))
617                 slantkey = 2;
618             else
619                 slantkey = 1;
620             if (!g_strcasecmp(components[4], "normal"))
621                 setwidthkey = 0;
622             else
623                 setwidthkey = 1;
624
625             p += sprintf(p, "%04d%04d%s%04d%04d%s%04d%04d%s%04d%s%04d%s",
626                          weightkey,
627                          (int)strlen(components[2]), components[2],
628                          slantkey,
629                          (int)strlen(components[3]), components[3],
630                          setwidthkey,
631                          (int)strlen(components[4]), components[4],
632                          (int)strlen(components[10]), components[10],
633                          (int)strlen(components[5]), components[5]);
634
635             assert(p - tmp < thistmpsize);
636
637             /*
638              * Size is in pixels, for our application, so we
639              * derive it directly from the pixel size field,
640              * number 6.
641              */
642             fontsize = atoi(components[6]);
643
644             /*
645              * Flags: we need to know whether this is a monospaced
646              * font, which we do by examining the spacing field
647              * again.
648              */
649             flags = FONTFLAG_SERVERSIDE;
650             if (!strchr("CcMm", components[10][0]))
651                 flags |= FONTFLAG_NONMONOSPACED;
652
653             /*
654              * Not sure why, but sometimes the X server will
655              * deliver dummy font types in which fontsize comes
656              * out as zero. Filter those out.
657              */
658             if (fontsize)
659                 callback(callback_ctx, fontnames[i], font, charset,
660                          style, stylekey, fontsize, flags, &x11font_vtable);
661         } else {
662             /*
663              * This isn't an XLFD, so it must be an alias.
664              * Transmit it with mostly null data.
665              * 
666              * It would be nice to work out if it's monospaced
667              * here, but at the moment I can't see that being
668              * anything but computationally hideous. Ah well.
669              */
670             callback(callback_ctx, fontnames[i], fontnames[i], NULL,
671                      NULL, NULL, 0, FONTFLAG_SERVERALIAS, &x11font_vtable);
672         }
673     }
674     XFreeFontNames(fontnames);
675 }
676
677 static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,
678                                        int *size, int *flags,
679                                        int resolve_aliases)
680 {
681     /*
682      * When given an X11 font name to try to make sense of for a
683      * font selector, we must attempt to load it (to see if it
684      * exists), and then canonify it by extracting its FONT
685      * property, which should give its full XLFD even if what we
686      * originally had was a wildcard.
687      * 
688      * However, we must carefully avoid canonifying font
689      * _aliases_, unless specifically asked to, because the font
690      * selector treats them as worthwhile in their own right.
691      */
692     XFontStruct *xfs;
693     Display *disp = GDK_DISPLAY();
694     Atom fontprop, fontprop2;
695     unsigned long ret;
696
697     xfs = XLoadQueryFont(disp, name);
698
699     if (!xfs)
700         return NULL;                   /* didn't make sense to us, sorry */
701
702     fontprop = XInternAtom(disp, "FONT", False);
703
704     if (XGetFontProperty(xfs, fontprop, &ret)) {
705         char *newname = XGetAtomName(disp, (Atom)ret);
706         if (newname) {
707             unsigned long fsize = 12;
708
709             fontprop2 = XInternAtom(disp, "PIXEL_SIZE", False);
710             if (XGetFontProperty(xfs, fontprop2, &fsize) && fsize > 0) {
711                 *size = fsize;
712                 XFreeFont(disp, xfs);
713                 if (flags) {
714                     if (name[0] == '-' || resolve_aliases)
715                         *flags = FONTFLAG_SERVERSIDE;
716                     else
717                         *flags = FONTFLAG_SERVERALIAS;
718                 }
719                 return dupstr(name[0] == '-' || resolve_aliases ?
720                               newname : name);
721             }
722         }
723     }
724
725     XFreeFont(disp, xfs);
726
727     return NULL;                       /* something went wrong */
728 }
729
730 static char *x11font_scale_fontname(GtkWidget *widget, const char *name,
731                                     int size)
732 {
733     return NULL;                       /* shan't */
734 }
735
736 #if GTK_CHECK_VERSION(2,0,0)
737
738 /* ----------------------------------------------------------------------
739  * Pango font implementation (for GTK 2 only).
740  */
741
742 #if defined PANGO_PRE_1POINT4 && !defined PANGO_PRE_1POINT6
743 #define PANGO_PRE_1POINT6              /* make life easier for pre-1.4 folk */
744 #endif
745
746 static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
747                                 int x, int y, const char *string, int len,
748                                 int wide, int bold, int cellwidth);
749 static unifont *pangofont_create(GtkWidget *widget, const char *name,
750                                  int wide, int bold,
751                                  int shadowoffset, int shadowalways);
752 static void pangofont_destroy(unifont *font);
753 static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback,
754                                  void *callback_ctx);
755 static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name,
756                                          int *size, int *flags,
757                                          int resolve_aliases);
758 static char *pangofont_scale_fontname(GtkWidget *widget, const char *name,
759                                       int size);
760
761 struct pangofont {
762     struct unifont u;
763     /*
764      * Pango objects.
765      */
766     PangoFontDescription *desc;
767     PangoFontset *fset;
768     /*
769      * The containing widget.
770      */
771     GtkWidget *widget;
772     /*
773      * Data passed in to unifont_create().
774      */
775     int bold, shadowoffset, shadowalways;
776 };
777
778 static const struct unifont_vtable pangofont_vtable = {
779     pangofont_create,
780     pangofont_destroy,
781     pangofont_draw_text,
782     pangofont_enum_fonts,
783     pangofont_canonify_fontname,
784     pangofont_scale_fontname,
785     "client",
786 };
787
788 /*
789  * This function is used to rigorously validate a
790  * PangoFontDescription. Later versions of Pango have a nasty
791  * habit of accepting _any_ old string as input to
792  * pango_font_description_from_string and returning a font
793  * description which can actually be used to display text, even if
794  * they have to do it by falling back to their most default font.
795  * This is doubtless helpful in some situations, but not here,
796  * because we need to know if a Pango font string actually _makes
797  * sense_ in order to fall back to treating it as an X font name
798  * if it doesn't. So we check that the font family is actually one
799  * supported by Pango.
800  */
801 static int pangofont_check_desc_makes_sense(PangoContext *ctx,
802                                             PangoFontDescription *desc)
803 {
804 #ifndef PANGO_PRE_1POINT6
805     PangoFontMap *map;
806 #endif
807     PangoFontFamily **families;
808     int i, nfamilies, matched;
809
810     /*
811      * Ask Pango for a list of font families, and iterate through
812      * them to see if one of them matches the family in the
813      * PangoFontDescription.
814      */
815 #ifndef PANGO_PRE_1POINT6
816     map = pango_context_get_font_map(ctx);
817     if (!map)
818         return FALSE;
819     pango_font_map_list_families(map, &families, &nfamilies);
820 #else
821     pango_context_list_families(ctx, &families, &nfamilies);
822 #endif
823
824     matched = FALSE;
825     for (i = 0; i < nfamilies; i++) {
826         if (!g_strcasecmp(pango_font_family_get_name(families[i]),
827                           pango_font_description_get_family(desc))) {
828             matched = TRUE;
829             break;
830         }
831     }
832     g_free(families);
833
834     return matched;
835 }
836
837 static unifont *pangofont_create(GtkWidget *widget, const char *name,
838                                  int wide, int bold,
839                                  int shadowoffset, int shadowalways)
840 {
841     struct pangofont *pfont;
842     PangoContext *ctx;
843 #ifndef PANGO_PRE_1POINT6
844     PangoFontMap *map;
845 #endif
846     PangoFontDescription *desc;
847     PangoFontset *fset;
848     PangoFontMetrics *metrics;
849
850     desc = pango_font_description_from_string(name);
851     if (!desc)
852         return NULL;
853     ctx = gtk_widget_get_pango_context(widget);
854     if (!ctx) {
855         pango_font_description_free(desc);
856         return NULL;
857     }
858     if (!pangofont_check_desc_makes_sense(ctx, desc)) {
859         pango_font_description_free(desc);
860         return NULL;
861     }
862 #ifndef PANGO_PRE_1POINT6
863     map = pango_context_get_font_map(ctx);
864     if (!map) {
865         pango_font_description_free(desc);
866         return NULL;
867     }
868     fset = pango_font_map_load_fontset(map, ctx, desc,
869                                        pango_context_get_language(ctx));
870 #else
871     fset = pango_context_load_fontset(ctx, desc,
872                                       pango_context_get_language(ctx));
873 #endif
874     if (!fset) {
875         pango_font_description_free(desc);
876         return NULL;
877     }
878     metrics = pango_fontset_get_metrics(fset);
879     if (!metrics ||
880         pango_font_metrics_get_approximate_digit_width(metrics) == 0) {
881         pango_font_description_free(desc);
882         g_object_unref(fset);
883         return NULL;
884     }
885
886     pfont = snew(struct pangofont);
887     pfont->u.vt = &pangofont_vtable;
888     pfont->u.width =
889         PANGO_PIXELS(pango_font_metrics_get_approximate_digit_width(metrics));
890     pfont->u.ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));
891     pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
892     pfont->u.height = pfont->u.ascent + pfont->u.descent;
893     /* The Pango API is hardwired to UTF-8 */
894     pfont->u.public_charset = CS_UTF8;
895     pfont->u.real_charset = CS_UTF8;
896     pfont->desc = desc;
897     pfont->fset = fset;
898     pfont->widget = widget;
899     pfont->bold = bold;
900     pfont->shadowoffset = shadowoffset;
901     pfont->shadowalways = shadowalways;
902
903     pango_font_metrics_unref(metrics);
904
905     return (unifont *)pfont;
906 }
907
908 static void pangofont_destroy(unifont *font)
909 {
910     struct pangofont *pfont = (struct pangofont *)font;
911     pango_font_description_free(pfont->desc);
912     g_object_unref(pfont->fset);
913     sfree(font);
914 }
915
916 static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
917                                 int x, int y, const char *string, int len,
918                                 int wide, int bold, int cellwidth)
919 {
920     struct pangofont *pfont = (struct pangofont *)font;
921     PangoLayout *layout;
922     PangoRectangle rect;
923     int shadowbold = FALSE;
924
925     if (wide)
926         cellwidth *= 2;
927
928     y -= pfont->u.ascent;
929
930     layout = pango_layout_new(gtk_widget_get_pango_context(pfont->widget));
931     pango_layout_set_font_description(layout, pfont->desc);
932     if (bold > pfont->bold) {
933         if (pfont->shadowalways)
934             shadowbold = TRUE;
935         else {
936             PangoFontDescription *desc2 =
937                 pango_font_description_copy_static(pfont->desc);
938             pango_font_description_set_weight(desc2, PANGO_WEIGHT_BOLD);
939             pango_layout_set_font_description(layout, desc2);
940         }
941     }
942
943     while (len > 0) {
944         int clen, n;
945
946         /*
947          * We want to display every character from this string in
948          * the centre of its own character cell. In the worst case,
949          * this requires a separate text-drawing call for each
950          * character; but in the common case where the font is
951          * properly fixed-width, we can draw many characters in one
952          * go which is much faster.
953          *
954          * This still isn't really ideal. If you look at what
955          * happens in the X protocol as a result of all of this, you
956          * find - naturally enough - that each call to
957          * gdk_draw_layout() generates a separate set of X RENDER
958          * operations involving creating a picture, setting a clip
959          * rectangle, doing some drawing and undoing the whole lot.
960          * In an ideal world, we should _always_ be able to turn the
961          * contents of this loop into a single RenderCompositeGlyphs
962          * operation which internally specifies inter-character
963          * deltas to get the spacing right, which would give us full
964          * speed _even_ in the worst case of a non-fixed-width font.
965          * However, Pango's architecture and documentation are so
966          * unhelpful that I have no idea how if at all to persuade
967          * them to do that.
968          */
969
970         /*
971          * Start by extracting a single UTF-8 character from the
972          * string.
973          */
974         clen = 1;
975         while (clen < len &&
976                (unsigned char)string[clen] >= 0x80 &&
977                (unsigned char)string[clen] < 0xC0)
978             clen++;
979         n = 1;
980
981         /*
982          * See if that character has the width we expect.
983          */
984         pango_layout_set_text(layout, string, clen);
985         pango_layout_get_pixel_extents(layout, NULL, &rect);
986
987         if (rect.width == cellwidth) {
988             /*
989              * Try extracting more characters, for as long as they
990              * stay well-behaved.
991              */
992             while (clen < len) {
993                 int oldclen = clen;
994                 clen++;                /* skip UTF-8 introducer byte */
995                 while (clen < len &&
996                        (unsigned char)string[clen] >= 0x80 &&
997                        (unsigned char)string[clen] < 0xC0)
998                     clen++;
999                 n++;
1000                 pango_layout_set_text(layout, string, clen);
1001                 pango_layout_get_pixel_extents(layout, NULL, &rect);
1002                 if (rect.width != n * cellwidth) {
1003                     clen = oldclen;
1004                     n--;
1005                     break;
1006                 }
1007             }
1008         }
1009
1010         pango_layout_set_text(layout, string, clen);
1011         pango_layout_get_pixel_extents(layout, NULL, &rect);
1012         gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2,
1013                         y + (pfont->u.height - rect.height)/2, layout);
1014         if (shadowbold)
1015             gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2 + pfont->shadowoffset,
1016                             y + (pfont->u.height - rect.height)/2, layout);
1017
1018         len -= clen;
1019         string += clen;
1020         x += n * cellwidth;
1021     }
1022
1023     g_object_unref(layout);
1024 }
1025
1026 /*
1027  * Dummy size value to be used when converting a
1028  * PangoFontDescription of a scalable font to a string for
1029  * internal use.
1030  */
1031 #define PANGO_DUMMY_SIZE 12
1032
1033 static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback,
1034                                  void *callback_ctx)
1035 {
1036     PangoContext *ctx;
1037 #ifndef PANGO_PRE_1POINT6
1038     PangoFontMap *map;
1039 #endif
1040     PangoFontFamily **families;
1041     int i, nfamilies;
1042
1043     ctx = gtk_widget_get_pango_context(widget);
1044     if (!ctx)
1045         return;
1046
1047     /*
1048      * Ask Pango for a list of font families, and iterate through
1049      * them.
1050      */
1051 #ifndef PANGO_PRE_1POINT6
1052     map = pango_context_get_font_map(ctx);
1053     if (!map)
1054         return;
1055     pango_font_map_list_families(map, &families, &nfamilies);
1056 #else
1057     pango_context_list_families(ctx, &families, &nfamilies);
1058 #endif
1059     for (i = 0; i < nfamilies; i++) {
1060         PangoFontFamily *family = families[i];
1061         const char *familyname;
1062         int flags;
1063         PangoFontFace **faces;
1064         int j, nfaces;
1065
1066         /*
1067          * Set up our flags for this font family, and get the name
1068          * string.
1069          */
1070         flags = FONTFLAG_CLIENTSIDE;
1071 #ifndef PANGO_PRE_1POINT4
1072         /*
1073          * In very early versions of Pango, we can't tell
1074          * monospaced fonts from non-monospaced.
1075          */
1076         if (!pango_font_family_is_monospace(family))
1077             flags |= FONTFLAG_NONMONOSPACED;
1078 #endif
1079         familyname = pango_font_family_get_name(family);
1080
1081         /*
1082          * Go through the available font faces in this family.
1083          */
1084         pango_font_family_list_faces(family, &faces, &nfaces);
1085         for (j = 0; j < nfaces; j++) {
1086             PangoFontFace *face = faces[j];
1087             PangoFontDescription *desc;
1088             const char *facename;
1089             int *sizes;
1090             int k, nsizes, dummysize;
1091
1092             /*
1093              * Get the face name string.
1094              */
1095             facename = pango_font_face_get_face_name(face);
1096
1097             /*
1098              * Set up a font description with what we've got so
1099              * far. We'll fill in the size field manually and then
1100              * call pango_font_description_to_string() to give the
1101              * full real name of the specific font.
1102              */
1103             desc = pango_font_face_describe(face);
1104
1105             /*
1106              * See if this font has a list of specific sizes.
1107              */
1108 #ifndef PANGO_PRE_1POINT4
1109             pango_font_face_list_sizes(face, &sizes, &nsizes);
1110 #else
1111             /*
1112              * In early versions of Pango, that call wasn't
1113              * supported; we just have to assume everything is
1114              * scalable.
1115              */
1116             sizes = NULL;
1117 #endif
1118             if (!sizes) {
1119                 /*
1120                  * Write a single entry with a dummy size.
1121                  */
1122                 dummysize = PANGO_DUMMY_SIZE * PANGO_SCALE;
1123                 sizes = &dummysize;
1124                 nsizes = 1;
1125             }
1126
1127             /*
1128              * If so, go through them one by one.
1129              */
1130             for (k = 0; k < nsizes; k++) {
1131                 char *fullname;
1132                 char stylekey[128];
1133
1134                 pango_font_description_set_size(desc, sizes[k]);
1135
1136                 fullname = pango_font_description_to_string(desc);
1137
1138                 /*
1139                  * Construct the sorting key for font styles.
1140                  */
1141                 {
1142                     char *p = stylekey;
1143                     int n;
1144
1145                     n = pango_font_description_get_weight(desc);
1146                     /* Weight: normal, then lighter, then bolder */
1147                     if (n <= PANGO_WEIGHT_NORMAL)
1148                         n = PANGO_WEIGHT_NORMAL - n;
1149                     p += sprintf(p, "%4d", n);
1150
1151                     n = pango_font_description_get_style(desc);
1152                     p += sprintf(p, " %2d", n);
1153
1154                     n = pango_font_description_get_stretch(desc);
1155                     /* Stretch: closer to normal sorts earlier */
1156                     n = 2 * abs(PANGO_STRETCH_NORMAL - n) +
1157                         (n < PANGO_STRETCH_NORMAL);
1158                     p += sprintf(p, " %2d", n);
1159
1160                     n = pango_font_description_get_variant(desc);
1161                     p += sprintf(p, " %2d", n);
1162                     
1163                 }
1164
1165                 /*
1166                  * Got everything. Hand off to the callback.
1167                  * (The charset string is NULL, because only
1168                  * server-side X fonts use it.)
1169                  */
1170                 callback(callback_ctx, fullname, familyname, NULL, facename,
1171                          stylekey,
1172                          (sizes == &dummysize ? 0 : PANGO_PIXELS(sizes[k])),
1173                          flags, &pangofont_vtable);
1174
1175                 g_free(fullname);
1176             }
1177             if (sizes != &dummysize)
1178                 g_free(sizes);
1179
1180             pango_font_description_free(desc);
1181         }
1182         g_free(faces);
1183     }
1184     g_free(families);
1185 }
1186
1187 static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name,
1188                                          int *size, int *flags,
1189                                          int resolve_aliases)
1190 {
1191     /*
1192      * When given a Pango font name to try to make sense of for a
1193      * font selector, we must normalise it to PANGO_DUMMY_SIZE and
1194      * extract its original size (in pixels) into the `size' field.
1195      */
1196     PangoContext *ctx;
1197 #ifndef PANGO_PRE_1POINT6
1198     PangoFontMap *map;
1199 #endif
1200     PangoFontDescription *desc;
1201     PangoFontset *fset;
1202     PangoFontMetrics *metrics;
1203     char *newname, *retname;
1204
1205     desc = pango_font_description_from_string(name);
1206     if (!desc)
1207         return NULL;
1208     ctx = gtk_widget_get_pango_context(widget);
1209     if (!ctx) {
1210         pango_font_description_free(desc);
1211         return NULL;
1212     }
1213     if (!pangofont_check_desc_makes_sense(ctx, desc)) {
1214         pango_font_description_free(desc);
1215         return NULL;
1216     }
1217 #ifndef PANGO_PRE_1POINT6
1218     map = pango_context_get_font_map(ctx);
1219     if (!map) {
1220         pango_font_description_free(desc);
1221         return NULL;
1222     }
1223     fset = pango_font_map_load_fontset(map, ctx, desc,
1224                                        pango_context_get_language(ctx));
1225 #else
1226     fset = pango_context_load_fontset(ctx, desc,
1227                                       pango_context_get_language(ctx));
1228 #endif
1229     if (!fset) {
1230         pango_font_description_free(desc);
1231         return NULL;
1232     }
1233     metrics = pango_fontset_get_metrics(fset);
1234     if (!metrics ||
1235         pango_font_metrics_get_approximate_digit_width(metrics) == 0) {
1236         pango_font_description_free(desc);
1237         g_object_unref(fset);
1238         return NULL;
1239     }
1240
1241     *size = PANGO_PIXELS(pango_font_description_get_size(desc));
1242     *flags = FONTFLAG_CLIENTSIDE;
1243     pango_font_description_set_size(desc, PANGO_DUMMY_SIZE * PANGO_SCALE);
1244     newname = pango_font_description_to_string(desc);
1245     retname = dupstr(newname);
1246     g_free(newname);
1247
1248     pango_font_metrics_unref(metrics);
1249     pango_font_description_free(desc);
1250     g_object_unref(fset);
1251
1252     return retname;
1253 }
1254
1255 static char *pangofont_scale_fontname(GtkWidget *widget, const char *name,
1256                                       int size)
1257 {
1258     PangoFontDescription *desc;
1259     char *newname, *retname;
1260
1261     desc = pango_font_description_from_string(name);
1262     if (!desc)
1263         return NULL;
1264     pango_font_description_set_size(desc, size * PANGO_SCALE);
1265     newname = pango_font_description_to_string(desc);
1266     retname = dupstr(newname);
1267     g_free(newname);
1268     pango_font_description_free(desc);
1269
1270     return retname;
1271 }
1272
1273 #endif /* GTK_CHECK_VERSION(2,0,0) */
1274
1275 /* ----------------------------------------------------------------------
1276  * Outermost functions which do the vtable dispatch.
1277  */
1278
1279 /*
1280  * Complete list of font-type subclasses. Listed in preference
1281  * order for unifont_create(). (That is, in the extremely unlikely
1282  * event that the same font name is valid as both a Pango and an
1283  * X11 font, it will be interpreted as the former in the absence
1284  * of an explicit type-disambiguating prefix.)
1285  */
1286 static const struct unifont_vtable *unifont_types[] = {
1287 #if GTK_CHECK_VERSION(2,0,0)
1288     &pangofont_vtable,
1289 #endif
1290     &x11font_vtable,
1291 };
1292
1293 /*
1294  * Function which takes a font name and processes the optional
1295  * scheme prefix. Returns the tail of the font name suitable for
1296  * passing to individual font scheme functions, and also provides
1297  * a subrange of the unifont_types[] array above.
1298  * 
1299  * The return values `start' and `end' denote a half-open interval
1300  * in unifont_types[]; that is, the correct way to iterate over
1301  * them is
1302  * 
1303  *   for (i = start; i < end; i++) {...}
1304  */
1305 static const char *unifont_do_prefix(const char *name, int *start, int *end)
1306 {
1307     int colonpos = strcspn(name, ":");
1308     int i;
1309
1310     if (name[colonpos]) {
1311         /*
1312          * There's a colon prefix on the font name. Use it to work
1313          * out which subclass to use.
1314          */
1315         for (i = 0; i < lenof(unifont_types); i++) {
1316             if (strlen(unifont_types[i]->prefix) == colonpos &&
1317                 !strncmp(unifont_types[i]->prefix, name, colonpos)) {
1318                 *start = i;
1319                 *end = i+1;
1320                 return name + colonpos + 1;
1321             }
1322         }
1323         /*
1324          * None matched, so return an empty scheme list to prevent
1325          * any scheme from being called at all.
1326          */
1327         *start = *end = 0;
1328         return name + colonpos + 1;
1329     } else {
1330         /*
1331          * No colon prefix, so just use all the subclasses.
1332          */
1333         *start = 0;
1334         *end = lenof(unifont_types);
1335         return name;
1336     }
1337 }
1338
1339 unifont *unifont_create(GtkWidget *widget, const char *name, int wide,
1340                         int bold, int shadowoffset, int shadowalways)
1341 {
1342     int i, start, end;
1343
1344     name = unifont_do_prefix(name, &start, &end);
1345
1346     for (i = start; i < end; i++) {
1347         unifont *ret = unifont_types[i]->create(widget, name, wide, bold,
1348                                                 shadowoffset, shadowalways);
1349         if (ret)
1350             return ret;
1351     }
1352     return NULL;                       /* font not found in any scheme */
1353 }
1354
1355 void unifont_destroy(unifont *font)
1356 {
1357     font->vt->destroy(font);
1358 }
1359
1360 void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
1361                        int x, int y, const char *string, int len,
1362                        int wide, int bold, int cellwidth)
1363 {
1364     font->vt->draw_text(target, gc, font, x, y, string, len,
1365                         wide, bold, cellwidth);
1366 }
1367
1368 #if GTK_CHECK_VERSION(2,0,0)
1369
1370 /* ----------------------------------------------------------------------
1371  * Implementation of a unified font selector. Used on GTK 2 only;
1372  * for GTK 1 we still use the standard font selector.
1373  */
1374
1375 typedef struct fontinfo fontinfo;
1376
1377 typedef struct unifontsel_internal {
1378     /* This must be the structure's first element, for cross-casting */
1379     unifontsel u;
1380     GtkListStore *family_model, *style_model, *size_model;
1381     GtkWidget *family_list, *style_list, *size_entry, *size_list;
1382     GtkWidget *filter_buttons[4];
1383     GtkWidget *preview_area;
1384     GdkPixmap *preview_pixmap;
1385     int preview_width, preview_height;
1386     GdkColor preview_fg, preview_bg;
1387     int filter_flags;
1388     tree234 *fonts_by_realname, *fonts_by_selorder;
1389     fontinfo *selected;
1390     int selsize, intendedsize;
1391     int inhibit_response;  /* inhibit callbacks when we change GUI controls */
1392 } unifontsel_internal;
1393
1394 /*
1395  * The structure held in the tree234s. All the string members are
1396  * part of the same allocated area, so don't need freeing
1397  * separately.
1398  */
1399 struct fontinfo {
1400     char *realname;
1401     char *family, *charset, *style, *stylekey;
1402     int size, flags;
1403     /*
1404      * Fallback sorting key, to permit multiple identical entries
1405      * to exist in the selorder tree.
1406      */
1407     int index;
1408     /*
1409      * Indices mapping fontinfo structures to indices in the list
1410      * boxes. sizeindex is irrelevant if the font is scalable
1411      * (size==0).
1412      */
1413     int familyindex, styleindex, sizeindex;
1414     /*
1415      * The class of font.
1416      */
1417     const struct unifont_vtable *fontclass;
1418 };
1419
1420 struct fontinfo_realname_find {
1421     const char *realname;
1422     int flags;
1423 };
1424
1425 static int strnullcasecmp(const char *a, const char *b)
1426 {
1427     int i;
1428
1429     /*
1430      * If exactly one of the inputs is NULL, it compares before
1431      * the other one.
1432      */
1433     if ((i = (!b) - (!a)) != 0)
1434         return i;
1435
1436     /*
1437      * NULL compares equal.
1438      */
1439     if (!a)
1440         return 0;
1441
1442     /*
1443      * Otherwise, ordinary strcasecmp.
1444      */
1445     return g_strcasecmp(a, b);
1446 }
1447
1448 static int fontinfo_realname_compare(void *av, void *bv)
1449 {
1450     fontinfo *a = (fontinfo *)av;
1451     fontinfo *b = (fontinfo *)bv;
1452     int i;
1453
1454     if ((i = strnullcasecmp(a->realname, b->realname)) != 0)
1455         return i;
1456     if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))
1457         return ((a->flags & FONTFLAG_SORT_MASK) <
1458                 (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);
1459     return 0;
1460 }
1461
1462 static int fontinfo_realname_find(void *av, void *bv)
1463 {
1464     struct fontinfo_realname_find *a = (struct fontinfo_realname_find *)av;
1465     fontinfo *b = (fontinfo *)bv;
1466     int i;
1467
1468     if ((i = strnullcasecmp(a->realname, b->realname)) != 0)
1469         return i;
1470     if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))
1471         return ((a->flags & FONTFLAG_SORT_MASK) <
1472                 (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);
1473     return 0;
1474 }
1475
1476 static int fontinfo_selorder_compare(void *av, void *bv)
1477 {
1478     fontinfo *a = (fontinfo *)av;
1479     fontinfo *b = (fontinfo *)bv;
1480     int i;
1481     if ((i = strnullcasecmp(a->family, b->family)) != 0)
1482         return i;
1483     /*
1484      * Font class comes immediately after family, so that fonts
1485      * from different classes with the same family
1486      */
1487     if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))
1488         return ((a->flags & FONTFLAG_SORT_MASK) <
1489                 (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);
1490     if ((i = strnullcasecmp(a->charset, b->charset)) != 0)
1491         return i;
1492     if ((i = strnullcasecmp(a->stylekey, b->stylekey)) != 0)
1493         return i;
1494     if ((i = strnullcasecmp(a->style, b->style)) != 0)
1495         return i;
1496     if (a->size != b->size)
1497         return (a->size < b->size ? -1 : +1);
1498     if (a->index != b->index)
1499         return (a->index < b->index ? -1 : +1);
1500     return 0;
1501 }
1502
1503 static void unifontsel_deselect(unifontsel_internal *fs)
1504 {
1505     fs->selected = NULL;
1506     gtk_list_store_clear(fs->style_model);
1507     gtk_list_store_clear(fs->size_model);
1508     gtk_widget_set_sensitive(fs->u.ok_button, FALSE);
1509     gtk_widget_set_sensitive(fs->size_entry, FALSE);
1510 }
1511
1512 static void unifontsel_setup_familylist(unifontsel_internal *fs)
1513 {
1514     GtkTreeIter iter;
1515     int i, listindex, minpos = -1, maxpos = -1;
1516     char *currfamily = NULL;
1517     int currflags = -1;
1518     fontinfo *info;
1519
1520     gtk_list_store_clear(fs->family_model);
1521     listindex = 0;
1522
1523     /*
1524      * Search through the font tree for anything matching our
1525      * current filter criteria. When we find one, add its font
1526      * name to the list box.
1527      */
1528     for (i = 0 ;; i++) {
1529         info = (fontinfo *)index234(fs->fonts_by_selorder, i);
1530         /*
1531          * info may be NULL if we've just run off the end of the
1532          * tree. We must still do a processing pass in that
1533          * situation, in case we had an unfinished font record in
1534          * progress.
1535          */
1536         if (info && (info->flags &~ fs->filter_flags)) {
1537             info->familyindex = -1;
1538             continue;                  /* we're filtering out this font */
1539         }
1540         if (!info || strnullcasecmp(currfamily, info->family) ||
1541             currflags != (info->flags & FONTFLAG_SORT_MASK)) {
1542             /*
1543              * We've either finished a family, or started a new
1544              * one, or both.
1545              */
1546             if (currfamily) {
1547                 gtk_list_store_append(fs->family_model, &iter);
1548                 gtk_list_store_set(fs->family_model, &iter,
1549                                    0, currfamily, 1, minpos, 2, maxpos+1, -1);
1550                 listindex++;
1551             }
1552             if (info) {
1553                 minpos = i;
1554                 currfamily = info->family;
1555                 currflags = info->flags & FONTFLAG_SORT_MASK;
1556             }
1557         }
1558         if (!info)
1559             break;                     /* now we're done */
1560         info->familyindex = listindex;
1561         maxpos = i;
1562     }
1563
1564     /*
1565      * If we've just filtered out the previously selected font,
1566      * deselect it thoroughly.
1567      */
1568     if (fs->selected && fs->selected->familyindex < 0)
1569         unifontsel_deselect(fs);
1570 }
1571
1572 static void unifontsel_setup_stylelist(unifontsel_internal *fs,
1573                                        int start, int end)
1574 {
1575     GtkTreeIter iter;
1576     int i, listindex, minpos = -1, maxpos = -1, started = FALSE;
1577     char *currcs = NULL, *currstyle = NULL;
1578     fontinfo *info;
1579
1580     gtk_list_store_clear(fs->style_model);
1581     listindex = 0;
1582     started = FALSE;
1583
1584     /*
1585      * Search through the font tree for anything matching our
1586      * current filter criteria. When we find one, add its charset
1587      * and/or style name to the list box.
1588      */
1589     for (i = start; i <= end; i++) {
1590         if (i == end)
1591             info = NULL;
1592         else
1593             info = (fontinfo *)index234(fs->fonts_by_selorder, i);
1594         /*
1595          * info may be NULL if we've just run off the end of the
1596          * relevant data. We must still do a processing pass in
1597          * that situation, in case we had an unfinished font
1598          * record in progress.
1599          */
1600         if (info && (info->flags &~ fs->filter_flags)) {
1601             info->styleindex = -1;
1602             continue;                  /* we're filtering out this font */
1603         }
1604         if (!info || !started || strnullcasecmp(currcs, info->charset) ||
1605              strnullcasecmp(currstyle, info->style)) {
1606             /*
1607              * We've either finished a style/charset, or started a
1608              * new one, or both.
1609              */
1610             started = TRUE;
1611             if (currstyle) {
1612                 gtk_list_store_append(fs->style_model, &iter);
1613                 gtk_list_store_set(fs->style_model, &iter,
1614                                    0, currstyle, 1, minpos, 2, maxpos+1,
1615                                    3, TRUE, -1);
1616                 listindex++;
1617             }
1618             if (info) {
1619                 minpos = i;
1620                 if (info->charset && strnullcasecmp(currcs, info->charset)) {
1621                     gtk_list_store_append(fs->style_model, &iter);
1622                     gtk_list_store_set(fs->style_model, &iter,
1623                                        0, info->charset, 1, -1, 2, -1,
1624                                        3, FALSE, -1);
1625                     listindex++;
1626                 }
1627                 currcs = info->charset;
1628                 currstyle = info->style;
1629             }
1630         }
1631         if (!info)
1632             break;                     /* now we're done */
1633         info->styleindex = listindex;
1634         maxpos = i;
1635     }
1636 }
1637
1638 static const int unifontsel_default_sizes[] = { 10, 12, 14, 16, 20, 24, 32 };
1639
1640 static void unifontsel_setup_sizelist(unifontsel_internal *fs,
1641                                       int start, int end)
1642 {
1643     GtkTreeIter iter;
1644     int i, listindex;
1645     char sizetext[40];
1646     fontinfo *info;
1647
1648     gtk_list_store_clear(fs->size_model);
1649     listindex = 0;
1650
1651     /*
1652      * Search through the font tree for anything matching our
1653      * current filter criteria. When we find one, add its font
1654      * name to the list box.
1655      */
1656     for (i = start; i < end; i++) {
1657         info = (fontinfo *)index234(fs->fonts_by_selorder, i);
1658         if (info->flags &~ fs->filter_flags) {
1659             info->sizeindex = -1;
1660             continue;                  /* we're filtering out this font */
1661         }
1662         if (info->size) {
1663             sprintf(sizetext, "%d", info->size);
1664             info->sizeindex = listindex;
1665             gtk_list_store_append(fs->size_model, &iter);
1666             gtk_list_store_set(fs->size_model, &iter,
1667                                0, sizetext, 1, i, 2, info->size, -1);
1668             listindex++;
1669         } else {
1670             int j;
1671
1672             assert(i == start);
1673             assert(i+1 == end);
1674
1675             for (j = 0; j < lenof(unifontsel_default_sizes); j++) {
1676                 sprintf(sizetext, "%d", unifontsel_default_sizes[j]);
1677                 gtk_list_store_append(fs->size_model, &iter);
1678                 gtk_list_store_set(fs->size_model, &iter, 0, sizetext, 1, i,
1679                                    2, unifontsel_default_sizes[j], -1);
1680                 listindex++;
1681             }
1682         }
1683     }
1684 }
1685
1686 static void unifontsel_set_filter_buttons(unifontsel_internal *fs)
1687 {
1688     int i;
1689
1690     for (i = 0; i < lenof(fs->filter_buttons); i++) {
1691         int flagbit = GPOINTER_TO_INT(gtk_object_get_data
1692                                       (GTK_OBJECT(fs->filter_buttons[i]),
1693                                        "user-data"));
1694         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fs->filter_buttons[i]),
1695                                      !!(fs->filter_flags & flagbit));
1696     }
1697 }
1698
1699 static void unifontsel_draw_preview_text(unifontsel_internal *fs)
1700 {
1701     unifont *font;
1702     char *sizename = NULL;
1703     fontinfo *info = fs->selected;
1704
1705     if (info) {
1706         sizename = info->fontclass->scale_fontname
1707             (GTK_WIDGET(fs->u.window), info->realname, fs->selsize);
1708         font = info->fontclass->create(GTK_WIDGET(fs->u.window),
1709                                        sizename ? sizename : info->realname,
1710                                        FALSE, FALSE, 0, 0);
1711     } else
1712         font = NULL;
1713
1714     if (fs->preview_pixmap) {
1715         GdkGC *gc = gdk_gc_new(fs->preview_pixmap);
1716         gdk_gc_set_foreground(gc, &fs->preview_bg);
1717         gdk_draw_rectangle(fs->preview_pixmap, gc, 1, 0, 0,
1718                            fs->preview_width, fs->preview_height);
1719         gdk_gc_set_foreground(gc, &fs->preview_fg);
1720         if (font) {
1721             /*
1722              * The pangram used here is rather carefully
1723              * constructed: it contains a sequence of very narrow
1724              * letters (`jil') and a pair of adjacent very wide
1725              * letters (`wm').
1726              *
1727              * If the user selects a proportional font, it will be
1728              * coerced into fixed-width character cells when used
1729              * in the actual terminal window. We therefore display
1730              * it the same way in the preview pane, so as to show
1731              * it the way it will actually be displayed - and we
1732              * deliberately pick a pangram which will show the
1733              * resulting miskerning at its worst.
1734              *
1735              * We aren't trying to sell people these fonts; we're
1736              * trying to let them make an informed choice. Better
1737              * that they find out the problems with using
1738              * proportional fonts in terminal windows here than
1739              * that they go to the effort of selecting their font
1740              * and _then_ realise it was a mistake.
1741              */
1742             info->fontclass->draw_text(fs->preview_pixmap, gc, font,
1743                                        0, font->ascent,
1744                                        "bankrupt jilted showmen quiz convex fogey",
1745                                        41, FALSE, FALSE, font->width);
1746             info->fontclass->draw_text(fs->preview_pixmap, gc, font,
1747                                        0, font->ascent + font->height,
1748                                        "BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY",
1749                                        41, FALSE, FALSE, font->width);
1750             /*
1751              * The ordering of punctuation here is also selected
1752              * with some specific aims in mind. I put ` and '
1753              * together because some software (and people) still
1754              * use them as matched quotes no matter what Unicode
1755              * might say on the matter, so people can quickly
1756              * check whether they look silly in a candidate font.
1757              * The sequence #_@ is there to let people judge the
1758              * suitability of the underscore as an effectively
1759              * alphabetic character (since that's how it's often
1760              * used in practice, at least by programmers).
1761              */
1762             info->fontclass->draw_text(fs->preview_pixmap, gc, font,
1763                                        0, font->ascent + font->height * 2,
1764                                        "0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$",
1765                                        42, FALSE, FALSE, font->width);
1766         }
1767         gdk_gc_unref(gc);
1768         gdk_window_invalidate_rect(fs->preview_area->window, NULL, FALSE);
1769     }
1770     if (font)
1771         info->fontclass->destroy(font);
1772
1773     sfree(sizename);
1774 }
1775
1776 static void unifontsel_select_font(unifontsel_internal *fs,
1777                                    fontinfo *info, int size, int leftlist,
1778                                    int size_is_explicit)
1779 {
1780     int index;
1781     int minval, maxval;
1782     GtkTreePath *treepath;
1783     GtkTreeIter iter;
1784
1785     fs->inhibit_response = TRUE;
1786
1787     fs->selected = info;
1788     fs->selsize = size;
1789     if (size_is_explicit)
1790         fs->intendedsize = size;
1791
1792     gtk_widget_set_sensitive(fs->u.ok_button, TRUE);
1793
1794     /*
1795      * Find the index of this fontinfo in the selorder list. 
1796      */
1797     index = -1;
1798     findpos234(fs->fonts_by_selorder, info, NULL, &index);
1799     assert(index >= 0);
1800
1801     /*
1802      * Adjust the font selector flags and redo the font family
1803      * list box, if necessary.
1804      */
1805     if (leftlist <= 0 &&
1806         (fs->filter_flags | info->flags) != fs->filter_flags) {
1807         fs->filter_flags |= info->flags;
1808         unifontsel_set_filter_buttons(fs);
1809         unifontsel_setup_familylist(fs);
1810     }
1811
1812     /*
1813      * Find the appropriate family name and select it in the list.
1814      */
1815     assert(info->familyindex >= 0);
1816     treepath = gtk_tree_path_new_from_indices(info->familyindex, -1);
1817     gtk_tree_selection_select_path
1818         (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->family_list)),
1819          treepath);
1820     gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->family_list),
1821                                  treepath, NULL, FALSE, 0.0, 0.0);
1822     gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, treepath);
1823     gtk_tree_path_free(treepath);
1824
1825     /*
1826      * Now set up the font style list.
1827      */
1828     gtk_tree_model_get(GTK_TREE_MODEL(fs->family_model), &iter,
1829                        1, &minval, 2, &maxval, -1);
1830     if (leftlist <= 1)
1831         unifontsel_setup_stylelist(fs, minval, maxval);
1832
1833     /*
1834      * Find the appropriate style name and select it in the list.
1835      */
1836     if (info->style) {
1837         assert(info->styleindex >= 0);
1838         treepath = gtk_tree_path_new_from_indices(info->styleindex, -1);
1839         gtk_tree_selection_select_path
1840             (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->style_list)),
1841              treepath);
1842         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->style_list),
1843                                      treepath, NULL, FALSE, 0.0, 0.0);
1844         gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->style_model),
1845                                 &iter, treepath);
1846         gtk_tree_path_free(treepath);
1847
1848         /*
1849          * And set up the size list.
1850          */
1851         gtk_tree_model_get(GTK_TREE_MODEL(fs->style_model), &iter,
1852                            1, &minval, 2, &maxval, -1);
1853         if (leftlist <= 2)
1854             unifontsel_setup_sizelist(fs, minval, maxval);
1855
1856         /*
1857          * Find the appropriate size, and select it in the list.
1858          */
1859         if (info->size) {
1860             assert(info->sizeindex >= 0);
1861             treepath = gtk_tree_path_new_from_indices(info->sizeindex, -1);
1862             gtk_tree_selection_select_path
1863                 (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->size_list)),
1864                  treepath);
1865             gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->size_list),
1866                                          treepath, NULL, FALSE, 0.0, 0.0);
1867             gtk_tree_path_free(treepath);
1868             size = info->size;
1869         } else {
1870             int j;
1871             for (j = 0; j < lenof(unifontsel_default_sizes); j++)
1872                 if (unifontsel_default_sizes[j] == size) {
1873                     treepath = gtk_tree_path_new_from_indices(j, -1);
1874                     gtk_tree_view_set_cursor(GTK_TREE_VIEW(fs->size_list),
1875                                              treepath, NULL, FALSE);
1876                     gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->size_list),
1877                                                  treepath, NULL, FALSE, 0.0,
1878                                                  0.0);
1879                     gtk_tree_path_free(treepath);
1880                 }
1881         }
1882
1883         /*
1884          * And set up the font size text entry box.
1885          */
1886         {
1887             char sizetext[40];
1888             sprintf(sizetext, "%d", size);
1889             gtk_entry_set_text(GTK_ENTRY(fs->size_entry), sizetext);
1890         }
1891     } else {
1892         if (leftlist <= 2)
1893             unifontsel_setup_sizelist(fs, 0, 0);
1894         gtk_entry_set_text(GTK_ENTRY(fs->size_entry), "");
1895     }
1896
1897     /*
1898      * Grey out the font size edit box if we're not using a
1899      * scalable font.
1900      */
1901     gtk_entry_set_editable(GTK_ENTRY(fs->size_entry), fs->selected->size == 0);
1902     gtk_widget_set_sensitive(fs->size_entry, fs->selected->size == 0);
1903
1904     unifontsel_draw_preview_text(fs);
1905
1906     fs->inhibit_response = FALSE;
1907 }
1908
1909 static void unifontsel_button_toggled(GtkToggleButton *tb, gpointer data)
1910 {
1911     unifontsel_internal *fs = (unifontsel_internal *)data;
1912     int newstate = gtk_toggle_button_get_active(tb);
1913     int newflags;
1914     int flagbit = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(tb),
1915                                                       "user-data"));
1916
1917     if (newstate)
1918         newflags = fs->filter_flags | flagbit;
1919     else
1920         newflags = fs->filter_flags & ~flagbit;
1921
1922     if (fs->filter_flags != newflags) {
1923         fs->filter_flags = newflags;
1924         unifontsel_setup_familylist(fs);
1925     }
1926 }
1927
1928 static void unifontsel_add_entry(void *ctx, const char *realfontname,
1929                                  const char *family, const char *charset,
1930                                  const char *style, const char *stylekey,
1931                                  int size, int flags,
1932                                  const struct unifont_vtable *fontclass)
1933 {
1934     unifontsel_internal *fs = (unifontsel_internal *)ctx;
1935     fontinfo *info;
1936     int totalsize;
1937     char *p;
1938
1939     totalsize = sizeof(fontinfo) + strlen(realfontname) +
1940         (family ? strlen(family) : 0) + (charset ? strlen(charset) : 0) +
1941         (style ? strlen(style) : 0) + (stylekey ? strlen(stylekey) : 0) + 10;
1942     info = (fontinfo *)smalloc(totalsize);
1943     info->fontclass = fontclass;
1944     p = (char *)info + sizeof(fontinfo);
1945     info->realname = p;
1946     strcpy(p, realfontname);
1947     p += 1+strlen(p);
1948     if (family) {
1949         info->family = p;
1950         strcpy(p, family);
1951         p += 1+strlen(p);
1952     } else
1953         info->family = NULL;
1954     if (charset) {
1955         info->charset = p;
1956         strcpy(p, charset);
1957         p += 1+strlen(p);
1958     } else
1959         info->charset = NULL;
1960     if (style) {
1961         info->style = p;
1962         strcpy(p, style);
1963         p += 1+strlen(p);
1964     } else
1965         info->style = NULL;
1966     if (stylekey) {
1967         info->stylekey = p;
1968         strcpy(p, stylekey);
1969         p += 1+strlen(p);
1970     } else
1971         info->stylekey = NULL;
1972     assert(p - (char *)info <= totalsize);
1973     info->size = size;
1974     info->flags = flags;
1975     info->index = count234(fs->fonts_by_selorder);
1976
1977     /*
1978      * It's just conceivable that a misbehaving font enumerator
1979      * might tell us about the same font real name more than once,
1980      * in which case we should silently drop the new one.
1981      */
1982     if (add234(fs->fonts_by_realname, info) != info) {
1983         sfree(info);
1984         return;
1985     }
1986     /*
1987      * However, we should never get a duplicate key in the
1988      * selorder tree, because the index field carefully
1989      * disambiguates otherwise identical records.
1990      */
1991     add234(fs->fonts_by_selorder, info);
1992 }
1993
1994 static fontinfo *update_for_intended_size(unifontsel_internal *fs,
1995                                           fontinfo *info)
1996 {
1997     fontinfo info2, *below, *above;
1998     int pos;
1999
2000     /*
2001      * Copy the info structure. This doesn't copy its dynamic
2002      * string fields, but that's unimportant because all we're
2003      * going to do is to adjust the size field and use it in one
2004      * tree search.
2005      */
2006     info2 = *info;
2007     info2.size = fs->intendedsize;
2008
2009     /*
2010      * Search in the tree to find the fontinfo structure which
2011      * best approximates the size the user last requested.
2012      */
2013     below = findrelpos234(fs->fonts_by_selorder, &info2, NULL,
2014                           REL234_LE, &pos);
2015     above = index234(fs->fonts_by_selorder, pos+1);
2016
2017     /*
2018      * See if we've found it exactly, which is an easy special
2019      * case. If we have, it'll be in `below' and not `above',
2020      * because we did a REL234_LE rather than REL234_LT search.
2021      */
2022     if (!fontinfo_selorder_compare(&info2, below))
2023         return below;
2024
2025     /*
2026      * Now we've either found two suitable fonts, one smaller and
2027      * one larger, or we're at one or other extreme end of the
2028      * scale. Find out which, by NULLing out either of below and
2029      * above if it differs from this one in any respect but size
2030      * (and the disambiguating index field). Bear in mind, also,
2031      * that either one might _already_ be NULL if we're at the
2032      * extreme ends of the font list.
2033      */
2034     if (below) {
2035         info2.size = below->size;
2036         info2.index = below->index;
2037         if (fontinfo_selorder_compare(&info2, below))
2038             below = NULL;
2039     }
2040     if (above) {
2041         info2.size = above->size;
2042         info2.index = above->index;
2043         if (fontinfo_selorder_compare(&info2, above))
2044             above = NULL;
2045     }
2046
2047     /*
2048      * Now return whichever of above and below is non-NULL, if
2049      * that's unambiguous.
2050      */
2051     if (!above)
2052         return below;
2053     if (!below)
2054         return above;
2055
2056     /*
2057      * And now we really do have to make a choice about whether to
2058      * round up or down. We'll do it by rounding to nearest,
2059      * breaking ties by rounding up.
2060      */
2061     if (above->size - fs->intendedsize <= fs->intendedsize - below->size)
2062         return above;
2063     else
2064         return below;
2065 }
2066
2067 static void family_changed(GtkTreeSelection *treeselection, gpointer data)
2068 {
2069     unifontsel_internal *fs = (unifontsel_internal *)data;
2070     GtkTreeModel *treemodel;
2071     GtkTreeIter treeiter;
2072     int minval;
2073     fontinfo *info;
2074
2075     if (fs->inhibit_response)          /* we made this change ourselves */
2076         return;
2077
2078     if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))
2079         return;
2080
2081     gtk_tree_model_get(treemodel, &treeiter, 1, &minval, -1);
2082     info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
2083     info = update_for_intended_size(fs, info);
2084     if (!info)
2085         return; /* _shouldn't_ happen unless font list is completely funted */
2086     if (!info->size)
2087         fs->selsize = fs->intendedsize;   /* font is scalable */
2088     unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize,
2089                            1, FALSE);
2090 }
2091
2092 static void style_changed(GtkTreeSelection *treeselection, gpointer data)
2093 {
2094     unifontsel_internal *fs = (unifontsel_internal *)data;
2095     GtkTreeModel *treemodel;
2096     GtkTreeIter treeiter;
2097     int minval;
2098     fontinfo *info;
2099
2100     if (fs->inhibit_response)          /* we made this change ourselves */
2101         return;
2102
2103     if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))
2104         return;
2105
2106     gtk_tree_model_get(treemodel, &treeiter, 1, &minval, -1);
2107     if (minval < 0)
2108         return;                    /* somehow a charset heading got clicked */
2109     info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
2110     info = update_for_intended_size(fs, info);
2111     if (!info)
2112         return; /* _shouldn't_ happen unless font list is completely funted */
2113     if (!info->size)
2114         fs->selsize = fs->intendedsize;   /* font is scalable */
2115     unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize,
2116                            2, FALSE);
2117 }
2118
2119 static void size_changed(GtkTreeSelection *treeselection, gpointer data)
2120 {
2121     unifontsel_internal *fs = (unifontsel_internal *)data;
2122     GtkTreeModel *treemodel;
2123     GtkTreeIter treeiter;
2124     int minval, size;
2125     fontinfo *info;
2126
2127     if (fs->inhibit_response)          /* we made this change ourselves */
2128         return;
2129
2130     if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))
2131         return;
2132
2133     gtk_tree_model_get(treemodel, &treeiter, 1, &minval, 2, &size, -1);
2134     info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
2135     unifontsel_select_font(fs, info, info->size ? info->size : size, 3, TRUE);
2136 }
2137
2138 static void size_entry_changed(GtkEditable *ed, gpointer data)
2139 {
2140     unifontsel_internal *fs = (unifontsel_internal *)data;
2141     const char *text;
2142     int size;
2143
2144     if (fs->inhibit_response)          /* we made this change ourselves */
2145         return;
2146
2147     text = gtk_entry_get_text(GTK_ENTRY(ed));
2148     size = atoi(text);
2149
2150     if (size > 0) {
2151         assert(fs->selected->size == 0);
2152         unifontsel_select_font(fs, fs->selected, size, 3, TRUE);
2153     }
2154 }
2155
2156 static void alias_resolve(GtkTreeView *treeview, GtkTreePath *path,
2157                           GtkTreeViewColumn *column, gpointer data)
2158 {
2159     unifontsel_internal *fs = (unifontsel_internal *)data;
2160     GtkTreeIter iter;
2161     int minval, newsize;
2162     fontinfo *info, *newinfo;
2163     char *newname;
2164
2165     if (fs->inhibit_response)          /* we made this change ourselves */
2166         return;
2167
2168     gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, path);
2169     gtk_tree_model_get(GTK_TREE_MODEL(fs->family_model), &iter, 1,&minval, -1);
2170     info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
2171     if (info) {
2172         int flags;
2173         struct fontinfo_realname_find f;
2174
2175         newname = info->fontclass->canonify_fontname
2176             (GTK_WIDGET(fs->u.window), info->realname, &newsize, &flags, TRUE);
2177
2178         f.realname = newname;
2179         f.flags = flags;
2180         newinfo = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);
2181
2182         sfree(newname);
2183         if (!newinfo)
2184             return;                    /* font name not in our index */
2185         if (newinfo == info)
2186             return;   /* didn't change under canonification => not an alias */
2187         unifontsel_select_font(fs, newinfo,
2188                                newinfo->size ? newinfo->size : newsize,
2189                                1, TRUE);
2190     }
2191 }
2192
2193 static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event,
2194                                    gpointer data)
2195 {
2196     unifontsel_internal *fs = (unifontsel_internal *)data;
2197
2198     if (fs->preview_pixmap) {
2199         gdk_draw_pixmap(widget->window,
2200                         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2201                         fs->preview_pixmap,
2202                         event->area.x, event->area.y,
2203                         event->area.x, event->area.y,
2204                         event->area.width, event->area.height);
2205     }
2206     return TRUE;
2207 }
2208
2209 static gint unifontsel_configure_area(GtkWidget *widget,
2210                                       GdkEventConfigure *event, gpointer data)
2211 {
2212     unifontsel_internal *fs = (unifontsel_internal *)data;
2213     int ox, oy, nx, ny, x, y;
2214
2215     /*
2216      * Enlarge the pixmap, but never shrink it.
2217      */
2218     ox = fs->preview_width;
2219     oy = fs->preview_height;
2220     x = event->width;
2221     y = event->height;
2222     if (x > ox || y > oy) {
2223         if (fs->preview_pixmap)
2224             gdk_pixmap_unref(fs->preview_pixmap);
2225         
2226         nx = (x > ox ? x : ox);
2227         ny = (y > oy ? y : oy);
2228         fs->preview_pixmap = gdk_pixmap_new(widget->window, nx, ny, -1);
2229         fs->preview_width = nx;
2230         fs->preview_height = ny;
2231
2232         unifontsel_draw_preview_text(fs);
2233     }
2234
2235     gdk_window_invalidate_rect(widget->window, NULL, FALSE);
2236
2237     return TRUE;
2238 }
2239
2240 unifontsel *unifontsel_new(const char *wintitle)
2241 {
2242     unifontsel_internal *fs = snew(unifontsel_internal);
2243     GtkWidget *table, *label, *w, *ww, *scroll;
2244     GtkListStore *model;
2245     GtkTreeViewColumn *column;
2246     int lists_height, preview_height, font_width, style_width, size_width;
2247     int i;
2248
2249     fs->inhibit_response = FALSE;
2250     fs->selected = NULL;
2251
2252     {
2253         /*
2254          * Invent some magic size constants.
2255          */
2256         GtkRequisition req;
2257         label = gtk_label_new("Quite Long Font Name (Foundry)");
2258         gtk_widget_size_request(label, &req);
2259         font_width = req.width;
2260         lists_height = 14 * req.height;
2261         preview_height = 5 * req.height;
2262         gtk_label_set_text(GTK_LABEL(label), "Italic Extra Condensed");
2263         gtk_widget_size_request(label, &req);
2264         style_width = req.width;
2265         gtk_label_set_text(GTK_LABEL(label), "48000");
2266         gtk_widget_size_request(label, &req);
2267         size_width = req.width;
2268 #if GTK_CHECK_VERSION(2,10,0)
2269         g_object_ref_sink(label);
2270         g_object_unref(label);
2271 #else
2272         gtk_object_sink(GTK_OBJECT(label));
2273 #endif
2274     }
2275
2276     /*
2277      * Create the dialog box and initialise the user-visible
2278      * fields in the returned structure.
2279      */
2280     fs->u.user_data = NULL;
2281     fs->u.window = GTK_WINDOW(gtk_dialog_new());
2282     gtk_window_set_title(fs->u.window, wintitle);
2283     fs->u.cancel_button = gtk_dialog_add_button
2284         (GTK_DIALOG(fs->u.window), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
2285     fs->u.ok_button = gtk_dialog_add_button
2286         (GTK_DIALOG(fs->u.window), GTK_STOCK_OK, GTK_RESPONSE_OK);
2287     gtk_widget_grab_default(fs->u.ok_button);
2288
2289     /*
2290      * Now set up the internal fields, including in particular all
2291      * the controls that actually allow the user to select fonts.
2292      */
2293     table = gtk_table_new(8, 3, FALSE);
2294     gtk_widget_show(table);
2295     gtk_table_set_col_spacings(GTK_TABLE(table), 8);
2296 #if GTK_CHECK_VERSION(2,4,0)
2297     /* GtkAlignment seems to be the simplest way to put padding round things */
2298     w = gtk_alignment_new(0, 0, 1, 1);
2299     gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8);
2300     gtk_container_add(GTK_CONTAINER(w), table);
2301     gtk_widget_show(w);
2302 #else
2303     w = table;
2304 #endif
2305     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(fs->u.window)->vbox),
2306                        w, TRUE, TRUE, 0);
2307
2308     label = gtk_label_new_with_mnemonic("_Font:");
2309     gtk_widget_show(label);
2310     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
2311     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
2312
2313     /*
2314      * The Font list box displays only a string, but additionally
2315      * stores two integers which give the limits within the
2316      * tree234 of the font entries covered by this list entry.
2317      */
2318     model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
2319     w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
2320     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);
2321     gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);
2322     gtk_widget_show(w);
2323     column = gtk_tree_view_column_new_with_attributes
2324         ("Font", gtk_cell_renderer_text_new(),
2325          "text", 0, (char *)NULL);
2326     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2327     gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
2328     g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),
2329                      "changed", G_CALLBACK(family_changed), fs);
2330     g_signal_connect(G_OBJECT(w), "row-activated",
2331                      G_CALLBACK(alias_resolve), fs);
2332
2333     scroll = gtk_scrolled_window_new(NULL, NULL);
2334     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
2335                                         GTK_SHADOW_IN);
2336     gtk_container_add(GTK_CONTAINER(scroll), w);
2337     gtk_widget_show(scroll);
2338     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
2339                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2340     gtk_widget_set_size_request(scroll, font_width, lists_height);
2341     gtk_table_attach(GTK_TABLE(table), scroll, 0, 1, 1, 3, GTK_FILL,
2342                      GTK_EXPAND | GTK_FILL, 0, 0);
2343     fs->family_model = model;
2344     fs->family_list = w;
2345
2346     label = gtk_label_new_with_mnemonic("_Style:");
2347     gtk_widget_show(label);
2348     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
2349     gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, GTK_FILL, 0, 0, 0);
2350
2351     /*
2352      * The Style list box can contain insensitive elements
2353      * (character set headings for server-side fonts), so we add
2354      * an extra column to the list store to hold that information.
2355      */
2356     model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT,
2357                                G_TYPE_BOOLEAN);
2358     w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
2359     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);
2360     gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);
2361     gtk_widget_show(w);
2362     column = gtk_tree_view_column_new_with_attributes
2363         ("Style", gtk_cell_renderer_text_new(),
2364          "text", 0, "sensitive", 3, (char *)NULL);
2365     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2366     gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
2367     g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),
2368                      "changed", G_CALLBACK(style_changed), fs);
2369
2370     scroll = gtk_scrolled_window_new(NULL, NULL);
2371     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
2372                                         GTK_SHADOW_IN);
2373     gtk_container_add(GTK_CONTAINER(scroll), w);
2374     gtk_widget_show(scroll);
2375     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
2376                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2377     gtk_widget_set_size_request(scroll, style_width, lists_height);
2378     gtk_table_attach(GTK_TABLE(table), scroll, 1, 2, 1, 3, GTK_FILL,
2379                      GTK_EXPAND | GTK_FILL, 0, 0);
2380     fs->style_model = model;
2381     fs->style_list = w;
2382
2383     label = gtk_label_new_with_mnemonic("Si_ze:");
2384     gtk_widget_show(label);
2385     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
2386     gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 0, 0);
2387
2388     /*
2389      * The Size label attaches primarily to a text input box so
2390      * that the user can select a size of their choice. The list
2391      * of available sizes is secondary.
2392      */
2393     fs->size_entry = w = gtk_entry_new();
2394     gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);
2395     gtk_widget_set_size_request(w, size_width, -1);
2396     gtk_widget_show(w);
2397     gtk_table_attach(GTK_TABLE(table), w, 2, 3, 1, 2, GTK_FILL, 0, 0, 0);
2398     g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(size_entry_changed),
2399                      fs);
2400
2401     model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
2402     w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
2403     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);
2404     gtk_widget_show(w);
2405     column = gtk_tree_view_column_new_with_attributes
2406         ("Size", gtk_cell_renderer_text_new(),
2407          "text", 0, (char *)NULL);
2408     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2409     gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
2410     g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),
2411                      "changed", G_CALLBACK(size_changed), fs);
2412
2413     scroll = gtk_scrolled_window_new(NULL, NULL);
2414     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
2415                                         GTK_SHADOW_IN);
2416     gtk_container_add(GTK_CONTAINER(scroll), w);
2417     gtk_widget_show(scroll);
2418     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
2419                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2420     gtk_table_attach(GTK_TABLE(table), scroll, 2, 3, 2, 3, GTK_FILL,
2421                      GTK_EXPAND | GTK_FILL, 0, 0);
2422     fs->size_model = model;
2423     fs->size_list = w;
2424
2425     /*
2426      * Preview widget.
2427      */
2428     fs->preview_area = gtk_drawing_area_new();
2429     fs->preview_pixmap = NULL;
2430     fs->preview_width = 0;
2431     fs->preview_height = 0;
2432     fs->preview_fg.pixel = fs->preview_bg.pixel = 0;
2433     fs->preview_fg.red = fs->preview_fg.green = fs->preview_fg.blue = 0x0000;
2434     fs->preview_bg.red = fs->preview_bg.green = fs->preview_bg.blue = 0xFFFF;
2435     gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_fg,
2436                              FALSE, FALSE);
2437     gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_bg,
2438                              FALSE, FALSE);
2439     gtk_signal_connect(GTK_OBJECT(fs->preview_area), "expose_event",
2440                        GTK_SIGNAL_FUNC(unifontsel_expose_area), fs);
2441     gtk_signal_connect(GTK_OBJECT(fs->preview_area), "configure_event",
2442                        GTK_SIGNAL_FUNC(unifontsel_configure_area), fs);
2443     gtk_widget_set_size_request(fs->preview_area, 1, preview_height);
2444     gtk_widget_show(fs->preview_area);
2445     ww = fs->preview_area;
2446     w = gtk_frame_new(NULL);
2447     gtk_container_add(GTK_CONTAINER(w), ww);
2448     gtk_widget_show(w);
2449 #if GTK_CHECK_VERSION(2,4,0)
2450     ww = w;
2451     /* GtkAlignment seems to be the simplest way to put padding round things */
2452     w = gtk_alignment_new(0, 0, 1, 1);
2453     gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8);
2454     gtk_container_add(GTK_CONTAINER(w), ww);
2455     gtk_widget_show(w);
2456 #endif
2457     ww = w;
2458     w = gtk_frame_new("Preview of font");
2459     gtk_container_add(GTK_CONTAINER(w), ww);
2460     gtk_widget_show(w);
2461     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 3, 4,
2462                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 8);
2463
2464     i = 0;
2465     w = gtk_check_button_new_with_label("Show client-side fonts");
2466     gtk_object_set_data(GTK_OBJECT(w), "user-data",
2467                         GINT_TO_POINTER(FONTFLAG_CLIENTSIDE));
2468     gtk_signal_connect(GTK_OBJECT(w), "toggled",
2469                        GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
2470     gtk_widget_show(w);
2471     fs->filter_buttons[i++] = w;
2472     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 4, 5, GTK_FILL, 0, 0, 0);
2473     w = gtk_check_button_new_with_label("Show server-side fonts");
2474     gtk_object_set_data(GTK_OBJECT(w), "user-data",
2475                         GINT_TO_POINTER(FONTFLAG_SERVERSIDE));
2476     gtk_signal_connect(GTK_OBJECT(w), "toggled",
2477                        GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
2478     gtk_widget_show(w);
2479     fs->filter_buttons[i++] = w;
2480     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 5, 6, GTK_FILL, 0, 0, 0);
2481     w = gtk_check_button_new_with_label("Show server-side font aliases");
2482     gtk_object_set_data(GTK_OBJECT(w), "user-data",
2483                         GINT_TO_POINTER(FONTFLAG_SERVERALIAS));
2484     gtk_signal_connect(GTK_OBJECT(w), "toggled",
2485                        GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
2486     gtk_widget_show(w);
2487     fs->filter_buttons[i++] = w;
2488     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 6, 7, GTK_FILL, 0, 0, 0);
2489     w = gtk_check_button_new_with_label("Show non-monospaced fonts");
2490     gtk_object_set_data(GTK_OBJECT(w), "user-data",
2491                         GINT_TO_POINTER(FONTFLAG_NONMONOSPACED));
2492     gtk_signal_connect(GTK_OBJECT(w), "toggled",
2493                        GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
2494     gtk_widget_show(w);
2495     fs->filter_buttons[i++] = w;
2496     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 7, 8, GTK_FILL, 0, 0, 0);
2497
2498     assert(i == lenof(fs->filter_buttons));
2499     fs->filter_flags = FONTFLAG_CLIENTSIDE | FONTFLAG_SERVERSIDE |
2500         FONTFLAG_SERVERALIAS;
2501     unifontsel_set_filter_buttons(fs);
2502
2503     /*
2504      * Go and find all the font names, and set up our master font
2505      * list.
2506      */
2507     fs->fonts_by_realname = newtree234(fontinfo_realname_compare);
2508     fs->fonts_by_selorder = newtree234(fontinfo_selorder_compare);
2509     for (i = 0; i < lenof(unifont_types); i++)
2510         unifont_types[i]->enum_fonts(GTK_WIDGET(fs->u.window),
2511                                      unifontsel_add_entry, fs);
2512
2513     /*
2514      * And set up the initial font names list.
2515      */
2516     unifontsel_setup_familylist(fs);
2517
2518     fs->selsize = fs->intendedsize = 13;   /* random default */
2519     gtk_widget_set_sensitive(fs->u.ok_button, FALSE);
2520
2521     return (unifontsel *)fs;
2522 }
2523
2524 void unifontsel_destroy(unifontsel *fontsel)
2525 {
2526     unifontsel_internal *fs = (unifontsel_internal *)fontsel;
2527     fontinfo *info;
2528
2529     if (fs->preview_pixmap)
2530         gdk_pixmap_unref(fs->preview_pixmap);
2531
2532     freetree234(fs->fonts_by_selorder);
2533     while ((info = delpos234(fs->fonts_by_realname, 0)) != NULL)
2534         sfree(info);
2535     freetree234(fs->fonts_by_realname);
2536
2537     gtk_widget_destroy(GTK_WIDGET(fs->u.window));
2538     sfree(fs);
2539 }
2540
2541 void unifontsel_set_name(unifontsel *fontsel, const char *fontname)
2542 {
2543     unifontsel_internal *fs = (unifontsel_internal *)fontsel;
2544     int i, start, end, size, flags;
2545     const char *fontname2 = NULL;
2546     fontinfo *info;
2547
2548     /*
2549      * Provide a default if given an empty or null font name.
2550      */
2551     if (!fontname || !*fontname)
2552         fontname = "server:fixed";
2553
2554     /*
2555      * Call the canonify_fontname function.
2556      */
2557     fontname = unifont_do_prefix(fontname, &start, &end);
2558     for (i = start; i < end; i++) {
2559         fontname2 = unifont_types[i]->canonify_fontname
2560             (GTK_WIDGET(fs->u.window), fontname, &size, &flags, FALSE);
2561         if (fontname2)
2562             break;
2563     }
2564     if (i == end)
2565         return;                        /* font name not recognised */
2566
2567     /*
2568      * Now look up the canonified font name in our index.
2569      */
2570     {
2571         struct fontinfo_realname_find f;
2572         f.realname = fontname2;
2573         f.flags = flags;
2574         info = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);
2575     }
2576
2577     /*
2578      * If we've found the font, and its size field is either
2579      * correct or zero (the latter indicating a scalable font),
2580      * then we're done. Otherwise, try looking up the original
2581      * font name instead.
2582      */
2583     if (!info || (info->size != size && info->size != 0)) {
2584         struct fontinfo_realname_find f;
2585         f.realname = fontname;
2586         f.flags = flags;
2587
2588         info = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);
2589         if (!info || info->size != size)
2590             return;                    /* font name not in our index */
2591     }
2592
2593     /*
2594      * Now we've got a fontinfo structure and a font size, so we
2595      * know everything we need to fill in all the fields in the
2596      * dialog.
2597      */
2598     unifontsel_select_font(fs, info, size, 0, TRUE);
2599 }
2600
2601 char *unifontsel_get_name(unifontsel *fontsel)
2602 {
2603     unifontsel_internal *fs = (unifontsel_internal *)fontsel;
2604     char *name;
2605
2606     if (!fs->selected)
2607         return NULL;
2608
2609     if (fs->selected->size == 0) {
2610         name = fs->selected->fontclass->scale_fontname
2611             (GTK_WIDGET(fs->u.window), fs->selected->realname, fs->selsize);
2612         if (name) {
2613             char *ret = dupcat(fs->selected->fontclass->prefix, ":",
2614                                name, NULL);
2615             sfree(name);
2616             return ret;
2617         }
2618     }
2619
2620     return dupcat(fs->selected->fontclass->prefix, ":",
2621                   fs->selected->realname, NULL);
2622 }
2623
2624 #endif /* GTK_CHECK_VERSION(2,0,0) */