]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - unix/gtkfont.c
Introduce a config option for building on OS X GTK.
[PuTTY.git] / unix / gtkfont.c
index cd0097b184ec9192ca820347043939da7c9d87ae..1a9860ab0eee5c14216365d7c7daed9d9055fcac 100644 (file)
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
+
 #include <gtk/gtk.h>
 #if !GTK_CHECK_VERSION(3,0,0)
 #include <gdk/gdkkeysyms.h>
 #endif
+
+#include "putty.h"
+#include "gtkfont.h"
+#include "gtkcompat.h"
+#include "tree234.h"
+
 #ifndef NOT_X_WINDOWS
 #include <gdk/gdkx.h>
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
 #endif
 
-#include "putty.h"
-#include "gtkfont.h"
-#include "tree234.h"
-
 /*
  * Future work:
  * 
  *  - it would be nice to have a display of the current font name,
  *    and in particular whether it's client- or server-side,
  *    during the progress of the font selector.
- * 
- *  - it would be nice if we could move the processing of
- *    underline and VT100 double width into this module, so that
- *    instead of using the ghastly pixmap-stretching technique
- *    everywhere we could tell the Pango backend to scale its
- *    fonts to double size properly and at full resolution.
- *    However, this requires me to learn how to make Pango stretch
- *    text to an arbitrary aspect ratio (for double-width only
- *    text, which perversely is harder than DW+DH), and right now
- *    I haven't the energy.
  */
 
 #if !GLIB_CHECK_VERSION(1,3,7)
@@ -218,7 +211,7 @@ static const struct unifont_vtable x11font_vtable = {
 
 static char *x11_guess_derived_font_name(XFontStruct *xfs, int bold, int wide)
 {
-    Display *disp = GDK_DISPLAY();
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     Atom fontprop = XInternAtom(disp, "FONT", False);
     unsigned long ret;
     if (XGetFontProperty(xfs, fontprop, &ret)) {
@@ -349,7 +342,7 @@ static unifont *x11font_create(GtkWidget *widget, const char *name,
 {
     struct x11font *xfont;
     XFontStruct *xfs;
-    Display *disp = GDK_DISPLAY();
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     Atom charset_registry, charset_encoding, spacing;
     unsigned long registry_ret, encoding_ret, spacing_ret;
     int pubcs, realcs, sixteen_bit, variable;
@@ -455,7 +448,7 @@ static unifont *x11font_create(GtkWidget *widget, const char *name,
 
 static void x11font_destroy(unifont *font)
 {
-    Display *disp = GDK_DISPLAY();
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     struct x11font *xfont = (struct x11font *)font;
     int i;
 
@@ -482,7 +475,7 @@ static void x11font_destroy(unifont *font)
 
 static void x11_alloc_subfont(struct x11font *xfont, int sfid)
 {
-    Display *disp = GDK_DISPLAY();
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     char *derived_name = x11_guess_derived_font_name
        (xfont->fonts[0].xfs, sfid & 1, !!(sfid & 2));
     xfont->fonts[sfid].xfs = XLoadQueryFont(disp, derived_name);
@@ -520,6 +513,8 @@ static int x11font_has_glyph(unifont *font, wchar_t glyph)
 
 #if !GTK_CHECK_VERSION(2,0,0)
 #define GDK_DRAWABLE_XID(d) GDK_WINDOW_XWINDOW(d) /* GTK1's name for this */
+#elif GTK_CHECK_VERSION(3,0,0)
+#define GDK_DRAWABLE_XID(d) GDK_WINDOW_XID(d) /* GTK3's name for this */
 #endif
 
 static int x11font_width_16(unifont_drawctx *ctx, x11font_individual *xfi,
@@ -539,15 +534,17 @@ static int x11font_width_8(unifont_drawctx *ctx, x11font_individual *xfi,
 #ifdef DRAW_TEXT_GDK
 static void x11font_gdk_setup(unifont_drawctx *ctx, x11font_individual *xfi)
 {
-    XSetFont(GDK_DISPLAY(), GDK_GC_XGC(ctx->u.gdk.gc), xfi->xfs->fid);
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+    XSetFont(disp, GDK_GC_XGC(ctx->u.gdk.gc), xfi->xfs->fid);
 }
 
 static void x11font_gdk_draw_16(unifont_drawctx *ctx,
                                 x11font_individual *xfi, int x, int y,
                                 const void *vstring, int start, int length)
 {
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     const XChar2b *string = (const XChar2b *)vstring;
-    XDrawString16(GDK_DISPLAY(), GDK_DRAWABLE_XID(ctx->u.gdk.target),
+    XDrawString16(disp, GDK_DRAWABLE_XID(ctx->u.gdk.target),
                   GDK_GC_XGC(ctx->u.gdk.gc), x, y, string+start, length);
 }
 
@@ -555,8 +552,9 @@ static void x11font_gdk_draw_8(unifont_drawctx *ctx,
                                x11font_individual *xfi, int x, int y,
                                const void *vstring, int start, int length)
 {
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     const char *string = (const char *)vstring;
-    XDrawString(GDK_DISPLAY(), GDK_DRAWABLE_XID(ctx->u.gdk.target),
+    XDrawString(disp, GDK_DRAWABLE_XID(ctx->u.gdk.target),
                 GDK_GC_XGC(ctx->u.gdk.gc), x, y, string+start, length);
 }
 #endif
@@ -565,6 +563,7 @@ static void x11font_gdk_draw_8(unifont_drawctx *ctx,
 static void x11font_cairo_setup(unifont_drawctx *ctx, x11font_individual *xfi)
 {
     if (xfi->pixmap == None) {
+        Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
         XGCValues gcvals;
         GdkWindow *widgetwin = gtk_widget_get_window(ctx->u.cairo.widget);
         int widgetscr = GDK_SCREEN_XNUMBER(gdk_window_get_screen(widgetwin));
@@ -597,13 +596,13 @@ static void x11font_cairo_setup(unifont_drawctx *ctx, x11font_individual *xfi)
         }
 
         xfi->pixmap = XCreatePixmap
-            (GDK_DISPLAY(),
+            (disp,
              GDK_DRAWABLE_XID(gtk_widget_get_window(ctx->u.cairo.widget)),
              xfi->pixwidth, xfi->pixheight, 1);
-        gcvals.foreground = WhitePixel(GDK_DISPLAY(), widgetscr);
-        gcvals.background = BlackPixel(GDK_DISPLAY(), widgetscr);
+        gcvals.foreground = WhitePixel(disp, widgetscr);
+        gcvals.background = BlackPixel(disp, widgetscr);
         gcvals.font = xfi->xfs->fid;
-        xfi->gc = XCreateGC(GDK_DISPLAY(), xfi->pixmap,
+        xfi->gc = XCreateGC(disp, xfi->pixmap,
                             GCForeground | GCBackground | GCFont, &gcvals);
     }
 }
@@ -613,11 +612,12 @@ static void x11font_cairo_cache_glyph(x11font_individual *xfi, int glyphindex)
     XImage *image;
     int x, y;
     unsigned char *bitmap;
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
 
     bitmap = snewn(xfi->allsize, unsigned char);
     memset(bitmap, 0, xfi->allsize);
 
-    image = XGetImage(GDK_DISPLAY(), xfi->pixmap, 0, 0,
+    image = XGetImage(disp, xfi->pixmap, 0, 0,
                       xfi->pixwidth, xfi->pixheight, AllPlanes, XYPixmap);
     for (y = 0; y < xfi->pixheight; y++) {
         for (x = 0; x < xfi->pixwidth; x++) {
@@ -636,7 +636,7 @@ static void x11font_cairo_cache_glyph(x11font_individual *xfi, int glyphindex)
          * principle that Unicode characters come in contiguous blocks
          * often used together */
         int old_nglyphs = xfi->nglyphs;
-        xfi->nglyphs = (glyphindex + 0xFF) & ~0xFF;
+        xfi->nglyphs = (glyphindex + 0x100) & ~0xFF;
         xfi->glyphcache = sresize(xfi->glyphcache, xfi->nglyphs,
                                   struct cairo_cached_glyph);
 
@@ -666,6 +666,7 @@ static void x11font_cairo_draw_16(unifont_drawctx *ctx,
                                   x11font_individual *xfi, int x, int y,
                                   const void *vstring, int start, int length)
 {
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     const XChar2b *string = (const XChar2b *)vstring + start;
     int i;
     for (i = 0; i < length; i++) {
@@ -674,7 +675,7 @@ static void x11font_cairo_draw_16(unifont_drawctx *ctx,
                               (unsigned char)string[i].byte2);
             if (glyphindex >= xfi->nglyphs ||
                 !xfi->glyphcache[glyphindex].surface) {
-                XDrawImageString16(GDK_DISPLAY(), xfi->pixmap, xfi->gc,
+                XDrawImageString16(disp, xfi->pixmap, xfi->gc,
                                    xfi->pixoriginx, xfi->pixoriginy,
                                    string+i, 1);
                 x11font_cairo_cache_glyph(xfi, glyphindex);
@@ -689,6 +690,7 @@ static void x11font_cairo_draw_8(unifont_drawctx *ctx,
                                  x11font_individual *xfi, int x, int y,
                                  const void *vstring, int start, int length)
 {
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     const char *string = (const char *)vstring + start;
     int i;
     for (i = 0; i < length; i++) {
@@ -696,7 +698,7 @@ static void x11font_cairo_draw_8(unifont_drawctx *ctx,
             int glyphindex = (unsigned char)string[i];
             if (glyphindex >= xfi->nglyphs ||
                 !xfi->glyphcache[glyphindex].surface) {
-                XDrawImageString(GDK_DISPLAY(), xfi->pixmap, xfi->gc,
+                XDrawImageString(disp, xfi->pixmap, xfi->gc,
                                  xfi->pixoriginx, xfi->pixoriginy,
                                  string+i, 1);
                 x11font_cairo_cache_glyph(xfi, glyphindex);
@@ -866,13 +868,14 @@ static void x11font_draw_text(unifont_drawctx *ctx, unifont *font,
 static void x11font_enum_fonts(GtkWidget *widget,
                               fontsel_add_entry callback, void *callback_ctx)
 {
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     char **fontnames;
     char *tmp = NULL;
     int nnames, i, max, tmpsize;
 
     max = 32768;
     while (1) {
-       fontnames = XListFonts(GDK_DISPLAY(), "*", max, &nnames);
+       fontnames = XListFonts(disp, "*", max, &nnames);
        if (nnames >= max) {
            XFreeFontNames(fontnames);
            max *= 2;
@@ -1047,7 +1050,7 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,
      * selector treats them as worthwhile in their own right.
      */
     XFontStruct *xfs;
-    Display *disp = GDK_DISPLAY();
+    Display *disp = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
     Atom fontprop, fontprop2;
     unsigned long ret;
 
@@ -1136,6 +1139,13 @@ struct pangofont {
      * Data passed in to unifont_create().
      */
     int bold, shadowoffset, shadowalways;
+    /*
+     * Cache of character widths, indexed by Unicode code point. In
+     * pixels; -1 means we haven't asked Pango about this character
+     * before.
+     */
+    int *widthcache;
+    unsigned nwidthcache;
 };
 
 static const struct unifont_vtable pangofont_vtable = {
@@ -1259,6 +1269,8 @@ static unifont *pangofont_create_internal(GtkWidget *widget,
     pfont->bold = bold;
     pfont->shadowoffset = shadowoffset;
     pfont->shadowalways = shadowalways;
+    pfont->widthcache = NULL;
+    pfont->nwidthcache = 0;
 
     pango_font_metrics_unref(metrics);
 
@@ -1312,10 +1324,40 @@ static void pangofont_destroy(unifont *font)
 {
     struct pangofont *pfont = (struct pangofont *)font;
     pango_font_description_free(pfont->desc);
+    sfree(pfont->widthcache);
     g_object_unref(pfont->fset);
     sfree(font);
 }
 
+static int pangofont_char_width(PangoLayout *layout, struct pangofont *pfont,
+                                wchar_t uchr, const char *utfchr, int utflen)
+{
+    /*
+     * Here we check whether a character has the same width as the
+     * character cell it'll be drawn in. Because profiling showed that
+     * pango_layout_get_pixel_extents() was a huge bottleneck when we
+     * were calling it every time we needed to know this, we instead
+     * call it only on characters we don't already know about, and
+     * cache the results.
+     */
+
+    if ((unsigned)uchr >= pfont->nwidthcache) {
+        unsigned newsize = ((int)uchr + 0x100) & ~0xFF;
+        pfont->widthcache = sresize(pfont->widthcache, newsize, int);
+        while (pfont->nwidthcache < newsize)
+            pfont->widthcache[pfont->nwidthcache++] = -1;
+    }
+
+    if (pfont->widthcache[uchr] < 0) {
+        PangoRectangle rect;
+        pango_layout_set_text(layout, utfchr, utflen);
+        pango_layout_get_pixel_extents(layout, NULL, &rect);
+        pfont->widthcache[uchr] = rect.width;
+    }
+
+    return pfont->widthcache[uchr];
+}
+
 static int pangofont_has_glyph(unifont *font, wchar_t glyph)
 {
     /* Pango implements font fallback, so assume it has everything */
@@ -1428,39 +1470,34 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
            clen++;
        n = 1;
 
-        /*
-         * If it's a right-to-left character, we must display it on
-         * its own, to stop Pango helpfully re-reversing our already
-         * reversed text.
-         */
-        if (!is_rtl(string[0])) {
-
+        if (is_rtl(string[0]) ||
+            pangofont_char_width(layout, pfont, string[n-1],
+                                 utfptr, clen) != cellwidth) {
             /*
-             * See if that character has the width we expect.
+             * If this character is a right-to-left one, or has an
+             * unusual width, then we must display it on its own.
              */
-            pango_layout_set_text(layout, utfptr, clen);
-            pango_layout_get_pixel_extents(layout, NULL, &rect);
-
-            if (rect.width == cellwidth) {
-                /*
-                 * Try extracting more characters, for as long as they
-                 * stay well-behaved.
-                 */
-                while (clen < utflen) {
-                    int oldclen = clen;
-                    clen++;                   /* skip UTF-8 introducer byte */
-                    while (clen < utflen &&
-                           (unsigned char)utfptr[clen] >= 0x80 &&
-                           (unsigned char)utfptr[clen] < 0xC0)
-                        clen++;
-                    n++;
-                    pango_layout_set_text(layout, utfptr, clen);
-                    pango_layout_get_pixel_extents(layout, NULL, &rect);
-                    if (rect.width != n * cellwidth) {
-                        clen = oldclen;
-                        n--;
-                        break;
-                    }
+        } else {
+            /*
+             * Try to amalgamate a contiguous string of characters
+             * with the expected sensible width, for the common case
+             * in which we're using a monospaced font and everything
+             * works as expected.
+             */
+            while (clen < utflen) {
+                int oldclen = clen;
+                clen++;                       /* skip UTF-8 introducer byte */
+                while (clen < utflen &&
+                       (unsigned char)utfptr[clen] >= 0x80 &&
+                       (unsigned char)utfptr[clen] < 0xC0)
+                    clen++;
+                n++;
+                if (pangofont_char_width(layout, pfont,
+                                         string[n-1], utfptr + oldclen,
+                                         clen - oldclen) != cellwidth) {
+                    clen = oldclen;
+                    n--;
+                    break;
                 }
             }
         }
@@ -1953,6 +1990,46 @@ static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x,
     }
 }
 
+/* ----------------------------------------------------------------------
+ * Utility routine used by the code below, and also gtkdlg.c.
+ */
+
+void get_label_text_dimensions(const char *text, int *width, int *height)
+{
+    /*
+     * Determine the dimensions of a piece of text in the standard
+     * font used in GTK interface elements like labels. We do this by
+     * instantiating an actual GtkLabel, and then querying its size.
+     *
+     * But GTK2 and GTK3 require us to query the size completely
+     * differently. I'm sure there ought to be an easier approach than
+     * the way I'm doing this in GTK3, too!
+     */
+    GtkWidget *label = gtk_label_new(text);
+
+#if GTK_CHECK_VERSION(3,0,0)
+    PangoLayout *layout = gtk_label_get_layout(GTK_LABEL(label));
+    PangoRectangle logrect;
+    pango_layout_get_extents(layout, NULL, &logrect);
+    if (width)
+        *width = logrect.width / PANGO_SCALE;
+    if (height)
+        *height = logrect.height / PANGO_SCALE;
+#else
+    GtkRequisition req;
+    gtk_widget_size_request(label, &req);
+    if (width)
+        *width = req.width;
+    if (height)
+        *height = req.height;
+#endif
+
+    g_object_ref_sink(G_OBJECT(label));
+#if GTK_CHECK_VERSION(2,10,0)
+    g_object_unref(label);
+#endif
+}
+
 #if GTK_CHECK_VERSION(2,0,0)
 
 /* ----------------------------------------------------------------------
@@ -2091,6 +2168,8 @@ static int fontinfo_selorder_compare(void *av, void *bv)
     return 0;
 }
 
+static void unifontsel_draw_preview_text(unifontsel_internal *fs);
+
 static void unifontsel_deselect(unifontsel_internal *fs)
 {
     fs->selected = NULL;
@@ -2098,6 +2177,7 @@ static void unifontsel_deselect(unifontsel_internal *fs)
     gtk_list_store_clear(fs->size_model);
     gtk_widget_set_sensitive(fs->u.ok_button, FALSE);
     gtk_widget_set_sensitive(fs->size_entry, FALSE);
+    unifontsel_draw_preview_text(fs);
 }
 
 static void unifontsel_setup_familylist(unifontsel_internal *fs)
@@ -2108,6 +2188,8 @@ static void unifontsel_setup_familylist(unifontsel_internal *fs)
     int currflags = -1;
     fontinfo *info;
 
+    fs->inhibit_response = TRUE;
+
     gtk_list_store_clear(fs->family_model);
     listindex = 0;
 
@@ -2158,6 +2240,8 @@ static void unifontsel_setup_familylist(unifontsel_internal *fs)
      */
     if (fs->selected && fs->selected->familyindex < 0)
        unifontsel_deselect(fs);
+
+    fs->inhibit_response = FALSE;
 }
 
 static void unifontsel_setup_stylelist(unifontsel_internal *fs,
@@ -2303,29 +2387,29 @@ static void unifontsel_draw_preview_text_inner(unifont_drawctx *dctx,
     } else
        font = NULL;
 
-    if (font) {
 #ifdef DRAW_TEXT_GDK
-        if (dctx->type == DRAWTYPE_GDK) {
-            gdk_gc_set_foreground(dctx->u.gdk.gc, &fs->preview_bg);
-            gdk_draw_rectangle(dctx->u.gdk.target, dctx->u.gdk.gc, 1, 0, 0,
-                               fs->preview_width, fs->preview_height);
-            gdk_gc_set_foreground(dctx->u.gdk.gc, &fs->preview_fg);
-        }
+    if (dctx->type == DRAWTYPE_GDK) {
+        gdk_gc_set_foreground(dctx->u.gdk.gc, &fs->preview_bg);
+        gdk_draw_rectangle(dctx->u.gdk.target, dctx->u.gdk.gc, 1, 0, 0,
+                           fs->preview_width, fs->preview_height);
+        gdk_gc_set_foreground(dctx->u.gdk.gc, &fs->preview_fg);
+    }
 #endif
 #ifdef DRAW_TEXT_CAIRO
-        if (dctx->type == DRAWTYPE_CAIRO) {
-            cairo_set_source_rgb(dctx->u.cairo.cr,
-                                 fs->preview_bg.red / 65535.0,
-                                 fs->preview_bg.green / 65535.0,
-                                 fs->preview_bg.blue / 65535.0);
-            cairo_paint(dctx->u.cairo.cr);
-            cairo_set_source_rgb(dctx->u.cairo.cr,
-                                 fs->preview_fg.red / 65535.0,
-                                 fs->preview_fg.green / 65535.0,
-                                 fs->preview_fg.blue / 65535.0);
-        }
+    if (dctx->type == DRAWTYPE_CAIRO) {
+        cairo_set_source_rgb(dctx->u.cairo.cr,
+                             fs->preview_bg.red / 65535.0,
+                             fs->preview_bg.green / 65535.0,
+                             fs->preview_bg.blue / 65535.0);
+        cairo_paint(dctx->u.cairo.cr);
+        cairo_set_source_rgb(dctx->u.cairo.cr,
+                             fs->preview_fg.red / 65535.0,
+                             fs->preview_fg.green / 65535.0,
+                             fs->preview_fg.blue / 65535.0);
+    }
 #endif
 
+    if (font) {
         /*
          * The pangram used here is rather carefully
          * constructed: it contains a sequence of very narrow
@@ -2428,6 +2512,7 @@ static void unifontsel_select_font(unifontsel_internal *fs,
 {
     int index;
     int minval, maxval;
+    gboolean success;
     GtkTreePath *treepath;
     GtkTreeIter iter;
 
@@ -2468,7 +2553,9 @@ static void unifontsel_select_font(unifontsel_internal *fs,
         treepath);
     gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->family_list),
                                 treepath, NULL, FALSE, 0.0, 0.0);
-    gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, treepath);
+    success = gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model),
+                                      &iter, treepath);
+    assert(success);
     gtk_tree_path_free(treepath);
 
     /*
@@ -2842,6 +2929,20 @@ static void alias_resolve(GtkTreeView *treeview, GtkTreePath *path,
     }
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+static gint unifontsel_draw_area(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+    unifontsel_internal *fs = (unifontsel_internal *)data;
+    unifont_drawctx dctx;
+
+    dctx.type = DRAWTYPE_CAIRO;
+    dctx.u.cairo.widget = widget;
+    dctx.u.cairo.cr = cr;
+    unifontsel_draw_preview_text_inner(&dctx, fs);
+
+    return TRUE;
+}
+#else
 static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event,
                                   gpointer data)
 {
@@ -2863,6 +2964,7 @@ static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event,
 
     return TRUE;
 }
+#endif
 
 static gint unifontsel_configure_area(GtkWidget *widget,
                                      GdkEventConfigure *event, gpointer data)
@@ -2911,25 +3013,22 @@ unifontsel *unifontsel_new(const char *wintitle)
     fs->selected = NULL;
 
     {
+        int width, height;
+
        /*
         * Invent some magic size constants.
         */
-       GtkRequisition req;
-       label = gtk_label_new("Quite Long Font Name (Foundry)");
-       gtk_widget_size_request(label, &req);
-       font_width = req.width;
-       lists_height = 14 * req.height;
-       preview_height = 5 * req.height;
-       gtk_label_set_text(GTK_LABEL(label), "Italic Extra Condensed");
-       gtk_widget_size_request(label, &req);
-       style_width = req.width;
-       gtk_label_set_text(GTK_LABEL(label), "48000");
-       gtk_widget_size_request(label, &req);
-       size_width = req.width;
-        g_object_ref_sink(G_OBJECT(label));
-#if GTK_CHECK_VERSION(2,10,0)
-       g_object_unref(label);
-#endif
+       get_label_text_dimensions("Quite Long Font Name (Foundry)",
+                                  &width, &height);
+       font_width = width;
+       lists_height = 14 * height;
+       preview_height = 5 * height;
+
+       get_label_text_dimensions("Italic Extra Condensed", &width, &height);
+       style_width = width;
+
+       get_label_text_dimensions("48000", &width, &height);
+       size_width = width;
     }
 
     /*
@@ -2940,18 +3039,23 @@ unifontsel *unifontsel_new(const char *wintitle)
     fs->u.window = GTK_WINDOW(gtk_dialog_new());
     gtk_window_set_title(fs->u.window, wintitle);
     fs->u.cancel_button = gtk_dialog_add_button
-       (GTK_DIALOG(fs->u.window), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+       (GTK_DIALOG(fs->u.window), STANDARD_CANCEL_LABEL, GTK_RESPONSE_CANCEL);
     fs->u.ok_button = gtk_dialog_add_button
-       (GTK_DIALOG(fs->u.window), GTK_STOCK_OK, GTK_RESPONSE_OK);
+       (GTK_DIALOG(fs->u.window), STANDARD_OK_LABEL, GTK_RESPONSE_OK);
     gtk_widget_grab_default(fs->u.ok_button);
 
     /*
      * Now set up the internal fields, including in particular all
      * the controls that actually allow the user to select fonts.
      */
+#if GTK_CHECK_VERSION(3,0,0)
+    table = gtk_grid_new();
+    gtk_grid_set_column_spacing(GTK_GRID(table), 8);
+#else
     table = gtk_table_new(8, 3, FALSE);
-    gtk_widget_show(table);
     gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+#endif
+    gtk_widget_show(table);
 #if GTK_CHECK_VERSION(2,4,0)
     /* GtkAlignment seems to be the simplest way to put padding round things */
     w = gtk_alignment_new(0, 0, 1, 1);
@@ -2968,7 +3072,12 @@ unifontsel *unifontsel_new(const char *wintitle)
     label = gtk_label_new_with_mnemonic("_Font:");
     gtk_widget_show(label);
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1);
+    g_object_set(G_OBJECT(label), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+#endif
 
     /*
      * The Font list box displays only a string, but additionally
@@ -2998,15 +3107,25 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
     gtk_widget_set_size_request(scroll, font_width, lists_height);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), scroll, 0, 1, 1, 2);
+    g_object_set(G_OBJECT(scroll), "expand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), scroll, 0, 1, 1, 3, GTK_FILL,
                     GTK_EXPAND | GTK_FILL, 0, 0);
+#endif
     fs->family_model = model;
     fs->family_list = w;
 
     label = gtk_label_new_with_mnemonic("_Style:");
     gtk_widget_show(label);
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), label, 1, 0, 1, 1);
+    g_object_set(G_OBJECT(label), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, GTK_FILL, 0, 0, 0);
+#endif
 
     /*
      * The Style list box can contain insensitive elements
@@ -3035,15 +3154,25 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
     gtk_widget_set_size_request(scroll, style_width, lists_height);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), scroll, 1, 1, 1, 2);
+    g_object_set(G_OBJECT(scroll), "expand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), scroll, 1, 2, 1, 3, GTK_FILL,
                     GTK_EXPAND | GTK_FILL, 0, 0);
+#endif
     fs->style_model = model;
     fs->style_list = w;
 
     label = gtk_label_new_with_mnemonic("Si_ze:");
     gtk_widget_show(label);
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), label, 2, 0, 1, 1);
+    g_object_set(G_OBJECT(label), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 0, 0);
+#endif
 
     /*
      * The Size label attaches primarily to a text input box so
@@ -3054,7 +3183,12 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);
     gtk_widget_set_size_request(w, size_width, -1);
     gtk_widget_show(w);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 2, 1, 1, 1);
+    g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 2, 3, 1, 2, GTK_FILL, 0, 0, 0);
+#endif
     g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(size_entry_changed),
                     fs);
 
@@ -3077,8 +3211,13 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_widget_show(scroll);
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), scroll, 2, 2, 1, 1);
+    g_object_set(G_OBJECT(scroll), "expand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), scroll, 2, 3, 2, 3, GTK_FILL,
                     GTK_EXPAND | GTK_FILL, 0, 0);
+#endif
     fs->size_model = model;
     fs->size_list = w;
 
@@ -3094,12 +3233,19 @@ unifontsel *unifontsel_new(const char *wintitle)
     fs->preview_fg.pixel = fs->preview_bg.pixel = 0;
     fs->preview_fg.red = fs->preview_fg.green = fs->preview_fg.blue = 0x0000;
     fs->preview_bg.red = fs->preview_bg.green = fs->preview_bg.blue = 0xFFFF;
+#if !GTK_CHECK_VERSION(3,0,0)
     gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_fg,
                             FALSE, FALSE);
     gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_bg,
                             FALSE, FALSE);
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(fs->preview_area), "draw",
+                     G_CALLBACK(unifontsel_draw_area), fs);
+#else
     g_signal_connect(G_OBJECT(fs->preview_area), "expose_event",
                      G_CALLBACK(unifontsel_expose_area), fs);
+#endif
     g_signal_connect(G_OBJECT(fs->preview_area), "configure_event",
                      G_CALLBACK(unifontsel_configure_area), fs);
     gtk_widget_set_size_request(fs->preview_area, 1, preview_height);
@@ -3120,8 +3266,13 @@ unifontsel *unifontsel_new(const char *wintitle)
     w = gtk_frame_new("Preview of font");
     gtk_container_add(GTK_CONTAINER(w), ww);
     gtk_widget_show(w);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 0, 3, 3, 1);
+    g_object_set(G_OBJECT(w), "expand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 3, 4,
                     GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 8);
+#endif
 
     /*
      * We only provide the checkboxes for client- and server-side
@@ -3138,7 +3289,12 @@ unifontsel *unifontsel_new(const char *wintitle)
                      G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(w);
     fs->filter_buttons[fs->n_filter_buttons++] = w;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 0, 4, 3, 1);
+    g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 4, 5, GTK_FILL, 0, 0, 0);
+#endif
     w = gtk_check_button_new_with_label("Show server-side fonts");
     g_object_set_data(G_OBJECT(w), "user-data",
                       GINT_TO_POINTER(FONTFLAG_SERVERSIDE));
@@ -3146,7 +3302,12 @@ unifontsel *unifontsel_new(const char *wintitle)
                      G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(w);
     fs->filter_buttons[fs->n_filter_buttons++] = w;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 0, 5, 3, 1);
+    g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 5, 6, GTK_FILL, 0, 0, 0);
+#endif
     w = gtk_check_button_new_with_label("Show server-side font aliases");
     g_object_set_data(G_OBJECT(w), "user-data",
                       GINT_TO_POINTER(FONTFLAG_SERVERALIAS));
@@ -3154,8 +3315,13 @@ unifontsel *unifontsel_new(const char *wintitle)
                      G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(w);
     fs->filter_buttons[fs->n_filter_buttons++] = w;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 0, 6, 3, 1);
+    g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 6, 7, GTK_FILL, 0, 0, 0);
 #endif
+#endif /* NOT_X_WINDOWS */
     w = gtk_check_button_new_with_label("Show non-monospaced fonts");
     g_object_set_data(G_OBJECT(w), "user-data",
                       GINT_TO_POINTER(FONTFLAG_NONMONOSPACED));
@@ -3163,7 +3329,12 @@ unifontsel *unifontsel_new(const char *wintitle)
                      G_CALLBACK(unifontsel_button_toggled), fs);
     gtk_widget_show(w);
     fs->filter_buttons[fs->n_filter_buttons++] = w;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_grid_attach(GTK_GRID(table), w, 0, 7, 3, 1);
+    g_object_set(G_OBJECT(w), "hexpand", TRUE, (const char *)NULL);
+#else
     gtk_table_attach(GTK_TABLE(table), w, 0, 3, 7, 8, GTK_FILL, 0, 0, 0);
+#endif
 
     assert(fs->n_filter_buttons <= lenof(fs->filter_buttons));
     fs->filter_flags = FONTFLAG_CLIENTSIDE | FONTFLAG_SERVERSIDE |