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