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