]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - unix/gtkfont.c
Merge tag '0.66'
[PuTTY.git] / unix / gtkfont.c
index 82c0dafaf7d942b6c6ee668de30ee7b841d35381..2f7e4a6172e96eb2b87cd6b92777befcd3732716 100644 (file)
 #include <gdk/gdkkeysyms.h>
 #endif
 
+#define MAY_REFER_TO_GTK_IN_HEADERS
+
 #include "putty.h"
 #include "gtkfont.h"
 #include "gtkcompat.h"
+#include "gtkmisc.h"
 #include "tree234.h"
 
 #ifndef NOT_X_WINDOWS
@@ -80,6 +83,9 @@ struct unifont_vtable {
     void (*draw_text)(unifont_drawctx *ctx, unifont *font,
                       int x, int y, const wchar_t *string, int len,
                       int wide, int bold, int cellwidth);
+    void (*draw_combining)(unifont_drawctx *ctx, unifont *font,
+                           int x, int y, const wchar_t *string, int len,
+                           int wide, int bold, int cellwidth);
     void (*enum_fonts)(GtkWidget *widget,
                       fontsel_add_entry callback, void *callback_ctx);
     char *(*canonify_fontname)(GtkWidget *widget, const char *name, int *size,
@@ -104,6 +110,9 @@ static int x11font_has_glyph(unifont *font, wchar_t glyph);
 static void x11font_draw_text(unifont_drawctx *ctx, unifont *font,
                               int x, int y, const wchar_t *string, int len,
                               int wide, int bold, int cellwidth);
+static void x11font_draw_combining(unifont_drawctx *ctx, unifont *font,
+                                   int x, int y, const wchar_t *string,
+                                   int len, int wide, int bold, int cellwidth);
 static unifont *x11font_create(GtkWidget *widget, const char *name,
                               int wide, int bold,
                               int shadowoffset, int shadowalways);
@@ -203,6 +212,7 @@ static const struct unifont_vtable x11font_vtable = {
     x11font_destroy,
     x11font_has_glyph,
     x11font_draw_text,
+    x11font_draw_combining,
     x11font_enum_fonts,
     x11font_canonify_fontname,
     x11font_scale_fontname,
@@ -865,6 +875,20 @@ static void x11font_draw_text(unifont_drawctx *ctx, unifont *font,
     }
 }
 
+static void x11font_draw_combining(unifont_drawctx *ctx, unifont *font,
+                                   int x, int y, const wchar_t *string,
+                                   int len, int wide, int bold, int cellwidth)
+{
+    /*
+     * For server-side fonts, there's no sophisticated system for
+     * combining characters intelligently, so the best we can do is to
+     * overprint them on each other in the obvious way.
+     */
+    int i;
+    for (i = 0; i < len; i++)
+        x11font_draw_text(ctx, font, x, y, string+i, 1, wide, bold, cellwidth);
+}
+
 static void x11font_enum_fonts(GtkWidget *widget,
                               fontsel_add_entry callback, void *callback_ctx)
 {
@@ -1109,6 +1133,10 @@ static int pangofont_has_glyph(unifont *font, wchar_t glyph);
 static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
                                 int x, int y, const wchar_t *string, int len,
                                 int wide, int bold, int cellwidth);
+static void pangofont_draw_combining(unifont_drawctx *ctx, unifont *font,
+                                     int x, int y, const wchar_t *string,
+                                     int len, int wide, int bold,
+                                     int cellwidth);
 static unifont *pangofont_create(GtkWidget *widget, const char *name,
                                 int wide, int bold,
                                 int shadowoffset, int shadowalways);
@@ -1154,6 +1182,7 @@ static const struct unifont_vtable pangofont_vtable = {
     pangofont_destroy,
     pangofont_has_glyph,
     pangofont_draw_text,
+    pangofont_draw_combining,
     pangofont_enum_fonts,
     pangofont_canonify_fontname,
     pangofont_scale_fontname,
@@ -1335,10 +1364,10 @@ static int pangofont_char_width(PangoLayout *layout, struct pangofont *pfont,
     /*
      * 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.
+     * asking Pango for text sizes 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) {
@@ -1351,7 +1380,7 @@ static int pangofont_char_width(PangoLayout *layout, struct pangofont *pfont,
     if (pfont->widthcache[uchr] < 0) {
         PangoRectangle rect;
         pango_layout_set_text(layout, utfchr, utflen);
-        pango_layout_get_pixel_extents(layout, NULL, &rect);
+        pango_layout_get_extents(layout, NULL, &rect);
         pfont->widthcache[uchr] = rect.width;
     }
 
@@ -1381,9 +1410,10 @@ static void pango_cairo_draw_layout(unifont_drawctx *ctx,
 }
 #endif
 
-static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
-                                int x, int y, const wchar_t *string, int len,
-                                int wide, int bold, int cellwidth)
+static void pangofont_draw_internal(unifont_drawctx *ctx, unifont *font,
+                                    int x, int y, const wchar_t *string,
+                                    int len, int wide, int bold, int cellwidth,
+                                    int combining)
 {
     struct pangofont *pfont = (struct pangofont *)font;
     PangoLayout *layout;
@@ -1434,6 +1464,7 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
     utfptr = utfstring;
     while (utflen > 0) {
        int clen, n;
+        int desired = cellwidth * PANGO_SCALE;
 
        /*
         * We want to display every character from this string in
@@ -1459,45 +1490,55 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
         * them to do that.
         */
 
-       /*
-        * Start by extracting a single UTF-8 character from the
-        * string.
-        */
-       clen = 1;
-       while (clen < utflen &&
-              (unsigned char)utfptr[clen] >= 0x80 &&
-              (unsigned char)utfptr[clen] < 0xC0)
-           clen++;
-       n = 1;
-
-        if (is_rtl(string[0]) ||
-            pangofont_char_width(layout, pfont, string[n-1],
-                                 utfptr, clen) != cellwidth) {
+        if (combining) {
             /*
-             * If this character is a right-to-left one, or has an
-             * unusual width, then we must display it on its own.
+             * For a character with combining stuff, we just dump the
+             * whole lot in one go, and expect it to take up just one
+             * character cell.
              */
+            clen = utflen;
+            n = 1;
         } 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.
+             * Start by extracting a single UTF-8 character from the
+             * string.
              */
-            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;
+            clen = 1;
+            while (clen < utflen &&
+                   (unsigned char)utfptr[clen] >= 0x80 &&
+                   (unsigned char)utfptr[clen] < 0xC0)
+                clen++;
+            n = 1;
+
+            if (is_rtl(string[0]) ||
+                pangofont_char_width(layout, pfont, string[n-1],
+                                     utfptr, clen) != desired) {
+                /*
+                 * If this character is a right-to-left one, or has an
+                 * unusual width, then we must display it on its own.
+                 */
+            } 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) != desired) {
+                        clen = oldclen;
+                        n--;
+                        break;
+                    }
                 }
             }
         }
@@ -1524,6 +1565,37 @@ static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
     g_object_unref(layout);
 }
 
+static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font,
+                                int x, int y, const wchar_t *string, int len,
+                                int wide, int bold, int cellwidth)
+{
+    pangofont_draw_internal(ctx, font, x, y, string, len, wide, bold,
+                            cellwidth, FALSE);
+}
+
+static void pangofont_draw_combining(unifont_drawctx *ctx, unifont *font,
+                                     int x, int y, const wchar_t *string,
+                                     int len, int wide, int bold,
+                                     int cellwidth)
+{
+    wchar_t *tmpstring = NULL;
+    if (mk_wcwidth(string[0]) == 0) {
+        /*
+         * If we've been told to draw a sequence of _only_ combining
+         * characters, prefix a space so that they have something to
+         * combine with.
+         */
+        tmpstring = snewn(len+1, wchar_t);
+        memcpy(tmpstring+1, string, len * sizeof(wchar_t));
+        tmpstring[0] = L' ';
+        string = tmpstring;
+        len++;
+    }
+    pangofont_draw_internal(ctx, font, x, y, string, len, wide, bold,
+                            cellwidth, TRUE);
+    sfree(tmpstring);
+}
+
 /*
  * Dummy size value to be used when converting a
  * PangoFontDescription of a scalable font to a string for
@@ -1869,6 +1941,14 @@ void unifont_draw_text(unifont_drawctx *ctx, unifont *font,
     font->vt->draw_text(ctx, font, x, y, string, len, wide, bold, cellwidth);
 }
 
+void unifont_draw_combining(unifont_drawctx *ctx, unifont *font,
+                            int x, int y, const wchar_t *string, int len,
+                            int wide, int bold, int cellwidth)
+{
+    font->vt->draw_combining(ctx, font, x, y, string, len, wide, bold,
+                             cellwidth);
+}
+
 /* ----------------------------------------------------------------------
  * Multiple-font wrapper. This is a type of unifont which encapsulates
  * up to two other unifonts, permitting missing glyphs in the main
@@ -1885,6 +1965,10 @@ void unifont_draw_text(unifont_drawctx *ctx, unifont *font,
 static void multifont_draw_text(unifont_drawctx *ctx, unifont *font,
                                 int x, int y, const wchar_t *string, int len,
                                 int wide, int bold, int cellwidth);
+static void multifont_draw_combining(unifont_drawctx *ctx, unifont *font,
+                                     int x, int y, const wchar_t *string,
+                                     int len, int wide, int bold,
+                                     int cellwidth);
 static void multifont_destroy(unifont *font);
 
 struct multifont {
@@ -1899,6 +1983,7 @@ static const struct unifont_vtable multifont_vtable = {
     multifont_destroy,
     NULL,
     multifont_draw_text,
+    multifont_draw_combining,
     NULL,
     NULL,
     NULL,
@@ -1959,9 +2044,15 @@ static void multifont_destroy(unifont *font)
     sfree(font);
 }
 
-static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x,
+typedef void (*unifont_draw_func_t)(unifont_drawctx *ctx, unifont *font,
+                                    int x, int y, const wchar_t *string,
+                                    int len, int wide, int bold,
+                                    int cellwidth);
+
+static void multifont_draw_main(unifont_drawctx *ctx, unifont *font, int x,
                                 int y, const wchar_t *string, int len,
-                                int wide, int bold, int cellwidth)
+                                int wide, int bold, int cellwidth,
+                                int cellinc, unifont_draw_func_t draw)
 {
     struct multifont *mfont = (struct multifont *)font;
     unifont *f;
@@ -1983,51 +2074,28 @@ static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x,
          */
         f = ok ? mfont->main : mfont->fallback;
         if (f)
-            unifont_draw_text(ctx, f, x, y, string, i, wide, bold, cellwidth);
+            draw(ctx, f, x, y, string, i, wide, bold, cellwidth);
         string += i;
         len -= i;
-        x += i * cellwidth;
+        x += i * cellinc;
     }
 }
 
-/* ----------------------------------------------------------------------
- * Utility routine used by the code below, and also gtkdlg.c.
- */
-
-void get_label_text_dimensions(const char *text, int *width, int *height)
+static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x,
+                                int y, const wchar_t *string, int len,
+                                int wide, int bold, int cellwidth)
 {
-    /*
-     * 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
+    multifont_draw_main(ctx, font, x, y, string, len, wide, bold,
+                        cellwidth, cellwidth, unifont_draw_text);
+}
 
-    g_object_ref_sink(G_OBJECT(label));
-#if GTK_CHECK_VERSION(2,10,0)
-    g_object_unref(label);
-#endif
+static void multifont_draw_combining(unifont_drawctx *ctx, unifont *font,
+                                     int x, int y, const wchar_t *string,
+                                     int len, int wide, int bold,
+                                     int cellwidth)
+{
+    multifont_draw_main(ctx, font, x, y, string, len, wide, bold,
+                        cellwidth, 0, unifont_draw_combining);
 }
 
 #if GTK_CHECK_VERSION(2,0,0)
@@ -2287,7 +2355,7 @@ static void unifontsel_setup_stylelist(unifontsel_internal *fs,
                gtk_list_store_append(fs->style_model, &iter);
                gtk_list_store_set(fs->style_model, &iter,
                                   0, currstyle, 1, minpos, 2, maxpos+1,
-                                  3, TRUE, -1);
+                                  3, TRUE, 4, PANGO_WEIGHT_NORMAL, -1);
                listindex++;
            }
            if (info) {
@@ -2296,7 +2364,7 @@ static void unifontsel_setup_stylelist(unifontsel_internal *fs,
                    gtk_list_store_append(fs->style_model, &iter);
                    gtk_list_store_set(fs->style_model, &iter,
                                       0, info->charset, 1, -1, 2, -1,
-                                      3, FALSE, -1);
+                                      3, FALSE, 4, PANGO_WEIGHT_BOLD, -1);
                    listindex++;
                }
                currcs = info->charset;
@@ -3056,22 +3124,30 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_table_set_col_spacings(GTK_TABLE(table), 8);
 #endif
     gtk_widget_show(table);
-#if GTK_CHECK_VERSION(2,4,0)
+
+#if GTK_CHECK_VERSION(3,0,0)
+    /* GtkAlignment has become deprecated and we use the "margin"
+     * property */
+    w = table;
+    g_object_set(G_OBJECT(w), "margin", 8, (const char *)NULL);
+#elif 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);
     gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8);
     gtk_container_add(GTK_CONTAINER(w), table);
     gtk_widget_show(w);
 #else
+    /* In GTK < 2.4, even that isn't available */
     w = table;
 #endif
+
     gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area
                                (GTK_DIALOG(fs->u.window))),
                       w, TRUE, TRUE, 0);
 
     label = gtk_label_new_with_mnemonic("_Font:");
     gtk_widget_show(label);
-    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+    align_label_left(GTK_LABEL(label));
 #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);
@@ -3119,7 +3195,7 @@ unifontsel *unifontsel_new(const char *wintitle)
 
     label = gtk_label_new_with_mnemonic("_Style:");
     gtk_widget_show(label);
-    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+    align_label_left(GTK_LABEL(label));
 #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);
@@ -3128,19 +3204,21 @@ unifontsel *unifontsel_new(const char *wintitle)
 #endif
 
     /*
-     * The Style list box can contain insensitive elements
-     * (character set headings for server-side fonts), so we add
-     * an extra column to the list store to hold that information.
+     * The Style list box can contain insensitive elements (character
+     * set headings for server-side fonts), so we add an extra column
+     * to the list store to hold that information. Also, since GTK3 at
+     * least doesn't seem to display insensitive elements differently
+     * by default, we add a further column to change their style.
      */
-    model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT,
-                              G_TYPE_BOOLEAN);
+    model = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT,
+                              G_TYPE_BOOLEAN, G_TYPE_INT);
     w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);
     gtk_label_set_mnemonic_widget(GTK_LABEL(label), w);
     gtk_widget_show(w);
     column = gtk_tree_view_column_new_with_attributes
        ("Style", gtk_cell_renderer_text_new(),
-        "text", 0, "sensitive", 3, (char *)NULL);
+        "text", 0, "sensitive", 3, "weight", 4, (char *)NULL);
     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
     gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
     g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),
@@ -3166,7 +3244,7 @@ unifontsel *unifontsel_new(const char *wintitle)
 
     label = gtk_label_new_with_mnemonic("Si_ze:");
     gtk_widget_show(label);
-    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+    align_label_left(GTK_LABEL(label));
 #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);
@@ -3254,7 +3332,12 @@ unifontsel *unifontsel_new(const char *wintitle)
     w = gtk_frame_new(NULL);
     gtk_container_add(GTK_CONTAINER(w), ww);
     gtk_widget_show(w);
-#if GTK_CHECK_VERSION(2,4,0)
+
+#if GTK_CHECK_VERSION(3,0,0)
+    /* GtkAlignment has become deprecated and we use the "margin"
+     * property */
+    g_object_set(G_OBJECT(w), "margin", 8, (const char *)NULL);
+#elif GTK_CHECK_VERSION(2,4,0)
     ww = w;
     /* GtkAlignment seems to be the simplest way to put padding round things */
     w = gtk_alignment_new(0, 0, 1, 1);
@@ -3262,6 +3345,7 @@ unifontsel *unifontsel_new(const char *wintitle)
     gtk_container_add(GTK_CONTAINER(w), ww);
     gtk_widget_show(w);
 #endif
+
     ww = w;
     w = gtk_frame_new("Preview of font");
     gtk_container_add(GTK_CONTAINER(w), ww);