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