diff options
author | Přemysl Janouch <p.janouch@gmail.com> | 2013-06-02 00:01:23 +0200 |
---|---|---|
committer | Přemysl Janouch <p.janouch@gmail.com> | 2013-06-02 00:04:43 +0200 |
commit | 1cc91a4b179b13c19d3318e64896088fefde6c3c (patch) | |
tree | 21dbf2d46a4a1e62df8b472773fdd2141f4d0553 /src/sdtui.c | |
parent | d3b966a93f176ce669f228b0d0dad0b9682c3e11 (diff) | |
download | tdv-1cc91a4b179b13c19d3318e64896088fefde6c3c.tar.gz tdv-1cc91a4b179b13c19d3318e64896088fefde6c3c.tar.xz tdv-1cc91a4b179b13c19d3318e64896088fefde6c3c.zip |
Fix double- and zero-wide characters
It's not perfect but seems to work well enough.
Diffstat (limited to 'src/sdtui.c')
-rw-r--r-- | src/sdtui.c | 87 |
1 files changed, 71 insertions, 16 deletions
diff --git a/src/sdtui.c b/src/sdtui.c index 502aec3..f875389 100644 --- a/src/sdtui.c +++ b/src/sdtui.c @@ -18,7 +18,8 @@ * */ -#define _XOPEN_SOURCE_EXTENDED /**< Yes, we want ncursesw. */ +#define _XOPEN_SOURCE 500 //!< wcwidth +#define _XOPEN_SOURCE_EXTENDED //!< Yes, we want ncursesw. #include <stdio.h> #include <stdlib.h> @@ -98,6 +99,18 @@ struct curses_event MEVENT mouse; }; +static size_t +unichar_width (gunichar ch) +{ + if (g_unichar_iszerowidth (ch)) + return 0; + return 1 + g_unichar_iswide (ch); +} + +#ifndef HAVE_WCWIDTH +#define wcwidth(x) 1 +#endif // ! HAVE_WCWIDTH + static gboolean is_character_in_locale (wchar_t c) { @@ -353,31 +366,68 @@ app_add_utf8_string (Application *self, const gchar *str, int n) ssize_t wide_len = wcslen (wide_str); wchar_t padding = L' ', error = L'?', ellipsis = L'…'; + if (!n) + return 0; + + // Compute how many wide characters fit in the limit + gint cols, i; + for (cols = i = 0; i < wide_len; i++) + { + if (!is_character_in_locale (wide_str[i])) + wide_str[i] = error; + + gint width = wcwidth (wide_str[i]); + if (n >= 0 && cols + width > n) + break; + cols += width; + } + if (n < 0) - n = wide_len; + n = cols; - if (wide_len > n) + // Append ellipsis if the whole string didn't fit + gint len = i; + if (len != wide_len) { - if (is_character_in_locale (ellipsis) && n > 0) - wide_str[n - 1] = ellipsis; - else if (n >= 3) + if (is_character_in_locale (ellipsis)) { - wide_str[n - 1] = L'.'; - wide_str[n - 2] = L'.'; - wide_str[n - 3] = L'.'; + if (cols + wcwidth (ellipsis) > n) + cols -= wcwidth (wide_str[len - 1]); + else + len++; + + wide_str[len - 1] = ellipsis; + cols += wcwidth (ellipsis); + } + else if (n >= 3 && len >= 3) + { + // With zero-width characters this overflows + // It's just a fallback anyway + cols -= wcwidth (wide_str[len - 1]); + cols -= wcwidth (wide_str[len - 2]); + cols -= wcwidth (wide_str[len - 3]); + cols += 3; + + wide_str[len - 1] = L'.'; + wide_str[len - 2] = L'.'; + wide_str[len - 3] = L'.'; } } - gint i; cchar_t cch; - for (i = 0; i < n; i++) + for (i = 0; i < len; i++) { - if (setcchar (&cch, (i < wide_len ? &wide_str[i] : &padding), - A_NORMAL, 0, NULL) == ERR) - setcchar (&cch, &error, A_NORMAL, 0, NULL); - add_wch (&cch); + if (setcchar (&cch, &wide_str[i], A_NORMAL, 0, NULL) == OK) + add_wch (&cch); + else + // This shouldn't happen + cols -= wcwidth (wide_str[i]); } + setcchar (&cch, &padding, A_NORMAL, 0, NULL); + while (cols++ < n) + add_wch (&cch); + g_free (wide_str); return n; } @@ -399,7 +449,12 @@ app_redraw_top (Application *self) app_add_utf8_string (self, input_utf8, COLS - indent); g_free (input_utf8); - move (0, indent + self->input_pos); + guint offset, i; + for (offset = i = 0; i < self->input_pos; i++) + // This may be inconsistent with the output of app_add_utf8_string() + offset += unichar_width (g_array_index (self->input, gunichar, i)); + + move (0, indent + offset); refresh (); } |