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