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