]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/gtkfont.c
Merge tag '0.65'
[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             const 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
875 static const struct unifont_vtable pangofont_vtable = {
876     pangofont_create,
877     pangofont_create_fallback,
878     pangofont_destroy,
879     pangofont_has_glyph,
880     pangofont_draw_text,
881     pangofont_enum_fonts,
882     pangofont_canonify_fontname,
883     pangofont_scale_fontname,
884     "client",
885 };
886
887 /*
888  * This function is used to rigorously validate a
889  * PangoFontDescription. Later versions of Pango have a nasty
890  * habit of accepting _any_ old string as input to
891  * pango_font_description_from_string and returning a font
892  * description which can actually be used to display text, even if
893  * they have to do it by falling back to their most default font.
894  * This is doubtless helpful in some situations, but not here,
895  * because we need to know if a Pango font string actually _makes
896  * sense_ in order to fall back to treating it as an X font name
897  * if it doesn't. So we check that the font family is actually one
898  * supported by Pango.
899  */
900 static int pangofont_check_desc_makes_sense(PangoContext *ctx,
901                                             PangoFontDescription *desc)
902 {
903 #ifndef PANGO_PRE_1POINT6
904     PangoFontMap *map;
905 #endif
906     PangoFontFamily **families;
907     int i, nfamilies, matched;
908
909     /*
910      * Ask Pango for a list of font families, and iterate through
911      * them to see if one of them matches the family in the
912      * PangoFontDescription.
913      */
914 #ifndef PANGO_PRE_1POINT6
915     map = pango_context_get_font_map(ctx);
916     if (!map)
917         return FALSE;
918     pango_font_map_list_families(map, &families, &nfamilies);
919 #else
920     pango_context_list_families(ctx, &families, &nfamilies);
921 #endif
922
923     matched = FALSE;
924     for (i = 0; i < nfamilies; i++) {
925         if (!g_ascii_strcasecmp(pango_font_family_get_name(families[i]),
926                                 pango_font_description_get_family(desc))) {
927             matched = TRUE;
928             break;
929         }
930     }
931     g_free(families);
932
933     return matched;
934 }
935
936 static unifont *pangofont_create_internal(GtkWidget *widget,
937                                           PangoContext *ctx,
938                                           PangoFontDescription *desc,
939                                           int wide, int bold,
940                                           int shadowoffset, int shadowalways)
941 {
942     struct pangofont *pfont;
943 #ifndef PANGO_PRE_1POINT6
944     PangoFontMap *map;
945 #endif
946     PangoFontset *fset;
947     PangoFontMetrics *metrics;
948
949 #ifndef PANGO_PRE_1POINT6
950     map = pango_context_get_font_map(ctx);
951     if (!map) {
952         pango_font_description_free(desc);
953         return NULL;
954     }
955     fset = pango_font_map_load_fontset(map, ctx, desc,
956                                        pango_context_get_language(ctx));
957 #else
958     fset = pango_context_load_fontset(ctx, desc,
959                                       pango_context_get_language(ctx));
960 #endif
961     if (!fset) {
962         pango_font_description_free(desc);
963         return NULL;
964     }
965     metrics = pango_fontset_get_metrics(fset);
966     if (!metrics ||
967         pango_font_metrics_get_approximate_digit_width(metrics) == 0) {
968         pango_font_description_free(desc);
969         g_object_unref(fset);
970         return NULL;
971     }
972
973     pfont = snew(struct pangofont);
974     pfont->u.vt = &pangofont_vtable;
975     pfont->u.width =
976         PANGO_PIXELS(pango_font_metrics_get_approximate_digit_width(metrics));
977     pfont->u.ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));
978     pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
979     pfont->u.height = pfont->u.ascent + pfont->u.descent;
980     pfont->u.want_fallback = FALSE;
981     /* The Pango API is hardwired to UTF-8 */
982     pfont->u.public_charset = CS_UTF8;
983     pfont->desc = desc;
984     pfont->fset = fset;
985     pfont->widget = widget;
986     pfont->bold = bold;
987     pfont->shadowoffset = shadowoffset;
988     pfont->shadowalways = shadowalways;
989
990     pango_font_metrics_unref(metrics);
991
992     return (unifont *)pfont;
993 }
994
995 static unifont *pangofont_create(GtkWidget *widget, const char *name,
996                                  int wide, int bold,
997                                  int shadowoffset, int shadowalways)
998 {
999     PangoContext *ctx;
1000     PangoFontDescription *desc;
1001
1002     desc = pango_font_description_from_string(name);
1003     if (!desc)
1004         return NULL;
1005     ctx = gtk_widget_get_pango_context(widget);
1006     if (!ctx) {
1007         pango_font_description_free(desc);
1008         return NULL;
1009     }
1010     if (!pangofont_check_desc_makes_sense(ctx, desc)) {
1011         pango_font_description_free(desc);
1012         return NULL;
1013     }
1014     return pangofont_create_internal(widget, ctx, desc, wide, bold,
1015                                      shadowoffset, shadowalways);
1016 }
1017
1018 static unifont *pangofont_create_fallback(GtkWidget *widget, int height,
1019                                           int wide, int bold,
1020                                           int shadowoffset, int shadowalways)
1021 {
1022     PangoContext *ctx;
1023     PangoFontDescription *desc;
1024
1025     desc = pango_font_description_from_string("Monospace");
1026     if (!desc)
1027         return NULL;
1028     ctx = gtk_widget_get_pango_context(widget);
1029     if (!ctx) {
1030         pango_font_description_free(desc);
1031         return NULL;
1032     }
1033     pango_font_description_set_absolute_size(desc, height * PANGO_SCALE);
1034     return pangofont_create_internal(widget, ctx, desc, wide, bold,
1035                                      shadowoffset, shadowalways);
1036 }
1037
1038 static void pangofont_destroy(unifont *font)
1039 {
1040     struct pangofont *pfont = (struct pangofont *)font;
1041     pango_font_description_free(pfont->desc);
1042     g_object_unref(pfont->fset);
1043     sfree(font);
1044 }
1045
1046 static int pangofont_has_glyph(unifont *font, wchar_t glyph)
1047 {
1048     /* Pango implements font fallback, so assume it has everything */
1049     return TRUE;
1050 }
1051
1052 static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
1053                                 int x, int y, const wchar_t *string, int len,
1054                                 int wide, int bold, int cellwidth)
1055 {
1056     struct pangofont *pfont = (struct pangofont *)font;
1057     PangoLayout *layout;
1058     PangoRectangle rect;
1059     char *utfstring, *utfptr;
1060     int utflen;
1061     int shadowbold = FALSE;
1062
1063     if (wide)
1064         cellwidth *= 2;
1065
1066     y -= pfont->u.ascent;
1067
1068     layout = pango_layout_new(gtk_widget_get_pango_context(pfont->widget));
1069     pango_layout_set_font_description(layout, pfont->desc);
1070     if (bold > pfont->bold) {
1071         if (pfont->shadowalways)
1072             shadowbold = TRUE;
1073         else {
1074             PangoFontDescription *desc2 =
1075                 pango_font_description_copy_static(pfont->desc);
1076             pango_font_description_set_weight(desc2, PANGO_WEIGHT_BOLD);
1077             pango_layout_set_font_description(layout, desc2);
1078         }
1079     }
1080
1081     /*
1082      * Pango always expects UTF-8, so convert the input wide character
1083      * string to UTF-8.
1084      */
1085     utfstring = snewn(len*6+1, char); /* UTF-8 has max 6 bytes/char */
1086     utflen = wc_to_mb(CS_UTF8, 0, string, len,
1087                       utfstring, len*6+1, ".", NULL, NULL);
1088
1089     utfptr = utfstring;
1090     while (utflen > 0) {
1091         int clen, n;
1092
1093         /*
1094          * We want to display every character from this string in
1095          * the centre of its own character cell. In the worst case,
1096          * this requires a separate text-drawing call for each
1097          * character; but in the common case where the font is
1098          * properly fixed-width, we can draw many characters in one
1099          * go which is much faster.
1100          *
1101          * This still isn't really ideal. If you look at what
1102          * happens in the X protocol as a result of all of this, you
1103          * find - naturally enough - that each call to
1104          * gdk_draw_layout() generates a separate set of X RENDER
1105          * operations involving creating a picture, setting a clip
1106          * rectangle, doing some drawing and undoing the whole lot.
1107          * In an ideal world, we should _always_ be able to turn the
1108          * contents of this loop into a single RenderCompositeGlyphs
1109          * operation which internally specifies inter-character
1110          * deltas to get the spacing right, which would give us full
1111          * speed _even_ in the worst case of a non-fixed-width font.
1112          * However, Pango's architecture and documentation are so
1113          * unhelpful that I have no idea how if at all to persuade
1114          * them to do that.
1115          */
1116
1117         /*
1118          * Start by extracting a single UTF-8 character from the
1119          * string.
1120          */
1121         clen = 1;
1122         while (clen < utflen &&
1123                (unsigned char)utfptr[clen] >= 0x80 &&
1124                (unsigned char)utfptr[clen] < 0xC0)
1125             clen++;
1126         n = 1;
1127
1128         /*
1129          * If it's a right-to-left character, we must display it on
1130          * its own, to stop Pango helpfully re-reversing our already
1131          * reversed text.
1132          */
1133         if (!is_rtl(string[0])) {
1134
1135             /*
1136              * See if that character has the width we expect.
1137              */
1138             pango_layout_set_text(layout, utfptr, clen);
1139             pango_layout_get_pixel_extents(layout, NULL, &rect);
1140
1141             if (rect.width == cellwidth) {
1142                 /*
1143                  * Try extracting more characters, for as long as they
1144                  * stay well-behaved.
1145                  */
1146                 while (clen < utflen) {
1147                     int oldclen = clen;
1148                     clen++;                    /* skip UTF-8 introducer byte */
1149                     while (clen < utflen &&
1150                            (unsigned char)utfptr[clen] >= 0x80 &&
1151                            (unsigned char)utfptr[clen] < 0xC0)
1152                         clen++;
1153                     n++;
1154                     pango_layout_set_text(layout, utfptr, clen);
1155                     pango_layout_get_pixel_extents(layout, NULL, &rect);
1156                     if (rect.width != n * cellwidth) {
1157                         clen = oldclen;
1158                         n--;
1159                         break;
1160                     }
1161                 }
1162             }
1163         }
1164
1165         pango_layout_set_text(layout, utfptr, clen);
1166         pango_layout_get_pixel_extents(layout, NULL, &rect);
1167         gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2,
1168                         y + (pfont->u.height - rect.height)/2, layout);
1169         if (shadowbold)
1170             gdk_draw_layout(target, gc, x + (n*cellwidth - rect.width)/2 + pfont->shadowoffset,
1171                             y + (pfont->u.height - rect.height)/2, layout);
1172
1173         utflen -= clen;
1174         utfptr += clen;
1175         string += n;
1176         x += n * cellwidth;
1177     }
1178
1179     sfree(utfstring);
1180
1181     g_object_unref(layout);
1182 }
1183
1184 /*
1185  * Dummy size value to be used when converting a
1186  * PangoFontDescription of a scalable font to a string for
1187  * internal use.
1188  */
1189 #define PANGO_DUMMY_SIZE 12
1190
1191 static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback,
1192                                  void *callback_ctx)
1193 {
1194     PangoContext *ctx;
1195 #ifndef PANGO_PRE_1POINT6
1196     PangoFontMap *map;
1197 #endif
1198     PangoFontFamily **families;
1199     int i, nfamilies;
1200
1201     ctx = gtk_widget_get_pango_context(widget);
1202     if (!ctx)
1203         return;
1204
1205     /*
1206      * Ask Pango for a list of font families, and iterate through
1207      * them.
1208      */
1209 #ifndef PANGO_PRE_1POINT6
1210     map = pango_context_get_font_map(ctx);
1211     if (!map)
1212         return;
1213     pango_font_map_list_families(map, &families, &nfamilies);
1214 #else
1215     pango_context_list_families(ctx, &families, &nfamilies);
1216 #endif
1217     for (i = 0; i < nfamilies; i++) {
1218         PangoFontFamily *family = families[i];
1219         const char *familyname;
1220         int flags;
1221         PangoFontFace **faces;
1222         int j, nfaces;
1223
1224         /*
1225          * Set up our flags for this font family, and get the name
1226          * string.
1227          */
1228         flags = FONTFLAG_CLIENTSIDE;
1229 #ifndef PANGO_PRE_1POINT4
1230         /*
1231          * In very early versions of Pango, we can't tell
1232          * monospaced fonts from non-monospaced.
1233          */
1234         if (!pango_font_family_is_monospace(family))
1235             flags |= FONTFLAG_NONMONOSPACED;
1236 #endif
1237         familyname = pango_font_family_get_name(family);
1238
1239         /*
1240          * Go through the available font faces in this family.
1241          */
1242         pango_font_family_list_faces(family, &faces, &nfaces);
1243         for (j = 0; j < nfaces; j++) {
1244             PangoFontFace *face = faces[j];
1245             PangoFontDescription *desc;
1246             const char *facename;
1247             int *sizes;
1248             int k, nsizes, dummysize;
1249
1250             /*
1251              * Get the face name string.
1252              */
1253             facename = pango_font_face_get_face_name(face);
1254
1255             /*
1256              * Set up a font description with what we've got so
1257              * far. We'll fill in the size field manually and then
1258              * call pango_font_description_to_string() to give the
1259              * full real name of the specific font.
1260              */
1261             desc = pango_font_face_describe(face);
1262
1263             /*
1264              * See if this font has a list of specific sizes.
1265              */
1266 #ifndef PANGO_PRE_1POINT4
1267             pango_font_face_list_sizes(face, &sizes, &nsizes);
1268 #else
1269             /*
1270              * In early versions of Pango, that call wasn't
1271              * supported; we just have to assume everything is
1272              * scalable.
1273              */
1274             sizes = NULL;
1275 #endif
1276             if (!sizes) {
1277                 /*
1278                  * Write a single entry with a dummy size.
1279                  */
1280                 dummysize = PANGO_DUMMY_SIZE * PANGO_SCALE;
1281                 sizes = &dummysize;
1282                 nsizes = 1;
1283             }
1284
1285             /*
1286              * If so, go through them one by one.
1287              */
1288             for (k = 0; k < nsizes; k++) {
1289                 char *fullname;
1290                 char stylekey[128];
1291
1292                 pango_font_description_set_size(desc, sizes[k]);
1293
1294                 fullname = pango_font_description_to_string(desc);
1295
1296                 /*
1297                  * Construct the sorting key for font styles.
1298                  */
1299                 {
1300                     char *p = stylekey;
1301                     int n;
1302
1303                     n = pango_font_description_get_weight(desc);
1304                     /* Weight: normal, then lighter, then bolder */
1305                     if (n <= PANGO_WEIGHT_NORMAL)
1306                         n = PANGO_WEIGHT_NORMAL - n;
1307                     p += sprintf(p, "%4d", n);
1308
1309                     n = pango_font_description_get_style(desc);
1310                     p += sprintf(p, " %2d", n);
1311
1312                     n = pango_font_description_get_stretch(desc);
1313                     /* Stretch: closer to normal sorts earlier */
1314                     n = 2 * abs(PANGO_STRETCH_NORMAL - n) +
1315                         (n < PANGO_STRETCH_NORMAL);
1316                     p += sprintf(p, " %2d", n);
1317
1318                     n = pango_font_description_get_variant(desc);
1319                     p += sprintf(p, " %2d", n);
1320                     
1321                 }
1322
1323                 /*
1324                  * Got everything. Hand off to the callback.
1325                  * (The charset string is NULL, because only
1326                  * server-side X fonts use it.)
1327                  */
1328                 callback(callback_ctx, fullname, familyname, NULL, facename,
1329                          stylekey,
1330                          (sizes == &dummysize ? 0 : PANGO_PIXELS(sizes[k])),
1331                          flags, &pangofont_vtable);
1332
1333                 g_free(fullname);
1334             }
1335             if (sizes != &dummysize)
1336                 g_free(sizes);
1337
1338             pango_font_description_free(desc);
1339         }
1340         g_free(faces);
1341     }
1342     g_free(families);
1343 }
1344
1345 static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name,
1346                                          int *size, int *flags,
1347                                          int resolve_aliases)
1348 {
1349     /*
1350      * When given a Pango font name to try to make sense of for a
1351      * font selector, we must normalise it to PANGO_DUMMY_SIZE and
1352      * extract its original size (in pixels) into the `size' field.
1353      */
1354     PangoContext *ctx;
1355 #ifndef PANGO_PRE_1POINT6
1356     PangoFontMap *map;
1357 #endif
1358     PangoFontDescription *desc;
1359     PangoFontset *fset;
1360     PangoFontMetrics *metrics;
1361     char *newname, *retname;
1362
1363     desc = pango_font_description_from_string(name);
1364     if (!desc)
1365         return NULL;
1366     ctx = gtk_widget_get_pango_context(widget);
1367     if (!ctx) {
1368         pango_font_description_free(desc);
1369         return NULL;
1370     }
1371     if (!pangofont_check_desc_makes_sense(ctx, desc)) {
1372         pango_font_description_free(desc);
1373         return NULL;
1374     }
1375 #ifndef PANGO_PRE_1POINT6
1376     map = pango_context_get_font_map(ctx);
1377     if (!map) {
1378         pango_font_description_free(desc);
1379         return NULL;
1380     }
1381     fset = pango_font_map_load_fontset(map, ctx, desc,
1382                                        pango_context_get_language(ctx));
1383 #else
1384     fset = pango_context_load_fontset(ctx, desc,
1385                                       pango_context_get_language(ctx));
1386 #endif
1387     if (!fset) {
1388         pango_font_description_free(desc);
1389         return NULL;
1390     }
1391     metrics = pango_fontset_get_metrics(fset);
1392     if (!metrics ||
1393         pango_font_metrics_get_approximate_digit_width(metrics) == 0) {
1394         pango_font_description_free(desc);
1395         g_object_unref(fset);
1396         return NULL;
1397     }
1398
1399     *size = PANGO_PIXELS(pango_font_description_get_size(desc));
1400     *flags = FONTFLAG_CLIENTSIDE;
1401     pango_font_description_set_size(desc, PANGO_DUMMY_SIZE * PANGO_SCALE);
1402     newname = pango_font_description_to_string(desc);
1403     retname = dupstr(newname);
1404     g_free(newname);
1405
1406     pango_font_metrics_unref(metrics);
1407     pango_font_description_free(desc);
1408     g_object_unref(fset);
1409
1410     return retname;
1411 }
1412
1413 static char *pangofont_scale_fontname(GtkWidget *widget, const char *name,
1414                                       int size)
1415 {
1416     PangoFontDescription *desc;
1417     char *newname, *retname;
1418
1419     desc = pango_font_description_from_string(name);
1420     if (!desc)
1421         return NULL;
1422     pango_font_description_set_size(desc, size * PANGO_SCALE);
1423     newname = pango_font_description_to_string(desc);
1424     retname = dupstr(newname);
1425     g_free(newname);
1426     pango_font_description_free(desc);
1427
1428     return retname;
1429 }
1430
1431 #endif /* GTK_CHECK_VERSION(2,0,0) */
1432
1433 /* ----------------------------------------------------------------------
1434  * Outermost functions which do the vtable dispatch.
1435  */
1436
1437 /*
1438  * Complete list of font-type subclasses. Listed in preference
1439  * order for unifont_create(). (That is, in the extremely unlikely
1440  * event that the same font name is valid as both a Pango and an
1441  * X11 font, it will be interpreted as the former in the absence
1442  * of an explicit type-disambiguating prefix.)
1443  *
1444  * The 'multifont' subclass is omitted here, as discussed above.
1445  */
1446 static const struct unifont_vtable *unifont_types[] = {
1447 #if GTK_CHECK_VERSION(2,0,0)
1448     &pangofont_vtable,
1449 #endif
1450     &x11font_vtable,
1451 };
1452
1453 /*
1454  * Function which takes a font name and processes the optional
1455  * scheme prefix. Returns the tail of the font name suitable for
1456  * passing to individual font scheme functions, and also provides
1457  * a subrange of the unifont_types[] array above.
1458  * 
1459  * The return values `start' and `end' denote a half-open interval
1460  * in unifont_types[]; that is, the correct way to iterate over
1461  * them is
1462  * 
1463  *   for (i = start; i < end; i++) {...}
1464  */
1465 static const char *unifont_do_prefix(const char *name, int *start, int *end)
1466 {
1467     int colonpos = strcspn(name, ":");
1468     int i;
1469
1470     if (name[colonpos]) {
1471         /*
1472          * There's a colon prefix on the font name. Use it to work
1473          * out which subclass to use.
1474          */
1475         for (i = 0; i < lenof(unifont_types); i++) {
1476             if (strlen(unifont_types[i]->prefix) == colonpos &&
1477                 !strncmp(unifont_types[i]->prefix, name, colonpos)) {
1478                 *start = i;
1479                 *end = i+1;
1480                 return name + colonpos + 1;
1481             }
1482         }
1483         /*
1484          * None matched, so return an empty scheme list to prevent
1485          * any scheme from being called at all.
1486          */
1487         *start = *end = 0;
1488         return name + colonpos + 1;
1489     } else {
1490         /*
1491          * No colon prefix, so just use all the subclasses.
1492          */
1493         *start = 0;
1494         *end = lenof(unifont_types);
1495         return name;
1496     }
1497 }
1498
1499 unifont *unifont_create(GtkWidget *widget, const char *name, int wide,
1500                         int bold, int shadowoffset, int shadowalways)
1501 {
1502     int i, start, end;
1503
1504     name = unifont_do_prefix(name, &start, &end);
1505
1506     for (i = start; i < end; i++) {
1507         unifont *ret = unifont_types[i]->create(widget, name, wide, bold,
1508                                                 shadowoffset, shadowalways);
1509         if (ret)
1510             return ret;
1511     }
1512     return NULL;                       /* font not found in any scheme */
1513 }
1514
1515 void unifont_destroy(unifont *font)
1516 {
1517     font->vt->destroy(font);
1518 }
1519
1520 void unifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
1521                        int x, int y, const wchar_t *string, int len,
1522                        int wide, int bold, int cellwidth)
1523 {
1524     font->vt->draw_text(target, gc, font, x, y, string, len,
1525                         wide, bold, cellwidth);
1526 }
1527
1528 /* ----------------------------------------------------------------------
1529  * Multiple-font wrapper. This is a type of unifont which encapsulates
1530  * up to two other unifonts, permitting missing glyphs in the main
1531  * font to be filled in by a fallback font.
1532  *
1533  * This is a type of unifont just like the previous two, but it has a
1534  * separate constructor which is manually called by the client, so it
1535  * doesn't appear in the list of available font types enumerated by
1536  * unifont_create. This means it's not used by unifontsel either, so
1537  * it doesn't need to support any methods except draw_text and
1538  * destroy.
1539  */
1540
1541 static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
1542                                 int x, int y, const wchar_t *string, int len,
1543                                 int wide, int bold, int cellwidth);
1544 static void multifont_destroy(unifont *font);
1545
1546 struct multifont {
1547     struct unifont u;
1548     unifont *main;
1549     unifont *fallback;
1550 };
1551
1552 static const struct unifont_vtable multifont_vtable = {
1553     NULL,                             /* creation is done specially */
1554     NULL,
1555     multifont_destroy,
1556     NULL,
1557     multifont_draw_text,
1558     NULL,
1559     NULL,
1560     NULL,
1561     "client",
1562 };
1563
1564 unifont *multifont_create(GtkWidget *widget, const char *name,
1565                           int wide, int bold,
1566                           int shadowoffset, int shadowalways)
1567 {
1568     int i;
1569     unifont *font, *fallback;
1570     struct multifont *mfont;
1571
1572     font = unifont_create(widget, name, wide, bold,
1573                           shadowoffset, shadowalways);
1574     if (!font)
1575         return NULL;
1576
1577     fallback = NULL;
1578     if (font->want_fallback) {
1579         for (i = 0; i < lenof(unifont_types); i++) {
1580             if (unifont_types[i]->create_fallback) {
1581                 fallback = unifont_types[i]->create_fallback
1582                     (widget, font->height, wide, bold,
1583                      shadowoffset, shadowalways);
1584                 if (fallback)
1585                     break;
1586             }
1587         }
1588     }
1589
1590     /*
1591      * Construct our multifont. Public members are all copied from the
1592      * primary font we're wrapping.
1593      */
1594     mfont = snew(struct multifont);
1595     mfont->u.vt = &multifont_vtable;
1596     mfont->u.width = font->width;
1597     mfont->u.ascent = font->ascent;
1598     mfont->u.descent = font->descent;
1599     mfont->u.height = font->height;
1600     mfont->u.public_charset = font->public_charset;
1601     mfont->u.want_fallback = FALSE; /* shouldn't be needed, but just in case */
1602     mfont->main = font;
1603     mfont->fallback = fallback;
1604
1605     return (unifont *)mfont;
1606 }
1607
1608 static void multifont_destroy(unifont *font)
1609 {
1610     struct multifont *mfont = (struct multifont *)font;
1611     unifont_destroy(mfont->main);
1612     if (mfont->fallback)
1613         unifont_destroy(mfont->fallback);
1614     sfree(font);
1615 }
1616
1617 static void multifont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
1618                                 int x, int y, const wchar_t *string, int len,
1619                                 int wide, int bold, int cellwidth)
1620 {
1621     struct multifont *mfont = (struct multifont *)font;
1622     int ok, i;
1623
1624     while (len > 0) {
1625         /*
1626          * Find a maximal sequence of characters which are, or are
1627          * not, supported by our main font.
1628          */
1629         ok = mfont->main->vt->has_glyph(mfont->main, string[0]);
1630         for (i = 1;
1631              i < len &&
1632              !mfont->main->vt->has_glyph(mfont->main, string[i]) == !ok;
1633              i++);
1634
1635         /*
1636          * Now display it.
1637          */
1638         unifont_draw_text(target, gc, ok ? mfont->main : mfont->fallback,
1639                           x, y, string, i, wide, bold, cellwidth);
1640         string += i;
1641         len -= i;
1642         x += i * cellwidth;
1643     }
1644 }
1645
1646 #if GTK_CHECK_VERSION(2,0,0)
1647
1648 /* ----------------------------------------------------------------------
1649  * Implementation of a unified font selector. Used on GTK 2 only;
1650  * for GTK 1 we still use the standard font selector.
1651  */
1652
1653 typedef struct fontinfo fontinfo;
1654
1655 typedef struct unifontsel_internal {
1656     /* This must be the structure's first element, for cross-casting */
1657     unifontsel u;
1658     GtkListStore *family_model, *style_model, *size_model;
1659     GtkWidget *family_list, *style_list, *size_entry, *size_list;
1660     GtkWidget *filter_buttons[4];
1661     GtkWidget *preview_area;
1662     GdkPixmap *preview_pixmap;
1663     int preview_width, preview_height;
1664     GdkColor preview_fg, preview_bg;
1665     int filter_flags;
1666     tree234 *fonts_by_realname, *fonts_by_selorder;
1667     fontinfo *selected;
1668     int selsize, intendedsize;
1669     int inhibit_response;  /* inhibit callbacks when we change GUI controls */
1670 } unifontsel_internal;
1671
1672 /*
1673  * The structure held in the tree234s. All the string members are
1674  * part of the same allocated area, so don't need freeing
1675  * separately.
1676  */
1677 struct fontinfo {
1678     char *realname;
1679     char *family, *charset, *style, *stylekey;
1680     int size, flags;
1681     /*
1682      * Fallback sorting key, to permit multiple identical entries
1683      * to exist in the selorder tree.
1684      */
1685     int index;
1686     /*
1687      * Indices mapping fontinfo structures to indices in the list
1688      * boxes. sizeindex is irrelevant if the font is scalable
1689      * (size==0).
1690      */
1691     int familyindex, styleindex, sizeindex;
1692     /*
1693      * The class of font.
1694      */
1695     const struct unifont_vtable *fontclass;
1696 };
1697
1698 struct fontinfo_realname_find {
1699     const char *realname;
1700     int flags;
1701 };
1702
1703 static int strnullcasecmp(const char *a, const char *b)
1704 {
1705     int i;
1706
1707     /*
1708      * If exactly one of the inputs is NULL, it compares before
1709      * the other one.
1710      */
1711     if ((i = (!b) - (!a)) != 0)
1712         return i;
1713
1714     /*
1715      * NULL compares equal.
1716      */
1717     if (!a)
1718         return 0;
1719
1720     /*
1721      * Otherwise, ordinary strcasecmp.
1722      */
1723     return g_ascii_strcasecmp(a, b);
1724 }
1725
1726 static int fontinfo_realname_compare(void *av, void *bv)
1727 {
1728     fontinfo *a = (fontinfo *)av;
1729     fontinfo *b = (fontinfo *)bv;
1730     int i;
1731
1732     if ((i = strnullcasecmp(a->realname, b->realname)) != 0)
1733         return i;
1734     if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))
1735         return ((a->flags & FONTFLAG_SORT_MASK) <
1736                 (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);
1737     return 0;
1738 }
1739
1740 static int fontinfo_realname_find(void *av, void *bv)
1741 {
1742     struct fontinfo_realname_find *a = (struct fontinfo_realname_find *)av;
1743     fontinfo *b = (fontinfo *)bv;
1744     int i;
1745
1746     if ((i = strnullcasecmp(a->realname, b->realname)) != 0)
1747         return i;
1748     if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))
1749         return ((a->flags & FONTFLAG_SORT_MASK) <
1750                 (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);
1751     return 0;
1752 }
1753
1754 static int fontinfo_selorder_compare(void *av, void *bv)
1755 {
1756     fontinfo *a = (fontinfo *)av;
1757     fontinfo *b = (fontinfo *)bv;
1758     int i;
1759     if ((i = strnullcasecmp(a->family, b->family)) != 0)
1760         return i;
1761     /*
1762      * Font class comes immediately after family, so that fonts
1763      * from different classes with the same family
1764      */
1765     if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))
1766         return ((a->flags & FONTFLAG_SORT_MASK) <
1767                 (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);
1768     if ((i = strnullcasecmp(a->charset, b->charset)) != 0)
1769         return i;
1770     if ((i = strnullcasecmp(a->stylekey, b->stylekey)) != 0)
1771         return i;
1772     if ((i = strnullcasecmp(a->style, b->style)) != 0)
1773         return i;
1774     if (a->size != b->size)
1775         return (a->size < b->size ? -1 : +1);
1776     if (a->index != b->index)
1777         return (a->index < b->index ? -1 : +1);
1778     return 0;
1779 }
1780
1781 static void unifontsel_deselect(unifontsel_internal *fs)
1782 {
1783     fs->selected = NULL;
1784     gtk_list_store_clear(fs->style_model);
1785     gtk_list_store_clear(fs->size_model);
1786     gtk_widget_set_sensitive(fs->u.ok_button, FALSE);
1787     gtk_widget_set_sensitive(fs->size_entry, FALSE);
1788 }
1789
1790 static void unifontsel_setup_familylist(unifontsel_internal *fs)
1791 {
1792     GtkTreeIter iter;
1793     int i, listindex, minpos = -1, maxpos = -1;
1794     char *currfamily = NULL;
1795     int currflags = -1;
1796     fontinfo *info;
1797
1798     gtk_list_store_clear(fs->family_model);
1799     listindex = 0;
1800
1801     /*
1802      * Search through the font tree for anything matching our
1803      * current filter criteria. When we find one, add its font
1804      * name to the list box.
1805      */
1806     for (i = 0 ;; i++) {
1807         info = (fontinfo *)index234(fs->fonts_by_selorder, i);
1808         /*
1809          * info may be NULL if we've just run off the end of the
1810          * tree. We must still do a processing pass in that
1811          * situation, in case we had an unfinished font record in
1812          * progress.
1813          */
1814         if (info && (info->flags &~ fs->filter_flags)) {
1815             info->familyindex = -1;
1816             continue;                  /* we're filtering out this font */
1817         }
1818         if (!info || strnullcasecmp(currfamily, info->family) ||
1819             currflags != (info->flags & FONTFLAG_SORT_MASK)) {
1820             /*
1821              * We've either finished a family, or started a new
1822              * one, or both.
1823              */
1824             if (currfamily) {
1825                 gtk_list_store_append(fs->family_model, &iter);
1826                 gtk_list_store_set(fs->family_model, &iter,
1827                                    0, currfamily, 1, minpos, 2, maxpos+1, -1);
1828                 listindex++;
1829             }
1830             if (info) {
1831                 minpos = i;
1832                 currfamily = info->family;
1833                 currflags = info->flags & FONTFLAG_SORT_MASK;
1834             }
1835         }
1836         if (!info)
1837             break;                     /* now we're done */
1838         info->familyindex = listindex;
1839         maxpos = i;
1840     }
1841
1842     /*
1843      * If we've just filtered out the previously selected font,
1844      * deselect it thoroughly.
1845      */
1846     if (fs->selected && fs->selected->familyindex < 0)
1847         unifontsel_deselect(fs);
1848 }
1849
1850 static void unifontsel_setup_stylelist(unifontsel_internal *fs,
1851                                        int start, int end)
1852 {
1853     GtkTreeIter iter;
1854     int i, listindex, minpos = -1, maxpos = -1, started = FALSE;
1855     char *currcs = NULL, *currstyle = NULL;
1856     fontinfo *info;
1857
1858     gtk_list_store_clear(fs->style_model);
1859     listindex = 0;
1860     started = FALSE;
1861
1862     /*
1863      * Search through the font tree for anything matching our
1864      * current filter criteria. When we find one, add its charset
1865      * and/or style name to the list box.
1866      */
1867     for (i = start; i <= end; i++) {
1868         if (i == end)
1869             info = NULL;
1870         else
1871             info = (fontinfo *)index234(fs->fonts_by_selorder, i);
1872         /*
1873          * info may be NULL if we've just run off the end of the
1874          * relevant data. We must still do a processing pass in
1875          * that situation, in case we had an unfinished font
1876          * record in progress.
1877          */
1878         if (info && (info->flags &~ fs->filter_flags)) {
1879             info->styleindex = -1;
1880             continue;                  /* we're filtering out this font */
1881         }
1882         if (!info || !started || strnullcasecmp(currcs, info->charset) ||
1883              strnullcasecmp(currstyle, info->style)) {
1884             /*
1885              * We've either finished a style/charset, or started a
1886              * new one, or both.
1887              */
1888             started = TRUE;
1889             if (currstyle) {
1890                 gtk_list_store_append(fs->style_model, &iter);
1891                 gtk_list_store_set(fs->style_model, &iter,
1892                                    0, currstyle, 1, minpos, 2, maxpos+1,
1893                                    3, TRUE, -1);
1894                 listindex++;
1895             }
1896             if (info) {
1897                 minpos = i;
1898                 if (info->charset && strnullcasecmp(currcs, info->charset)) {
1899                     gtk_list_store_append(fs->style_model, &iter);
1900                     gtk_list_store_set(fs->style_model, &iter,
1901                                        0, info->charset, 1, -1, 2, -1,
1902                                        3, FALSE, -1);
1903                     listindex++;
1904                 }
1905                 currcs = info->charset;
1906                 currstyle = info->style;
1907             }
1908         }
1909         if (!info)
1910             break;                     /* now we're done */
1911         info->styleindex = listindex;
1912         maxpos = i;
1913     }
1914 }
1915
1916 static const int unifontsel_default_sizes[] = { 10, 12, 14, 16, 20, 24, 32 };
1917
1918 static void unifontsel_setup_sizelist(unifontsel_internal *fs,
1919                                       int start, int end)
1920 {
1921     GtkTreeIter iter;
1922     int i, listindex;
1923     char sizetext[40];
1924     fontinfo *info;
1925
1926     gtk_list_store_clear(fs->size_model);
1927     listindex = 0;
1928
1929     /*
1930      * Search through the font tree for anything matching our
1931      * current filter criteria. When we find one, add its font
1932      * name to the list box.
1933      */
1934     for (i = start; i < end; i++) {
1935         info = (fontinfo *)index234(fs->fonts_by_selorder, i);
1936         if (info->flags &~ fs->filter_flags) {
1937             info->sizeindex = -1;
1938             continue;                  /* we're filtering out this font */
1939         }
1940         if (info->size) {
1941             sprintf(sizetext, "%d", info->size);
1942             info->sizeindex = listindex;
1943             gtk_list_store_append(fs->size_model, &iter);
1944             gtk_list_store_set(fs->size_model, &iter,
1945                                0, sizetext, 1, i, 2, info->size, -1);
1946             listindex++;
1947         } else {
1948             int j;
1949
1950             assert(i == start);
1951             assert(i+1 == end);
1952
1953             for (j = 0; j < lenof(unifontsel_default_sizes); j++) {
1954                 sprintf(sizetext, "%d", unifontsel_default_sizes[j]);
1955                 gtk_list_store_append(fs->size_model, &iter);
1956                 gtk_list_store_set(fs->size_model, &iter, 0, sizetext, 1, i,
1957                                    2, unifontsel_default_sizes[j], -1);
1958                 listindex++;
1959             }
1960         }
1961     }
1962 }
1963
1964 static void unifontsel_set_filter_buttons(unifontsel_internal *fs)
1965 {
1966     int i;
1967
1968     for (i = 0; i < lenof(fs->filter_buttons); i++) {
1969         int flagbit = GPOINTER_TO_INT(gtk_object_get_data
1970                                       (GTK_OBJECT(fs->filter_buttons[i]),
1971                                        "user-data"));
1972         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fs->filter_buttons[i]),
1973                                      !!(fs->filter_flags & flagbit));
1974     }
1975 }
1976
1977 static void unifontsel_draw_preview_text(unifontsel_internal *fs)
1978 {
1979     unifont *font;
1980     char *sizename = NULL;
1981     fontinfo *info = fs->selected;
1982
1983     if (info) {
1984         sizename = info->fontclass->scale_fontname
1985             (GTK_WIDGET(fs->u.window), info->realname, fs->selsize);
1986         font = info->fontclass->create(GTK_WIDGET(fs->u.window),
1987                                        sizename ? sizename : info->realname,
1988                                        FALSE, FALSE, 0, 0);
1989     } else
1990         font = NULL;
1991
1992     if (fs->preview_pixmap) {
1993         GdkGC *gc = gdk_gc_new(fs->preview_pixmap);
1994         gdk_gc_set_foreground(gc, &fs->preview_bg);
1995         gdk_draw_rectangle(fs->preview_pixmap, gc, 1, 0, 0,
1996                            fs->preview_width, fs->preview_height);
1997         gdk_gc_set_foreground(gc, &fs->preview_fg);
1998         if (font) {
1999             /*
2000              * The pangram used here is rather carefully
2001              * constructed: it contains a sequence of very narrow
2002              * letters (`jil') and a pair of adjacent very wide
2003              * letters (`wm').
2004              *
2005              * If the user selects a proportional font, it will be
2006              * coerced into fixed-width character cells when used
2007              * in the actual terminal window. We therefore display
2008              * it the same way in the preview pane, so as to show
2009              * it the way it will actually be displayed - and we
2010              * deliberately pick a pangram which will show the
2011              * resulting miskerning at its worst.
2012              *
2013              * We aren't trying to sell people these fonts; we're
2014              * trying to let them make an informed choice. Better
2015              * that they find out the problems with using
2016              * proportional fonts in terminal windows here than
2017              * that they go to the effort of selecting their font
2018              * and _then_ realise it was a mistake.
2019              */
2020             info->fontclass->draw_text(fs->preview_pixmap, gc, font,
2021                                        0, font->ascent,
2022                                        L"bankrupt jilted showmen quiz convex fogey",
2023                                        41, FALSE, FALSE, font->width);
2024             info->fontclass->draw_text(fs->preview_pixmap, gc, font,
2025                                        0, font->ascent + font->height,
2026                                        L"BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY",
2027                                        41, FALSE, FALSE, font->width);
2028             /*
2029              * The ordering of punctuation here is also selected
2030              * with some specific aims in mind. I put ` and '
2031              * together because some software (and people) still
2032              * use them as matched quotes no matter what Unicode
2033              * might say on the matter, so people can quickly
2034              * check whether they look silly in a candidate font.
2035              * The sequence #_@ is there to let people judge the
2036              * suitability of the underscore as an effectively
2037              * alphabetic character (since that's how it's often
2038              * used in practice, at least by programmers).
2039              */
2040             info->fontclass->draw_text(fs->preview_pixmap, gc, font,
2041                                        0, font->ascent + font->height * 2,
2042                                        L"0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$",
2043                                        42, FALSE, FALSE, font->width);
2044         }
2045         gdk_gc_unref(gc);
2046         gdk_window_invalidate_rect(fs->preview_area->window, NULL, FALSE);
2047     }
2048     if (font)
2049         info->fontclass->destroy(font);
2050
2051     sfree(sizename);
2052 }
2053
2054 static void unifontsel_select_font(unifontsel_internal *fs,
2055                                    fontinfo *info, int size, int leftlist,
2056                                    int size_is_explicit)
2057 {
2058     int index;
2059     int minval, maxval;
2060     GtkTreePath *treepath;
2061     GtkTreeIter iter;
2062
2063     fs->inhibit_response = TRUE;
2064
2065     fs->selected = info;
2066     fs->selsize = size;
2067     if (size_is_explicit)
2068         fs->intendedsize = size;
2069
2070     gtk_widget_set_sensitive(fs->u.ok_button, TRUE);
2071
2072     /*
2073      * Find the index of this fontinfo in the selorder list. 
2074      */
2075     index = -1;
2076     findpos234(fs->fonts_by_selorder, info, NULL, &index);
2077     assert(index >= 0);
2078
2079     /*
2080      * Adjust the font selector flags and redo the font family
2081      * list box, if necessary.
2082      */
2083     if (leftlist <= 0 &&
2084         (fs->filter_flags | info->flags) != fs->filter_flags) {
2085         fs->filter_flags |= info->flags;
2086         unifontsel_set_filter_buttons(fs);
2087         unifontsel_setup_familylist(fs);
2088     }
2089
2090     /*
2091      * Find the appropriate family name and select it in the list.
2092      */
2093     assert(info->familyindex >= 0);
2094     treepath = gtk_tree_path_new_from_indices(info->familyindex, -1);
2095     gtk_tree_selection_select_path
2096         (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->family_list)),
2097          treepath);
2098     gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->family_list),
2099                                  treepath, NULL, FALSE, 0.0, 0.0);
2100     gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, treepath);
2101     gtk_tree_path_free(treepath);
2102
2103     /*
2104      * Now set up the font style list.
2105      */
2106     gtk_tree_model_get(GTK_TREE_MODEL(fs->family_model), &iter,
2107                        1, &minval, 2, &maxval, -1);
2108     if (leftlist <= 1)
2109         unifontsel_setup_stylelist(fs, minval, maxval);
2110
2111     /*
2112      * Find the appropriate style name and select it in the list.
2113      */
2114     if (info->style) {
2115         assert(info->styleindex >= 0);
2116         treepath = gtk_tree_path_new_from_indices(info->styleindex, -1);
2117         gtk_tree_selection_select_path
2118             (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->style_list)),
2119              treepath);
2120         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->style_list),
2121                                      treepath, NULL, FALSE, 0.0, 0.0);
2122         gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->style_model),
2123                                 &iter, treepath);
2124         gtk_tree_path_free(treepath);
2125
2126         /*
2127          * And set up the size list.
2128          */
2129         gtk_tree_model_get(GTK_TREE_MODEL(fs->style_model), &iter,
2130                            1, &minval, 2, &maxval, -1);
2131         if (leftlist <= 2)
2132             unifontsel_setup_sizelist(fs, minval, maxval);
2133
2134         /*
2135          * Find the appropriate size, and select it in the list.
2136          */
2137         if (info->size) {
2138             assert(info->sizeindex >= 0);
2139             treepath = gtk_tree_path_new_from_indices(info->sizeindex, -1);
2140             gtk_tree_selection_select_path
2141                 (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->size_list)),
2142                  treepath);
2143             gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->size_list),
2144                                          treepath, NULL, FALSE, 0.0, 0.0);
2145             gtk_tree_path_free(treepath);
2146             size = info->size;
2147         } else {
2148             int j;
2149             for (j = 0; j < lenof(unifontsel_default_sizes); j++)
2150                 if (unifontsel_default_sizes[j] == size) {
2151                     treepath = gtk_tree_path_new_from_indices(j, -1);
2152                     gtk_tree_view_set_cursor(GTK_TREE_VIEW(fs->size_list),
2153                                              treepath, NULL, FALSE);
2154                     gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->size_list),
2155                                                  treepath, NULL, FALSE, 0.0,
2156                                                  0.0);
2157                     gtk_tree_path_free(treepath);
2158                 }
2159         }
2160
2161         /*
2162          * And set up the font size text entry box.
2163          */
2164         {
2165             char sizetext[40];
2166             sprintf(sizetext, "%d", size);
2167             gtk_entry_set_text(GTK_ENTRY(fs->size_entry), sizetext);
2168         }
2169     } else {
2170         if (leftlist <= 2)
2171             unifontsel_setup_sizelist(fs, 0, 0);
2172         gtk_entry_set_text(GTK_ENTRY(fs->size_entry), "");
2173     }
2174
2175     /*
2176      * Grey out the font size edit box if we're not using a
2177      * scalable font.
2178      */
2179     gtk_entry_set_editable(GTK_ENTRY(fs->size_entry), fs->selected->size == 0);
2180     gtk_widget_set_sensitive(fs->size_entry, fs->selected->size == 0);
2181
2182     unifontsel_draw_preview_text(fs);
2183
2184     fs->inhibit_response = FALSE;
2185 }
2186
2187 static void unifontsel_button_toggled(GtkToggleButton *tb, gpointer data)
2188 {
2189     unifontsel_internal *fs = (unifontsel_internal *)data;
2190     int newstate = gtk_toggle_button_get_active(tb);
2191     int newflags;
2192     int flagbit = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(tb),
2193                                                       "user-data"));
2194
2195     if (newstate)
2196         newflags = fs->filter_flags | flagbit;
2197     else
2198         newflags = fs->filter_flags & ~flagbit;
2199
2200     if (fs->filter_flags != newflags) {
2201         fs->filter_flags = newflags;
2202         unifontsel_setup_familylist(fs);
2203     }
2204 }
2205
2206 static void unifontsel_add_entry(void *ctx, const char *realfontname,
2207                                  const char *family, const char *charset,
2208                                  const char *style, const char *stylekey,
2209                                  int size, int flags,
2210                                  const struct unifont_vtable *fontclass)
2211 {
2212     unifontsel_internal *fs = (unifontsel_internal *)ctx;
2213     fontinfo *info;
2214     int totalsize;
2215     char *p;
2216
2217     totalsize = sizeof(fontinfo) + strlen(realfontname) +
2218         (family ? strlen(family) : 0) + (charset ? strlen(charset) : 0) +
2219         (style ? strlen(style) : 0) + (stylekey ? strlen(stylekey) : 0) + 10;
2220     info = (fontinfo *)smalloc(totalsize);
2221     info->fontclass = fontclass;
2222     p = (char *)info + sizeof(fontinfo);
2223     info->realname = p;
2224     strcpy(p, realfontname);
2225     p += 1+strlen(p);
2226     if (family) {
2227         info->family = p;
2228         strcpy(p, family);
2229         p += 1+strlen(p);
2230     } else
2231         info->family = NULL;
2232     if (charset) {
2233         info->charset = p;
2234         strcpy(p, charset);
2235         p += 1+strlen(p);
2236     } else
2237         info->charset = NULL;
2238     if (style) {
2239         info->style = p;
2240         strcpy(p, style);
2241         p += 1+strlen(p);
2242     } else
2243         info->style = NULL;
2244     if (stylekey) {
2245         info->stylekey = p;
2246         strcpy(p, stylekey);
2247         p += 1+strlen(p);
2248     } else
2249         info->stylekey = NULL;
2250     assert(p - (char *)info <= totalsize);
2251     info->size = size;
2252     info->flags = flags;
2253     info->index = count234(fs->fonts_by_selorder);
2254
2255     /*
2256      * It's just conceivable that a misbehaving font enumerator
2257      * might tell us about the same font real name more than once,
2258      * in which case we should silently drop the new one.
2259      */
2260     if (add234(fs->fonts_by_realname, info) != info) {
2261         sfree(info);
2262         return;
2263     }
2264     /*
2265      * However, we should never get a duplicate key in the
2266      * selorder tree, because the index field carefully
2267      * disambiguates otherwise identical records.
2268      */
2269     add234(fs->fonts_by_selorder, info);
2270 }
2271
2272 static fontinfo *update_for_intended_size(unifontsel_internal *fs,
2273                                           fontinfo *info)
2274 {
2275     fontinfo info2, *below, *above;
2276     int pos;
2277
2278     /*
2279      * Copy the info structure. This doesn't copy its dynamic
2280      * string fields, but that's unimportant because all we're
2281      * going to do is to adjust the size field and use it in one
2282      * tree search.
2283      */
2284     info2 = *info;
2285     info2.size = fs->intendedsize;
2286
2287     /*
2288      * Search in the tree to find the fontinfo structure which
2289      * best approximates the size the user last requested.
2290      */
2291     below = findrelpos234(fs->fonts_by_selorder, &info2, NULL,
2292                           REL234_LE, &pos);
2293     if (!below)
2294         pos = -1;
2295     above = index234(fs->fonts_by_selorder, pos+1);
2296
2297     /*
2298      * See if we've found it exactly, which is an easy special
2299      * case. If we have, it'll be in `below' and not `above',
2300      * because we did a REL234_LE rather than REL234_LT search.
2301      */
2302     if (below && !fontinfo_selorder_compare(&info2, below))
2303         return below;
2304
2305     /*
2306      * Now we've either found two suitable fonts, one smaller and
2307      * one larger, or we're at one or other extreme end of the
2308      * scale. Find out which, by NULLing out either of below and
2309      * above if it differs from this one in any respect but size
2310      * (and the disambiguating index field). Bear in mind, also,
2311      * that either one might _already_ be NULL if we're at the
2312      * extreme ends of the font list.
2313      */
2314     if (below) {
2315         info2.size = below->size;
2316         info2.index = below->index;
2317         if (fontinfo_selorder_compare(&info2, below))
2318             below = NULL;
2319     }
2320     if (above) {
2321         info2.size = above->size;
2322         info2.index = above->index;
2323         if (fontinfo_selorder_compare(&info2, above))
2324             above = NULL;
2325     }
2326
2327     /*
2328      * Now return whichever of above and below is non-NULL, if
2329      * that's unambiguous.
2330      */
2331     if (!above)
2332         return below;
2333     if (!below)
2334         return above;
2335
2336     /*
2337      * And now we really do have to make a choice about whether to
2338      * round up or down. We'll do it by rounding to nearest,
2339      * breaking ties by rounding up.
2340      */
2341     if (above->size - fs->intendedsize <= fs->intendedsize - below->size)
2342         return above;
2343     else
2344         return below;
2345 }
2346
2347 static void family_changed(GtkTreeSelection *treeselection, gpointer data)
2348 {
2349     unifontsel_internal *fs = (unifontsel_internal *)data;
2350     GtkTreeModel *treemodel;
2351     GtkTreeIter treeiter;
2352     int minval;
2353     fontinfo *info;
2354
2355     if (fs->inhibit_response)          /* we made this change ourselves */
2356         return;
2357
2358     if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))
2359         return;
2360
2361     gtk_tree_model_get(treemodel, &treeiter, 1, &minval, -1);
2362     info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
2363     info = update_for_intended_size(fs, info);
2364     if (!info)
2365         return; /* _shouldn't_ happen unless font list is completely funted */
2366     if (!info->size)
2367         fs->selsize = fs->intendedsize;   /* font is scalable */
2368     unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize,
2369                            1, FALSE);
2370 }
2371
2372 static void style_changed(GtkTreeSelection *treeselection, gpointer data)
2373 {
2374     unifontsel_internal *fs = (unifontsel_internal *)data;
2375     GtkTreeModel *treemodel;
2376     GtkTreeIter treeiter;
2377     int minval;
2378     fontinfo *info;
2379
2380     if (fs->inhibit_response)          /* we made this change ourselves */
2381         return;
2382
2383     if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))
2384         return;
2385
2386     gtk_tree_model_get(treemodel, &treeiter, 1, &minval, -1);
2387     if (minval < 0)
2388         return;                    /* somehow a charset heading got clicked */
2389     info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
2390     info = update_for_intended_size(fs, info);
2391     if (!info)
2392         return; /* _shouldn't_ happen unless font list is completely funted */
2393     if (!info->size)
2394         fs->selsize = fs->intendedsize;   /* font is scalable */
2395     unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize,
2396                            2, FALSE);
2397 }
2398
2399 static void size_changed(GtkTreeSelection *treeselection, gpointer data)
2400 {
2401     unifontsel_internal *fs = (unifontsel_internal *)data;
2402     GtkTreeModel *treemodel;
2403     GtkTreeIter treeiter;
2404     int minval, size;
2405     fontinfo *info;
2406
2407     if (fs->inhibit_response)          /* we made this change ourselves */
2408         return;
2409
2410     if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))
2411         return;
2412
2413     gtk_tree_model_get(treemodel, &treeiter, 1, &minval, 2, &size, -1);
2414     info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
2415     unifontsel_select_font(fs, info, info->size ? info->size : size, 3, TRUE);
2416 }
2417
2418 static void size_entry_changed(GtkEditable *ed, gpointer data)
2419 {
2420     unifontsel_internal *fs = (unifontsel_internal *)data;
2421     const char *text;
2422     int size;
2423
2424     if (fs->inhibit_response)          /* we made this change ourselves */
2425         return;
2426
2427     text = gtk_entry_get_text(GTK_ENTRY(ed));
2428     size = atoi(text);
2429
2430     if (size > 0) {
2431         assert(fs->selected->size == 0);
2432         unifontsel_select_font(fs, fs->selected, size, 3, TRUE);
2433     }
2434 }
2435
2436 static void alias_resolve(GtkTreeView *treeview, GtkTreePath *path,
2437                           GtkTreeViewColumn *column, gpointer data)
2438 {
2439     unifontsel_internal *fs = (unifontsel_internal *)data;
2440     GtkTreeIter iter;
2441     int minval, newsize;
2442     fontinfo *info, *newinfo;
2443     char *newname;
2444
2445     if (fs->inhibit_response)          /* we made this change ourselves */
2446         return;
2447
2448     gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, path);
2449     gtk_tree_model_get(GTK_TREE_MODEL(fs->family_model), &iter, 1,&minval, -1);
2450     info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
2451     if (info) {
2452         int flags;
2453         struct fontinfo_realname_find f;
2454
2455         newname = info->fontclass->canonify_fontname
2456             (GTK_WIDGET(fs->u.window), info->realname, &newsize, &flags, TRUE);
2457
2458         f.realname = newname;
2459         f.flags = flags;
2460         newinfo = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);
2461
2462         sfree(newname);
2463         if (!newinfo)
2464             return;                    /* font name not in our index */
2465         if (newinfo == info)
2466             return;   /* didn't change under canonification => not an alias */
2467         unifontsel_select_font(fs, newinfo,
2468                                newinfo->size ? newinfo->size : newsize,
2469                                1, TRUE);
2470     }
2471 }
2472
2473 static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event,
2474                                    gpointer data)
2475 {
2476     unifontsel_internal *fs = (unifontsel_internal *)data;
2477
2478     if (fs->preview_pixmap) {
2479         gdk_draw_pixmap(widget->window,
2480                         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2481                         fs->preview_pixmap,
2482                         event->area.x, event->area.y,
2483                         event->area.x, event->area.y,
2484                         event->area.width, event->area.height);
2485     }
2486     return TRUE;
2487 }
2488
2489 static gint unifontsel_configure_area(GtkWidget *widget,
2490                                       GdkEventConfigure *event, gpointer data)
2491 {
2492     unifontsel_internal *fs = (unifontsel_internal *)data;
2493     int ox, oy, nx, ny, x, y;
2494
2495     /*
2496      * Enlarge the pixmap, but never shrink it.
2497      */
2498     ox = fs->preview_width;
2499     oy = fs->preview_height;
2500     x = event->width;
2501     y = event->height;
2502     if (x > ox || y > oy) {
2503         if (fs->preview_pixmap)
2504             gdk_pixmap_unref(fs->preview_pixmap);
2505         
2506         nx = (x > ox ? x : ox);
2507         ny = (y > oy ? y : oy);
2508         fs->preview_pixmap = gdk_pixmap_new(widget->window, nx, ny, -1);
2509         fs->preview_width = nx;
2510         fs->preview_height = ny;
2511
2512         unifontsel_draw_preview_text(fs);
2513     }
2514
2515     gdk_window_invalidate_rect(widget->window, NULL, FALSE);
2516
2517     return TRUE;
2518 }
2519
2520 unifontsel *unifontsel_new(const char *wintitle)
2521 {
2522     unifontsel_internal *fs = snew(unifontsel_internal);
2523     GtkWidget *table, *label, *w, *ww, *scroll;
2524     GtkListStore *model;
2525     GtkTreeViewColumn *column;
2526     int lists_height, preview_height, font_width, style_width, size_width;
2527     int i;
2528
2529     fs->inhibit_response = FALSE;
2530     fs->selected = NULL;
2531
2532     {
2533         /*
2534          * Invent some magic size constants.
2535          */
2536         GtkRequisition req;
2537         label = gtk_label_new("Quite Long Font Name (Foundry)");
2538         gtk_widget_size_request(label, &req);
2539         font_width = req.width;
2540         lists_height = 14 * req.height;
2541         preview_height = 5 * req.height;
2542         gtk_label_set_text(GTK_LABEL(label), "Italic Extra Condensed");
2543         gtk_widget_size_request(label, &req);
2544         style_width = req.width;
2545         gtk_label_set_text(GTK_LABEL(label), "48000");
2546         gtk_widget_size_request(label, &req);
2547         size_width = req.width;
2548 #if GTK_CHECK_VERSION(2,10,0)
2549         g_object_ref_sink(label);
2550         g_object_unref(label);
2551 #else
2552         gtk_object_sink(GTK_OBJECT(label));
2553 #endif
2554     }
2555
2556     /*
2557      * Create the dialog box and initialise the user-visible
2558      * fields in the returned structure.
2559      */
2560     fs->u.user_data = NULL;
2561     fs->u.window = GTK_WINDOW(gtk_dialog_new());
2562     gtk_window_set_title(fs->u.window, wintitle);
2563     fs->u.cancel_button = gtk_dialog_add_button
2564         (GTK_DIALOG(fs->u.window), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
2565     fs->u.ok_button = gtk_dialog_add_button
2566         (GTK_DIALOG(fs->u.window), GTK_STOCK_OK, GTK_RESPONSE_OK);
2567     gtk_widget_grab_default(fs->u.ok_button);
2568
2569     /*
2570      * Now set up the internal fields, including in particular all
2571      * the controls that actually allow the user to select fonts.
2572      */
2573     table = gtk_table_new(8, 3, FALSE);
2574     gtk_widget_show(table);
2575     gtk_table_set_col_spacings(GTK_TABLE(table), 8);
2576 #if GTK_CHECK_VERSION(2,4,0)
2577     /* GtkAlignment seems to be the simplest way to put padding round things */
2578     w = gtk_alignment_new(0, 0, 1, 1);
2579     gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8);
2580     gtk_container_add(GTK_CONTAINER(w), table);
2581     gtk_widget_show(w);
2582 #else
2583     w = table;
2584 #endif
2585     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(fs->u.window)->vbox),
2586                        w, TRUE, TRUE, 0);
2587
2588     label = gtk_label_new_with_mnemonic("_Font:");
2589     gtk_widget_show(label);
2590     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
2591     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
2592
2593     /*
2594      * The Font list box displays only a string, but additionally
2595      * stores two integers which give the limits within the
2596      * tree234 of the font entries covered by this list entry.
2597      */
2598     model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
2599     w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
2600     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);
2601     gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);
2602     gtk_widget_show(w);
2603     column = gtk_tree_view_column_new_with_attributes
2604         ("Font", gtk_cell_renderer_text_new(),
2605          "text", 0, (char *)NULL);
2606     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2607     gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
2608     g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),
2609                      "changed", G_CALLBACK(family_changed), fs);
2610     g_signal_connect(G_OBJECT(w), "row-activated",
2611                      G_CALLBACK(alias_resolve), fs);
2612
2613     scroll = gtk_scrolled_window_new(NULL, NULL);
2614     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
2615                                         GTK_SHADOW_IN);
2616     gtk_container_add(GTK_CONTAINER(scroll), w);
2617     gtk_widget_show(scroll);
2618     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
2619                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2620     gtk_widget_set_size_request(scroll, font_width, lists_height);
2621     gtk_table_attach(GTK_TABLE(table), scroll, 0, 1, 1, 3, GTK_FILL,
2622                      GTK_EXPAND | GTK_FILL, 0, 0);
2623     fs->family_model = model;
2624     fs->family_list = w;
2625
2626     label = gtk_label_new_with_mnemonic("_Style:");
2627     gtk_widget_show(label);
2628     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
2629     gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, GTK_FILL, 0, 0, 0);
2630
2631     /*
2632      * The Style list box can contain insensitive elements
2633      * (character set headings for server-side fonts), so we add
2634      * an extra column to the list store to hold that information.
2635      */
2636     model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT,
2637                                G_TYPE_BOOLEAN);
2638     w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
2639     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);
2640     gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);
2641     gtk_widget_show(w);
2642     column = gtk_tree_view_column_new_with_attributes
2643         ("Style", gtk_cell_renderer_text_new(),
2644          "text", 0, "sensitive", 3, (char *)NULL);
2645     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2646     gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
2647     g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),
2648                      "changed", G_CALLBACK(style_changed), fs);
2649
2650     scroll = gtk_scrolled_window_new(NULL, NULL);
2651     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
2652                                         GTK_SHADOW_IN);
2653     gtk_container_add(GTK_CONTAINER(scroll), w);
2654     gtk_widget_show(scroll);
2655     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
2656                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2657     gtk_widget_set_size_request(scroll, style_width, lists_height);
2658     gtk_table_attach(GTK_TABLE(table), scroll, 1, 2, 1, 3, GTK_FILL,
2659                      GTK_EXPAND | GTK_FILL, 0, 0);
2660     fs->style_model = model;
2661     fs->style_list = w;
2662
2663     label = gtk_label_new_with_mnemonic("Si_ze:");
2664     gtk_widget_show(label);
2665     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
2666     gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 0, 0);
2667
2668     /*
2669      * The Size label attaches primarily to a text input box so
2670      * that the user can select a size of their choice. The list
2671      * of available sizes is secondary.
2672      */
2673     fs->size_entry = w = gtk_entry_new();
2674     gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);
2675     gtk_widget_set_size_request(w, size_width, -1);
2676     gtk_widget_show(w);
2677     gtk_table_attach(GTK_TABLE(table), w, 2, 3, 1, 2, GTK_FILL, 0, 0, 0);
2678     g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(size_entry_changed),
2679                      fs);
2680
2681     model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
2682     w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
2683     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);
2684     gtk_widget_show(w);
2685     column = gtk_tree_view_column_new_with_attributes
2686         ("Size", gtk_cell_renderer_text_new(),
2687          "text", 0, (char *)NULL);
2688     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2689     gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
2690     g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),
2691                      "changed", G_CALLBACK(size_changed), fs);
2692
2693     scroll = gtk_scrolled_window_new(NULL, NULL);
2694     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
2695                                         GTK_SHADOW_IN);
2696     gtk_container_add(GTK_CONTAINER(scroll), w);
2697     gtk_widget_show(scroll);
2698     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
2699                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2700     gtk_table_attach(GTK_TABLE(table), scroll, 2, 3, 2, 3, GTK_FILL,
2701                      GTK_EXPAND | GTK_FILL, 0, 0);
2702     fs->size_model = model;
2703     fs->size_list = w;
2704
2705     /*
2706      * Preview widget.
2707      */
2708     fs->preview_area = gtk_drawing_area_new();
2709     fs->preview_pixmap = NULL;
2710     fs->preview_width = 0;
2711     fs->preview_height = 0;
2712     fs->preview_fg.pixel = fs->preview_bg.pixel = 0;
2713     fs->preview_fg.red = fs->preview_fg.green = fs->preview_fg.blue = 0x0000;
2714     fs->preview_bg.red = fs->preview_bg.green = fs->preview_bg.blue = 0xFFFF;
2715     gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_fg,
2716                              FALSE, FALSE);
2717     gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_bg,
2718                              FALSE, FALSE);
2719     gtk_signal_connect(GTK_OBJECT(fs->preview_area), "expose_event",
2720                        GTK_SIGNAL_FUNC(unifontsel_expose_area), fs);
2721     gtk_signal_connect(GTK_OBJECT(fs->preview_area), "configure_event",
2722                        GTK_SIGNAL_FUNC(unifontsel_configure_area), fs);
2723     gtk_widget_set_size_request(fs->preview_area, 1, preview_height);
2724     gtk_widget_show(fs->preview_area);
2725     ww = fs->preview_area;
2726     w = gtk_frame_new(NULL);
2727     gtk_container_add(GTK_CONTAINER(w), ww);
2728     gtk_widget_show(w);
2729 #if GTK_CHECK_VERSION(2,4,0)
2730     ww = w;
2731     /* GtkAlignment seems to be the simplest way to put padding round things */
2732     w = gtk_alignment_new(0, 0, 1, 1);
2733     gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8);
2734     gtk_container_add(GTK_CONTAINER(w), ww);
2735     gtk_widget_show(w);
2736 #endif
2737     ww = w;
2738     w = gtk_frame_new("Preview of font");
2739     gtk_container_add(GTK_CONTAINER(w), ww);
2740     gtk_widget_show(w);
2741     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 3, 4,
2742                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 8);
2743
2744     i = 0;
2745     w = gtk_check_button_new_with_label("Show client-side fonts");
2746     gtk_object_set_data(GTK_OBJECT(w), "user-data",
2747                         GINT_TO_POINTER(FONTFLAG_CLIENTSIDE));
2748     gtk_signal_connect(GTK_OBJECT(w), "toggled",
2749                        GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
2750     gtk_widget_show(w);
2751     fs->filter_buttons[i++] = w;
2752     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 4, 5, GTK_FILL, 0, 0, 0);
2753     w = gtk_check_button_new_with_label("Show server-side fonts");
2754     gtk_object_set_data(GTK_OBJECT(w), "user-data",
2755                         GINT_TO_POINTER(FONTFLAG_SERVERSIDE));
2756     gtk_signal_connect(GTK_OBJECT(w), "toggled",
2757                        GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
2758     gtk_widget_show(w);
2759     fs->filter_buttons[i++] = w;
2760     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 5, 6, GTK_FILL, 0, 0, 0);
2761     w = gtk_check_button_new_with_label("Show server-side font aliases");
2762     gtk_object_set_data(GTK_OBJECT(w), "user-data",
2763                         GINT_TO_POINTER(FONTFLAG_SERVERALIAS));
2764     gtk_signal_connect(GTK_OBJECT(w), "toggled",
2765                        GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
2766     gtk_widget_show(w);
2767     fs->filter_buttons[i++] = w;
2768     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 6, 7, GTK_FILL, 0, 0, 0);
2769     w = gtk_check_button_new_with_label("Show non-monospaced fonts");
2770     gtk_object_set_data(GTK_OBJECT(w), "user-data",
2771                         GINT_TO_POINTER(FONTFLAG_NONMONOSPACED));
2772     gtk_signal_connect(GTK_OBJECT(w), "toggled",
2773                        GTK_SIGNAL_FUNC(unifontsel_button_toggled), fs);
2774     gtk_widget_show(w);
2775     fs->filter_buttons[i++] = w;
2776     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 7, 8, GTK_FILL, 0, 0, 0);
2777
2778     assert(i == lenof(fs->filter_buttons));
2779     fs->filter_flags = FONTFLAG_CLIENTSIDE | FONTFLAG_SERVERSIDE |
2780         FONTFLAG_SERVERALIAS;
2781     unifontsel_set_filter_buttons(fs);
2782
2783     /*
2784      * Go and find all the font names, and set up our master font
2785      * list.
2786      */
2787     fs->fonts_by_realname = newtree234(fontinfo_realname_compare);
2788     fs->fonts_by_selorder = newtree234(fontinfo_selorder_compare);
2789     for (i = 0; i < lenof(unifont_types); i++)
2790         unifont_types[i]->enum_fonts(GTK_WIDGET(fs->u.window),
2791                                      unifontsel_add_entry, fs);
2792
2793     /*
2794      * And set up the initial font names list.
2795      */
2796     unifontsel_setup_familylist(fs);
2797
2798     fs->selsize = fs->intendedsize = 13;   /* random default */
2799     gtk_widget_set_sensitive(fs->u.ok_button, FALSE);
2800
2801     return (unifontsel *)fs;
2802 }
2803
2804 void unifontsel_destroy(unifontsel *fontsel)
2805 {
2806     unifontsel_internal *fs = (unifontsel_internal *)fontsel;
2807     fontinfo *info;
2808
2809     if (fs->preview_pixmap)
2810         gdk_pixmap_unref(fs->preview_pixmap);
2811
2812     freetree234(fs->fonts_by_selorder);
2813     while ((info = delpos234(fs->fonts_by_realname, 0)) != NULL)
2814         sfree(info);
2815     freetree234(fs->fonts_by_realname);
2816
2817     gtk_widget_destroy(GTK_WIDGET(fs->u.window));
2818     sfree(fs);
2819 }
2820
2821 void unifontsel_set_name(unifontsel *fontsel, const char *fontname)
2822 {
2823     unifontsel_internal *fs = (unifontsel_internal *)fontsel;
2824     int i, start, end, size, flags;
2825     const char *fontname2 = NULL;
2826     fontinfo *info;
2827
2828     /*
2829      * Provide a default if given an empty or null font name.
2830      */
2831     if (!fontname || !*fontname)
2832         fontname = "server:fixed";
2833
2834     /*
2835      * Call the canonify_fontname function.
2836      */
2837     fontname = unifont_do_prefix(fontname, &start, &end);
2838     for (i = start; i < end; i++) {
2839         fontname2 = unifont_types[i]->canonify_fontname
2840             (GTK_WIDGET(fs->u.window), fontname, &size, &flags, FALSE);
2841         if (fontname2)
2842             break;
2843     }
2844     if (i == end)
2845         return;                        /* font name not recognised */
2846
2847     /*
2848      * Now look up the canonified font name in our index.
2849      */
2850     {
2851         struct fontinfo_realname_find f;
2852         f.realname = fontname2;
2853         f.flags = flags;
2854         info = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);
2855     }
2856
2857     /*
2858      * If we've found the font, and its size field is either
2859      * correct or zero (the latter indicating a scalable font),
2860      * then we're done. Otherwise, try looking up the original
2861      * font name instead.
2862      */
2863     if (!info || (info->size != size && info->size != 0)) {
2864         struct fontinfo_realname_find f;
2865         f.realname = fontname;
2866         f.flags = flags;
2867
2868         info = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);
2869         if (!info || info->size != size)
2870             return;                    /* font name not in our index */
2871     }
2872
2873     /*
2874      * Now we've got a fontinfo structure and a font size, so we
2875      * know everything we need to fill in all the fields in the
2876      * dialog.
2877      */
2878     unifontsel_select_font(fs, info, size, 0, TRUE);
2879 }
2880
2881 char *unifontsel_get_name(unifontsel *fontsel)
2882 {
2883     unifontsel_internal *fs = (unifontsel_internal *)fontsel;
2884     char *name;
2885
2886     if (!fs->selected)
2887         return NULL;
2888
2889     if (fs->selected->size == 0) {
2890         name = fs->selected->fontclass->scale_fontname
2891             (GTK_WIDGET(fs->u.window), fs->selected->realname, fs->selsize);
2892         if (name) {
2893             char *ret = dupcat(fs->selected->fontclass->prefix, ":",
2894                                name, NULL);
2895             sfree(name);
2896             return ret;
2897         }
2898     }
2899
2900     return dupcat(fs->selected->fontclass->prefix, ":",
2901                   fs->selected->realname, NULL);
2902 }
2903
2904 #endif /* GTK_CHECK_VERSION(2,0,0) */