Fix double- and zero-wide characters

It's not perfect but seems to work well enough.
This commit is contained in:
Přemysl Eric Janouch 2013-06-02 00:01:23 +02:00
parent d3b966a93f
commit 1cc91a4b17
3 changed files with 78 additions and 17 deletions

View File

@ -17,6 +17,10 @@ set (project_VERSION "${project_VERSION_MAJOR}")
set (project_VERSION "${project_VERSION}.${project_VERSION_MINOR}") set (project_VERSION "${project_VERSION}.${project_VERSION_MINOR}")
set (project_VERSION "${project_VERSION}.${project_VERSION_PATCH}") set (project_VERSION "${project_VERSION}.${project_VERSION_PATCH}")
# Configuration
include (CheckFunctionExists)
CHECK_FUNCTION_EXISTS ("wcwidth" HAVE_WCWIDTH)
# Dependecies # Dependecies
find_package (PkgConfig REQUIRED) find_package (PkgConfig REQUIRED)
pkg_check_modules (dependencies REQUIRED ncursesw glib-2.0 gio-2.0 pango) pkg_check_modules (dependencies REQUIRED ncursesw glib-2.0 gio-2.0 pango)

View File

@ -8,5 +8,7 @@
#define GETTEXT_PACKAGE PROJECT_NAME #define GETTEXT_PACKAGE PROJECT_NAME
#define GETTEXT_DIRNAME "${CMAKE_INSTALL_PREFIX}/share/locale" #define GETTEXT_DIRNAME "${CMAKE_INSTALL_PREFIX}/share/locale"
#cmakedefine HAVE_WCWIDTH
#endif /* ! CONFIG_H */ #endif /* ! CONFIG_H */

View File

@ -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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -98,6 +99,18 @@ struct curses_event
MEVENT mouse; 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 static gboolean
is_character_in_locale (wchar_t c) 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); ssize_t wide_len = wcslen (wide_str);
wchar_t padding = L' ', error = L'?', ellipsis = L''; 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) 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) if (is_character_in_locale (ellipsis))
wide_str[n - 1] = ellipsis;
else if (n >= 3)
{ {
wide_str[n - 1] = L'.'; if (cols + wcwidth (ellipsis) > n)
wide_str[n - 2] = L'.'; cols -= wcwidth (wide_str[len - 1]);
wide_str[n - 3] = L'.'; 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; 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), if (setcchar (&cch, &wide_str[i], A_NORMAL, 0, NULL) == OK)
A_NORMAL, 0, NULL) == ERR)
setcchar (&cch, &error, A_NORMAL, 0, NULL);
add_wch (&cch); 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); g_free (wide_str);
return n; return n;
} }
@ -399,7 +449,12 @@ app_redraw_top (Application *self)
app_add_utf8_string (self, input_utf8, COLS - indent); app_add_utf8_string (self, input_utf8, COLS - indent);
g_free (input_utf8); 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 (); refresh ();
} }