From 64cb43b3b2a347aeadb45459b3a954cc26bffbd9 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 6 Jan 2014 13:34:30 -0500 Subject: [PATCH] tui: add nmt_newt_text_width() We were using g_utf8_strlen() to measure strings for layout, but that counts combining marks too, and also fails to deal with "fullwidth" (ie, double-width) CJK characters. Add a new utility function to do a better job of this (based on code from vte), and use it everywhere. --- tui/newt/nmt-newt-textbox.c | 8 ++++---- tui/newt/nmt-newt-utils.c | 31 +++++++++++++++++++++++++++++++ tui/newt/nmt-newt-utils.h | 2 ++ tui/nmt-connect-connection-list.c | 4 ++-- tui/nmt-route-table.c | 18 +++++++++--------- tui/nmtui-connect.c | 10 +++++----- tui/nmtui.c | 2 +- 7 files changed, 54 insertions(+), 21 deletions(-) diff --git a/tui/newt/nmt-newt-textbox.c b/tui/newt/nmt-newt-textbox.c index 888bde28f..67b8b247d 100644 --- a/tui/newt/nmt-newt-textbox.c +++ b/tui/newt/nmt-newt-textbox.c @@ -95,7 +95,7 @@ nmt_newt_textbox_set_text (NmtNewtTextbox *textbox, { NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox); char **lines; - int i, len; + int i, width; if (!text) text = ""; @@ -108,9 +108,9 @@ nmt_newt_textbox_set_text (NmtNewtTextbox *textbox, priv->width = priv->height = 0; lines = g_strsplit (priv->text, "\n", -1); for (i = 0; lines[i]; i++) { - len = g_utf8_strlen (lines[i], -1); - if (len > priv->width) - priv->width = len; + width = nmt_newt_text_width (lines[i]); + if (width > priv->width) + priv->width = width; } g_free (lines); priv->height = MIN (i, 1); diff --git a/tui/newt/nmt-newt-utils.c b/tui/newt/nmt-newt-utils.c index d7e0eeaee..028c2438c 100644 --- a/tui/newt/nmt-newt-utils.c +++ b/tui/newt/nmt-newt-utils.c @@ -259,6 +259,37 @@ nmt_newt_locale_from_utf8 (const char *str_utf8) return str_lc; } +/** + * nmt_newt_text_width + * @str: a UTF-8 string + * + * Computes the width (in terminal columns) of @str. + * + * Returns: the width of @str + */ +int +nmt_newt_text_width (const char *str) +{ + int width; + gunichar ch; + + for (width = 0; *str; str = g_utf8_next_char (str)) { + ch = g_utf8_get_char (str); + + /* Based on _vte_iso2022_unichar_width */ + if (G_LIKELY (ch < 0x80)) + width += 1; + else if (G_UNLIKELY (g_unichar_iszerowidth (ch))) + width += 0; + else if (G_UNLIKELY (g_unichar_iswide (ch))) + width += 2; + else + width += 1; + } + + return width; +} + /** * nmt_newt_edit_string: * @data: data to edit diff --git a/tui/newt/nmt-newt-utils.h b/tui/newt/nmt-newt-utils.h index c71a6b77f..3b37868a0 100644 --- a/tui/newt/nmt-newt-utils.h +++ b/tui/newt/nmt-newt-utils.h @@ -37,6 +37,8 @@ typedef enum { char *nmt_newt_locale_to_utf8 (const char *str_lc); char *nmt_newt_locale_from_utf8 (const char *str_utf8); +int nmt_newt_text_width (const char *str); + void nmt_newt_message_dialog (const char *message, ...); int nmt_newt_choice_dialog (const char *button1, diff --git a/tui/nmt-connect-connection-list.c b/tui/nmt-connect-connection-list.c index 726eaab0b..cb3d3bac9 100644 --- a/tui/nmt-connect-connection-list.c +++ b/tui/nmt-connect-connection-list.c @@ -436,7 +436,7 @@ nmt_connect_connection_list_rebuild (NmtConnectConnectionList *list) for (citer = nmtdev->conns; citer; citer = citer->next) { nmtconn = citer->data; - max_width = MAX (max_width, g_utf8_strlen (nmtconn->name, -1)); + max_width = MAX (max_width, nmt_newt_text_width (nmtconn->name)); } } @@ -477,7 +477,7 @@ nmt_connect_connection_list_rebuild (NmtConnectConnectionList *list) row = g_strdup_printf ("%c %s%-*s%s", active_col, nmtconn->name, - (int)(max_width - g_utf8_strlen (nmtconn->name, -1)), "", + (int)(max_width - nmt_newt_text_width (nmtconn->name)), "", strength_col); nmt_newt_listbox_append (listbox, row, nmtconn); diff --git a/tui/nmt-route-table.c b/tui/nmt-route-table.c index 8f9cb24df..75b2b67ae 100644 --- a/tui/nmt-route-table.c +++ b/tui/nmt-route-table.c @@ -211,13 +211,13 @@ nmt_route_table_init (NmtRouteTable *table) NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); NmtNewtWidget *header, *empty; NmtNewtWidget *dest_prefix_label, *next_hop_label, *metric_label; - int dest_prefix_len, next_hop_len, metric_len; + int dest_prefix_width, next_hop_width, metric_width; char *text; header = nmt_newt_grid_new (); text = g_strdup_printf ("%s/%s", _("Destination"), _("Prefix")); - dest_prefix_len = g_utf8_strlen (text, -1); + dest_prefix_width = nmt_newt_text_width (text); dest_prefix_label = g_object_new (NMT_TYPE_NEWT_LABEL, "text", text, "style", NMT_NEWT_LABEL_PLAIN, @@ -230,7 +230,7 @@ nmt_route_table_init (NmtRouteTable *table) "text", text, "style", NMT_NEWT_LABEL_PLAIN, NULL); - next_hop_len = g_utf8_strlen (text, -1); + next_hop_width = nmt_newt_text_width (text); nmt_newt_grid_add (NMT_NEWT_GRID (header), next_hop_label, 1, 0); text = _("Metric"); @@ -238,18 +238,18 @@ nmt_route_table_init (NmtRouteTable *table) "text", text, "style", NMT_NEWT_LABEL_PLAIN, NULL); - metric_len = g_utf8_strlen (text, -1); + metric_width = nmt_newt_text_width (text); nmt_newt_grid_add (NMT_NEWT_GRID (header), metric_label, 2, 0); - priv->ip_entry_width = MAX (20, MAX (dest_prefix_len, next_hop_len)); - priv->metric_entry_width = MAX (7, metric_len); + priv->ip_entry_width = MAX (20, MAX (dest_prefix_width, next_hop_width)); + priv->metric_entry_width = MAX (7, metric_width); nmt_newt_widget_set_padding (dest_prefix_label, - 0, 0, priv->ip_entry_width - dest_prefix_len, 0); + 0, 0, priv->ip_entry_width - dest_prefix_width, 0); nmt_newt_widget_set_padding (next_hop_label, - 2, 0, priv->ip_entry_width - next_hop_len, 0); + 2, 0, priv->ip_entry_width - next_hop_width, 0); nmt_newt_widget_set_padding (metric_label, - 2, 0, priv->metric_entry_width - metric_len, 0); + 2, 0, priv->metric_entry_width - metric_width, 0); nmt_newt_grid_add (NMT_NEWT_GRID (table), header, 0, 0); diff --git a/tui/nmtui-connect.c b/tui/nmtui-connect.c index 9849048c2..5facc0725 100644 --- a/tui/nmtui-connect.c +++ b/tui/nmtui-connect.c @@ -193,15 +193,15 @@ listbox_active_changed (GObject *object, gboolean has_selection; if (G_UNLIKELY (activate == NULL)) { - int activate_len, deactivate_len; + int activate_width, deactivate_width; activate = _("Activate"); - activate_len = g_utf8_strlen (activate, -1); + activate_width = nmt_newt_text_width (activate); deactivate = _("Deactivate"); - deactivate_len = g_utf8_strlen (deactivate, -1); + deactivate_width = nmt_newt_text_width (deactivate); - activate_padding = MAX (0, deactivate_len - activate_len); - deactivate_padding = MAX (0, activate_len - deactivate_len); + activate_padding = MAX (0, deactivate_width - activate_width); + deactivate_padding = MAX (0, activate_width - deactivate_width); } has_selection = nmt_connect_connection_list_get_selection (list, NULL, NULL, NULL, &ac); diff --git a/tui/nmtui.c b/tui/nmtui.c index b1c9c87a1..85c76459d 100644 --- a/tui/nmtui.c +++ b/tui/nmtui.c @@ -164,7 +164,7 @@ usage (void) g_printerr ("%s: nmtui\n", usage); for (i = 0; i < num_subprograms; i++) { g_printerr ("%*s nmtui %s [%s]\n", - (int) g_utf8_strlen (usage, -1), " ", + nmt_newt_text_width (usage), " ", subprograms[i].name, _(subprograms[i].arg)); }