This commit is contained in:
Xavier G 2016-05-03 19:32:16 +00:00
commit 66ea96978f
11 changed files with 249 additions and 96 deletions

View file

@ -44,6 +44,8 @@ libcore_a_SOURCES = \
settings.c \
signals.c \
special-vars.c \
utf8.c \
wcwidth.c \
write-buffer.c
structure_headers = \

View file

@ -25,10 +25,7 @@
#include "settings.h"
#include "servers.h"
#include "misc.h"
#define ALIGN_RIGHT 0x01
#define ALIGN_CUT 0x02
#define ALIGN_PAD 0x04
#include "utf8.h"
#define isvarchar(c) \
(i_isalnum(c) || (c) == '_')
@ -316,31 +313,79 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad)
}
/* return the aligned text */
static char *get_alignment(const char *text, int align, int flags, char pad)
char *get_alignment(const char *text, int align, int flags, char pad)
{
GString *str;
char *ret;
gchar *ret;
gchar *ret_p;
uint i;
/* Terminology: width is an amount of columns, length is an amount of
* characters.
*/
uint start_width, start_length;
uint cut_width, cut_length;
uint final_width, final_length;
uint pad_count;
uint delta;
g_return_val_if_fail(text != NULL, NULL);
str = g_string_new(text);
/* abort if non-valid UTF-8 */
if (!g_utf8_validate(text, -1, NULL))
return NULL;
/* cut */
if ((flags & ALIGN_CUT) && align > 0 && str->len > align)
g_string_truncate(str, align);
/* how many columns and chars do we have in the first place? */
start_width = get_utf8_string_width(text, 1 /* skip UTF-8 validation */);
start_length = g_utf8_strlen(text, 1024);
/* how many columns and chars will we have after the cut? */
cut_width = start_width;
cut_length = start_length;
delta = 0;
if ((flags & ALIGN_CUT) && align > 0 && start_width > align) {
cut_width = align;
cut_length = get_utf8_chars_for_width(text,
align,
1 /* skip UTF-8 validation */,
&delta);
/* At this point, we know that we have to take "cut_length" chars from
* "text" and append "delta" padding chars to reach "align" columns.
*/
}
/* how many columns will we have after the pad? */
final_width = cut_width;
final_length = cut_length;
pad_count = delta;
if ((flags & ALIGN_PAD) && align > cut_width) {
final_width = align;
pad_count += (final_width - cut_width);
final_length += pad_count;
}
/* add pad characters */
if (flags & ALIGN_PAD) {
while (str->len < align) {
if (flags & ALIGN_RIGHT)
g_string_prepend_c(str, pad);
else
g_string_append_c(str, pad);
/* allocate 4 bytes for each character we will have in the end */
ret = g_malloc((4 * final_length) + 1);
ret_p = ret;
/* left pad to align right, if necessary */
if (pad_count && (flags & ALIGN_RIGHT)) {
for (i = 0; i < pad_count; i++) {
*ret_p = pad;
ret_p ++;
}
}
ret = str->str;
g_string_free(str, FALSE);
/* copy the original string (either n characters or the whole of it) */
g_utf8_strncpy(ret_p, text, cut_length);
/* sadly, at this point, we have no idea where g_utf8_strncpy put the trailing \0... */
ret_p += strlen(ret_p);
/* right pad to align left, if necessary */
if (pad_count && !(flags & ALIGN_RIGHT)) {
for (i = 0; i < pad_count; i++) {
*ret_p = pad;
ret_p ++;
}
*ret_p = '\0';
}
return ret;
}

View file

@ -9,9 +9,17 @@
#define PARSE_FLAG_ESCAPE_THEME 0x08 /* if any arguments/variables contain { or } chars, escape them with % */
#define PARSE_FLAG_ONLY_ARGS 0x10 /* expand only arguments ($0 $1 etc.) but no other $variables */
#define ALIGN_RIGHT 0x01
#define ALIGN_CUT 0x02
#define ALIGN_PAD 0x04
typedef char* (*SPECIAL_HISTORY_FUNC)
(const char *text, void *item, int *free_ret);
/* Cut and/or pad text so it takes exactly "align" characters on the screen */
char *get_alignment(const char *text, int align, int flags, char pad);
/* Parse and expand text after '$' character. return value has to be
g_free()'d if `free_ret' is TRUE. */
char *parse_special(char **cmd, SERVER_REC *server, void *item,

124
src/core/utf8.c Normal file
View file

@ -0,0 +1,124 @@
/* utf8.c - Operations on UTF-8 strings.
*
* Copyright (C) 2002 Timo Sirainen
*
* Based on GLib code by
*
* Copyright (C) 1999 Tom Tromey
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <glib.h>
#include "module.h"
/*
* Return the width (number of columns when displayed) of the character pointed
* by c.
*/
int get_utf8_char_width(const gchar *c) {
gunichar uc;
int char_width;
uc = g_utf8_get_char(c);
char_width = g_unichar_isprint(uc) ? (1 + g_unichar_iswide(uc)) : 0;
return char_width;
}
/*
* Return the number of columns taken by the s string, assuming its end is
* marked with a '\0' character and assuming it is a UTF-8 (multibyte) string.
* If s is NULL, this function returns -1.
* By default, this function takes care to validate s by calling
* g_utf8_validate(s, -1, NULL). If this check is deemed unnecessary, passing a
* non-zero value as skip_validation will skip that step. If the validation
* fails, this function returns -1. Otherwise, it will strive to provide a
* value as close as possible to what is expected.
*/
int get_utf8_string_width(const gchar *s, int skip_validation) {
const gchar *c;
int str_width;
/* Ensure s is non-NULL: */
if (!s) {
return -1;
}
/* Validate the string, unless required otherwise: */
if (!skip_validation) {
if (!g_utf8_validate(s, -1, NULL)) {
/* Another possibility here would be to return strlen(s). */
return -1;
}
}
/* Iterate over characters to determine the width: */
str_width = 0;
for (c = s; *c; c = g_utf8_next_char(c)) {
str_width += get_utf8_char_width(c);
}
/* Note: there probably are some Unicode subtleties (Fitzpatrick
* modifiers?) that make the above implementation somewhat naive, but we
* have to start somewhere.
*/
return str_width;
}
/* Return the amount of characters from s it takes to reach n columns, or -1 if
* s is NULL.
*/
int get_utf8_chars_for_width(const gchar *s, unsigned int n, int skip_validation, unsigned int *delta) {
const gchar *c;
int str_width, char_width, char_count;
/* Ensure s is non-NULL: */
if (!s) {
return -1;
}
/* Handle the dummy case where n is 0: */
if (!n) {
return 0;
}
/* Validate the string, unless required otherwise: */
if (!skip_validation) {
if (!g_utf8_validate(s, -1, NULL)) {
/* Another possibility here would be to return strlen(s). */
return -1;
}
}
/* Iterate over characters until we reach n: */
char_count = 0;
str_width = 0;
for (c = s; *c; c = g_utf8_next_char(c)) {
char_width = get_utf8_char_width(c);
if (str_width + char_width > n) {
/* We are about to exceed n, stop here. */
break;
}
++ char_count;
str_width += char_width;
}
/* At this point, we know that char_count characters reach str_width
* columns, which is less than or equal to n. */
/* Optionally provide the delta between str_width and n */
if (delta) {
*delta = n - str_width;
}
return char_count;
}

View file

@ -8,9 +8,16 @@
#define is_big5_hi(hi) (0x81 <= (hi) && (hi) <= 0xFE)
#define is_big5(hi,lo) (is_big5_hi(hi) && is_big5_lo(lo))
typedef guint32 unichar;
/* Returns width for character (0-2). */
int mk_wcwidth(unichar c);
/* Return the number of columns occupied by a given string. */
int get_utf8_char_width(const gchar *);
int get_utf8_string_width(const gchar *, int);
int get_utf8_chars_for_width(const gchar *, unsigned int, int, unsigned int *);
#define unichar_isprint(c) (((c) & ~0x80) >= 32)
#define is_utf8_leading(c) (((c) & 0xc0) != 0x80)

View file

@ -60,6 +60,7 @@
*/
#include "module.h"
#include "utf8.h"
struct interval {
int first;

View file

@ -24,8 +24,6 @@ libfe_common_core_a_SOURCES = \
fe-queries.c \
fe-server.c \
fe-settings.c \
utf8.c \
wcwidth.c \
formats.c \
hilight-text.c \
keyboard.c \
@ -62,6 +60,3 @@ pkginc_fe_common_core_HEADERS = \
window-items.h \
windows-layout.h \
fe-windows.h
noinst_HEADERS = \
utf8.h

View file

@ -26,6 +26,8 @@
#include "levels.h"
#include "misc.h"
#include "settings.h"
#include "special-vars.h"
#include "utf8.h"
#include "chat-protocols.h"
#include "chatnets.h"
@ -323,40 +325,40 @@ static void cmd_channel_remove(const char *data)
static int get_nick_length(void *data)
{
return strlen(((NICK_REC *) data)->nick);
return get_utf8_string_width(((NICK_REC *) data)->nick, 1024);
}
static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
{
WINDOW_REC *window;
WINDOW_REC *window;
TEXT_DEST_REC dest;
GString *str;
GSList *tmp;
char *format, *stripped, *prefix_format;
char *linebuf, nickmode[2] = { 0, 0 };
char *format, *stripped, *prefix_format;
char *aligned_nick, nickmode[2] = { 0, 0 };
int *columns, cols, rows, last_col_rows, col, row, max_width;
int item_extra, linebuf_size, formatnum;
int item_extra, formatnum;
window = window_find_closest(channel->server, channel->visible_name,
MSGLEVEL_CLIENTCRAP);
max_width = window->width;
MSGLEVEL_CLIENTCRAP);
max_width = window->width;
/* get the length of item extra stuff ("[ ] ") */
/* get the length of item extra stuff ("[ ] ") */
format = format_get_text(MODULE_NAME, NULL,
channel->server, channel->visible_name,
TXT_NAMES_NICK, " ", "");
channel->server, channel->visible_name,
TXT_NAMES_NICK, " ", "");
stripped = strip_codes(format);
item_extra = strlen(stripped);
g_free(stripped);
g_free(stripped);
g_free(format);
if (settings_get_int("names_max_width") > 0 &&
settings_get_int("names_max_width") < max_width)
max_width = settings_get_int("names_max_width");
/* remove width of the timestamp from max_width */
/* remove width of the timestamp from max_width */
format_create_dest(&dest, channel->server, channel->visible_name,
MSGLEVEL_CLIENTCRAP, NULL);
MSGLEVEL_CLIENTCRAP, NULL);
format = format_get_line_start(current_theme, &dest, time(NULL));
if (format != NULL) {
stripped = strip_codes(format);
@ -365,11 +367,11 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
g_free(format);
}
/* remove width of the prefix from max_width */
/* remove width of the prefix from max_width */
prefix_format = format_get_text(MODULE_NAME, NULL,
channel->server, channel->visible_name,
TXT_NAMES_PREFIX,
channel->visible_name);
channel->server, channel->visible_name,
TXT_NAMES_PREFIX,
channel->visible_name);
if (prefix_format != NULL) {
stripped = strip_codes(prefix_format);
max_width -= strlen(stripped);
@ -384,19 +386,18 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
/* calculate columns */
cols = get_max_column_count(nicklist, get_nick_length, max_width,
settings_get_int("names_max_columns"),
item_extra, 3, &columns, &rows);
settings_get_int("names_max_columns"),
item_extra, 3, &columns, &rows);
nicklist = columns_sort_list(nicklist, rows);
/* rows in last column */
/* rows in last column */
last_col_rows = rows-(cols*rows-g_slist_length(nicklist));
if (last_col_rows == 0)
last_col_rows = rows;
last_col_rows = rows;
str = g_string_new(prefix_format);
linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size);
col = 0; row = 0;
col = 0; row = 0;
for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
NICK_REC *rec = tmp->data;
@ -405,48 +406,44 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
else
nickmode[0] = ' ';
if (linebuf_size < columns[col]-item_extra+1) {
linebuf_size = (columns[col]-item_extra+1)*2;
linebuf = g_realloc(linebuf, linebuf_size);
}
memset(linebuf, ' ', columns[col]-item_extra);
linebuf[columns[col]-item_extra] = '\0';
memcpy(linebuf, rec->nick, strlen(rec->nick));
aligned_nick = get_alignment(rec->nick,
columns[col]-item_extra,
ALIGN_PAD, ' ');
formatnum = rec->op ? TXT_NAMES_NICK_OP :
rec->halfop ? TXT_NAMES_NICK_HALFOP :
rec->voice ? TXT_NAMES_NICK_VOICE :
TXT_NAMES_NICK;
formatnum = rec->op ? TXT_NAMES_NICK_OP :
rec->halfop ? TXT_NAMES_NICK_HALFOP :
rec->voice ? TXT_NAMES_NICK_VOICE :
TXT_NAMES_NICK;
format = format_get_text(MODULE_NAME, NULL,
channel->server,
channel->visible_name,
formatnum, nickmode, linebuf);
channel->server,
channel->visible_name,
formatnum, nickmode, aligned_nick);
g_string_append(str, format);
g_free(aligned_nick);
g_free(format);
if (++col == cols) {
printtext(channel->server, channel->visible_name,
MSGLEVEL_CLIENTCRAP, "%s", str->str);
MSGLEVEL_CLIENTCRAP, "%s", str->str);
g_string_truncate(str, 0);
if (prefix_format != NULL)
g_string_assign(str, prefix_format);
g_string_assign(str, prefix_format);
col = 0; row++;
if (row == last_col_rows)
cols--;
cols--;
}
}
if (str->len > strlen(prefix_format)) {
printtext(channel->server, channel->visible_name,
MSGLEVEL_CLIENTCRAP, "%s", str->str);
MSGLEVEL_CLIENTCRAP, "%s", str->str);
}
g_slist_free(nicklist);
g_string_free(str, TRUE);
g_free_not_null(columns);
g_free_not_null(prefix_format);
g_free(linebuf);
}
void fe_channels_nicklist(CHANNEL_REC *channel, int flags)

View file

@ -2,7 +2,7 @@
#define MODULE_NAME "fe-common/core"
typedef guint32 unichar;
#include "utf8.h"
typedef struct {
time_t time;
char *nick;

View file

@ -1,26 +0,0 @@
/* utf8.c - Operations on UTF-8 strings.
*
* Copyright (C) 2002 Timo Sirainen
*
* Based on GLib code by
*
* Copyright (C) 1999 Tom Tromey
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"

View file

@ -27,7 +27,7 @@ typedef struct _TERM_WINDOW TERM_WINDOW;
#define TERM_TYPE_UTF8 1
#define TERM_TYPE_BIG5 2
typedef guint32 unichar;
#include "utf8.h"
extern TERM_WINDOW *root_window;
extern int term_width, term_height;