diff options
| author | Přemysl Janouch <p.janouch@gmail.com> | 2016-09-26 14:14:01 +0200 | 
|---|---|---|
| committer | Přemysl Janouch <p.janouch@gmail.com> | 2016-09-26 14:14:01 +0200 | 
| commit | a87aca9c76c57770a69740b7e1ac9a56eb73f22c (patch) | |
| tree | dd1768ab791edbb3f6b44ceec732b107052e2032 /src | |
| parent | 5f9cd0885c473e32660a6ea9534311d559e08a50 (diff) | |
| download | tdv-a87aca9c76c57770a69740b7e1ac9a56eb73f22c.tar.gz tdv-a87aca9c76c57770a69740b7e1ac9a56eb73f22c.tar.xz tdv-a87aca9c76c57770a69740b7e1ac9a56eb73f22c.zip | |
Refactor app_add_utf8_string()
Diffstat (limited to 'src')
| -rw-r--r-- | src/sdtui.c | 185 | 
1 files changed, 133 insertions, 52 deletions
| diff --git a/src/sdtui.c b/src/sdtui.c index fff326e..d41058b 100644 --- a/src/sdtui.c +++ b/src/sdtui.c @@ -344,77 +344,158 @@ app_is_character_in_locale (Application *self, gunichar ch)  	return TRUE;  } -/// Write the given UTF-8 string padded with spaces. -/// @param[in] n  The number of characters to write, or -1 for the whole string. -/// @param[in] attrs  Text attributes for the text, without padding. -///                   To change the attributes of all output, use attrset(). -/// @return The number of characters output. -static gsize -app_add_utf8_string (Application *self, const gchar *str, int attrs, int n) +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +// Necessary abstraction to simplify aligned, formatted character output + +typedef struct row_char                 RowChar; +typedef struct row_buffer               RowBuffer; + +struct row_char  { -	if (!n) -		return 0; +	gunichar c;                         ///< Unicode codepoint +	chtype attrs;                       ///< Special attributes +	int width;                          ///< How many cells this takes +}; +struct row_buffer +{ +	Application *app;                   ///< Reference to Application +	GArray *chars;                      ///< Characters +	int total_width;                    ///< Total width of all characters +}; + +static void +row_buffer_init (RowBuffer *self, Application *app) +{ +	self->app = app; +	self->chars = g_array_new (FALSE, TRUE, sizeof (RowChar)); +	self->total_width = 0; +} + +#define row_buffer_free(self) g_array_unref ((self)->chars) + +/// Replace invalid chars and push all codepoints to the array w/ attributes. +static void +row_buffer_append (RowBuffer *self, const gchar *str, chtype attrs) +{  	glong ucs4_len;  	gunichar *ucs4 = g_utf8_to_ucs4_fast (str, -1, &ucs4_len); - -	// Replace invalid chars and compute how many characters fit in the limit -	gint cols, i; -	for (cols = i = 0; i < ucs4_len; i++) +	for (glong i = 0; i < ucs4_len; i++)  	{ -		if (!app_is_character_in_locale (self, ucs4[i])) -			ucs4[i] = '?'; +		// XXX: this is very crude as it disrespects combining marks +		gunichar c = +			app_is_character_in_locale (self->app, ucs4[i]) ? ucs4[i] : '?'; +		struct row_char rc = { ucs4[i], attrs, unichar_width (c) }; +		g_array_append_val (self->chars, rc); +		self->total_width += rc.width; +	} +	g_free (ucs4); +} -		gint width = unichar_width (ucs4[i]); -		if (n >= 0 && cols + width > n) -			break; -		cols += width; +/// Pop as many codepoints as needed to free up "space" character cells. +/// Given the suffix nature of combining marks, this should work pretty fine. +static gint +row_buffer_pop_cells (RowBuffer *self, gint space) +{ +	int made = 0; +	while (self->chars->len && made < space) +	{ +		guint last = self->chars->len - 1; +		made += g_array_index (self->chars, RowChar, last).width; +		g_array_remove_index (self->chars, last);  	} +	self->total_width -= made; +	return made; +} -	if (n < 0) -		n = cols; +static void +row_buffer_ellipsis (RowBuffer *self, int target, chtype attrs) +{ +	row_buffer_pop_cells (self, self->total_width - target); -	// Append ellipsis if the whole string didn't fit  	gunichar ellipsis = L'…'; -	gint ellipsis_width = unichar_width (ellipsis); - -	gint len = i; -	if (len != ucs4_len) +	if (app_is_character_in_locale (self->app, ellipsis))  	{ -		if (app_is_character_in_locale (self, ellipsis)) -		{ -			if (cols + ellipsis_width > n) -				cols -= unichar_width (ucs4[len - 1]); -			else -				len++; +		if (self->total_width >= target) +			row_buffer_pop_cells (self, 1); +		if (self->total_width + 1 <= target) +			row_buffer_append (self, "…", attrs); +	} +	else if (target >= 3) +	{ +		if (self->total_width >= target) +			row_buffer_pop_cells (self, 3); +		if (self->total_width + 3 <= target) +			row_buffer_append (self, "...", attrs); +	} +} -			ucs4[len - 1] = ellipsis; -			cols += ellipsis_width; -		} -		else if (n >= 3 && len >= 3) +static void +row_buffer_print (RowBuffer *self, gunichar *ucs4, size_t len, chtype attrs) +{ +	gsize locale_str_len; +	guchar *str = (guchar *) g_convert_with_iconv +		((const gchar *) ucs4, len * sizeof *ucs4, +		self->app->ucs4_to_locale, NULL, &locale_str_len, NULL); +	g_return_if_fail (str != NULL); + +	for (gsize i = 0; i < locale_str_len; i++) +		addch (str[i] | attrs); +	g_free (str); +} + +static void +row_buffer_flush (RowBuffer *self) +{ +	if (!self->chars->len) +		return; + +	gunichar ucs4[self->chars->len]; +	for (guint i = 0; i < self->chars->len; i++) +		ucs4[i] = g_array_index (self->chars, RowChar, i).c; + +	guint mark = 0; +	for (guint i = 1; i < self->chars->len; i++) +	{ +		chtype attrs = g_array_index (self->chars, RowChar, i - 1).attrs; +		if (attrs != g_array_index (self->chars, RowChar, i).attrs)  		{ -			// With zero-width characters this overflows -			// It's just a fallback anyway -			cols -= unichar_width (ucs4[len - 1]); ucs4[len - 1] = '.'; -			cols -= unichar_width (ucs4[len - 2]); ucs4[len - 2] = '.'; -			cols -= unichar_width (ucs4[len - 3]); ucs4[len - 3] = '.'; -			cols += 3; +			row_buffer_print (self, ucs4 + mark, i - mark, attrs); +			mark = i;  		}  	} +	row_buffer_print (self, ucs4 + mark, self->chars->len - mark, +		g_array_index (self->chars, RowChar, self->chars->len - 1).attrs); +} -	guchar *locale_str; -	gsize locale_str_len; -	locale_str = (guchar *) g_convert_with_iconv ((const gchar *) ucs4, -		len * sizeof *ucs4, self->ucs4_to_locale, NULL, &locale_str_len, NULL); -	g_return_val_if_fail (locale_str != NULL, 0); +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -	for (gsize i = 0; i < locale_str_len; i++) -		addch (locale_str[i] | attrs); -	while (cols++ < n) +/// Write the given UTF-8 string padded with spaces. +/// @param[in] n  The number of characters to write, or -1 for the whole string. +/// @param[in] attrs  Text attributes for the text, without padding. +///                   To change the attributes of all output, use attrset(). +/// @return The number of characters output. +static gsize +app_add_utf8_string (Application *self, const gchar *str, chtype attrs, int n) +{ +	if (!n) +		return 0; + +	RowBuffer buf; +	row_buffer_init (&buf, self); +	row_buffer_append (&buf, str, attrs); + +	if (n < 0) +		n = buf.total_width; +	if (buf.total_width > n) +		row_buffer_ellipsis (&buf, n, attrs); + +	row_buffer_flush (&buf); +	for (int i = buf.total_width; i < n; i++)  		addch (' '); -	g_free (locale_str); -	g_free (ucs4); +	row_buffer_free (&buf);  	return n;  } | 
