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