mirror of
https://github.com/irssi/irssi.git
synced 2026-08-09 11:50:10 +02:00
make lines reformattable
- completely removed the old textbuffer representation ( https://github.com/shabble/irssi-docs/wiki/Notes-256-Colour#textbuffer-encoding ) - textbuffer-formats is an extra module, so if we unhook the signals it should go back to the "old way" of storing pre-rendered tex - design uses cache, original formats and list of arguments
This commit is contained in:
parent
94ae7f9cd3
commit
f95fc81130
21 changed files with 789 additions and 587 deletions
|
|
@ -26,176 +26,59 @@
|
|||
#include <irssi/src/core/utf8.h>
|
||||
#include <irssi/src/core/iregex.h>
|
||||
|
||||
#include <irssi/src/fe-text/textbuffer-formats.h>
|
||||
#include <irssi/src/fe-text/textbuffer.h>
|
||||
|
||||
#define TEXT_CHUNK_USABLE_SIZE (LINE_TEXT_CHUNK_SIZE-2-(int)sizeof(char*))
|
||||
|
||||
TEXT_BUFFER_REC *textbuffer_create(void)
|
||||
TEXT_BUFFER_REC *textbuffer_create(WINDOW_REC *window)
|
||||
{
|
||||
TEXT_BUFFER_REC *buffer;
|
||||
|
||||
buffer = g_slice_new0(TEXT_BUFFER_REC);
|
||||
buffer->window = window;
|
||||
buffer->last_eol = TRUE;
|
||||
buffer->last_fg = -1;
|
||||
buffer->last_bg = -1;
|
||||
return buffer;
|
||||
buffer->cur_text = g_string_sized_new(TEXT_CHUNK_USABLE_SIZE);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void textbuffer_destroy(TEXT_BUFFER_REC *buffer)
|
||||
{
|
||||
GSList *tmp;
|
||||
|
||||
g_return_if_fail(buffer != NULL);
|
||||
|
||||
textbuffer_remove_all_lines(buffer);
|
||||
g_slice_free(TEXT_BUFFER_REC, buffer);
|
||||
}
|
||||
|
||||
static TEXT_CHUNK_REC *text_chunk_find(TEXT_BUFFER_REC *buffer,
|
||||
const unsigned char *data)
|
||||
{
|
||||
GSList *tmp;
|
||||
|
||||
for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next) {
|
||||
TEXT_CHUNK_REC *rec = tmp->data;
|
||||
|
||||
if (data >= rec->buffer &&
|
||||
data < rec->buffer+sizeof(rec->buffer))
|
||||
return rec;
|
||||
g_string_free(buffer->cur_text, TRUE);
|
||||
for (tmp = buffer->cur_info; tmp != NULL; tmp = tmp->next) {
|
||||
LINE_INFO_REC *info = buffer->cur_info->data;
|
||||
textbuffer_format_rec_free(info->format);
|
||||
g_free(info->text);
|
||||
g_free(info);
|
||||
}
|
||||
g_slist_free(buffer->cur_info);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define mark_temp_eol(chunk) G_STMT_START { \
|
||||
(chunk)->buffer[(chunk)->pos] = 0; \
|
||||
(chunk)->buffer[(chunk)->pos+1] = LINE_CMD_EOL; \
|
||||
} G_STMT_END
|
||||
|
||||
static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer)
|
||||
{
|
||||
TEXT_CHUNK_REC *rec;
|
||||
unsigned char *buf, *ptr, **pptr;
|
||||
|
||||
rec = g_slice_new(TEXT_CHUNK_REC);
|
||||
rec->pos = 0;
|
||||
rec->refcount = 0;
|
||||
|
||||
if (buffer->cur_line != NULL && buffer->cur_line->text != NULL) {
|
||||
/* create a link to new block from the old block */
|
||||
buf = buffer->cur_text->buffer + buffer->cur_text->pos;
|
||||
*buf++ = 0; *buf++ = (char) LINE_CMD_CONTINUE;
|
||||
|
||||
/* we want to store pointer to beginning of the new text
|
||||
block to char* buffer. this probably isn't ANSI-C
|
||||
compatible, and trying this without the pptr variable
|
||||
breaks at least NetBSD/Alpha, so don't go "optimize"
|
||||
it :) */
|
||||
ptr = rec->buffer; pptr = &ptr;
|
||||
memcpy(buf, pptr, sizeof(unsigned char *));
|
||||
} else {
|
||||
/* just to be safe */
|
||||
mark_temp_eol(rec);
|
||||
}
|
||||
|
||||
buffer->cur_text = rec;
|
||||
buffer->text_chunks = g_slist_append(buffer->text_chunks, rec);
|
||||
return rec;
|
||||
}
|
||||
|
||||
static void text_chunk_destroy(TEXT_BUFFER_REC *buffer, TEXT_CHUNK_REC *chunk)
|
||||
{
|
||||
buffer->text_chunks = g_slist_remove(buffer->text_chunks, chunk);
|
||||
g_slice_free(TEXT_CHUNK_REC, chunk);
|
||||
}
|
||||
|
||||
static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line)
|
||||
{
|
||||
TEXT_CHUNK_REC *chunk;
|
||||
const unsigned char *text;
|
||||
unsigned char cmd, *tmp = NULL;
|
||||
|
||||
for (text = line->text;; text++) {
|
||||
if (*text != '\0')
|
||||
continue;
|
||||
|
||||
text++;
|
||||
cmd = *text;
|
||||
if (cmd == LINE_CMD_CONTINUE || cmd == LINE_CMD_EOL) {
|
||||
if (cmd == LINE_CMD_CONTINUE)
|
||||
memcpy(&tmp, text+1, sizeof(char *));
|
||||
|
||||
/* free the previous block */
|
||||
chunk = text_chunk_find(buffer, text);
|
||||
if (--chunk->refcount == 0) {
|
||||
if (buffer->cur_text == chunk)
|
||||
chunk->pos = 0;
|
||||
else
|
||||
text_chunk_destroy(buffer, chunk);
|
||||
}
|
||||
|
||||
if (cmd == LINE_CMD_EOL)
|
||||
break;
|
||||
|
||||
text = tmp-1;
|
||||
}
|
||||
}
|
||||
buffer->window = NULL;
|
||||
g_slice_free(TEXT_BUFFER_REC, buffer);
|
||||
}
|
||||
|
||||
static void text_chunk_append(TEXT_BUFFER_REC *buffer,
|
||||
const unsigned char *data, int len)
|
||||
{
|
||||
TEXT_CHUNK_REC *chunk;
|
||||
int left;
|
||||
int i;
|
||||
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
chunk = buffer->cur_text;
|
||||
while (chunk->pos + len >= TEXT_CHUNK_USABLE_SIZE) {
|
||||
left = TEXT_CHUNK_USABLE_SIZE - chunk->pos;
|
||||
|
||||
/* don't split utf-8 character. (assume we can split non-utf8 anywhere.) */
|
||||
if (left < len && !is_utf8_leading(data[left])) {
|
||||
int i;
|
||||
for (i = 1; i < 4 && left >= i; i++)
|
||||
if (is_utf8_leading(data[left - i])) {
|
||||
left -= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 5; i > 0; --i) {
|
||||
if (left >= i && data[left-i] == 0) {
|
||||
left -= i; /* don't split the commands */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(chunk->buffer + chunk->pos, data, left);
|
||||
chunk->pos += left;
|
||||
|
||||
chunk = text_chunk_create(buffer);
|
||||
chunk->refcount++;
|
||||
len -= left; data += left;
|
||||
}
|
||||
|
||||
memcpy(chunk->buffer + chunk->pos, data, len);
|
||||
chunk->pos += len;
|
||||
|
||||
mark_temp_eol(chunk);
|
||||
/* g_string_append_len(buffer->cur_text, (const char *)data, len); */
|
||||
g_string_append(buffer->cur_text, (const char *) data);
|
||||
}
|
||||
|
||||
static LINE_REC *textbuffer_line_create(TEXT_BUFFER_REC *buffer)
|
||||
{
|
||||
LINE_REC *rec;
|
||||
|
||||
if (buffer->cur_text == NULL)
|
||||
text_chunk_create(buffer);
|
||||
|
||||
rec = g_slice_new0(LINE_REC);
|
||||
rec->text = buffer->cur_text->buffer + buffer->cur_text->pos;
|
||||
|
||||
buffer->cur_text->refcount++;
|
||||
return rec;
|
||||
}
|
||||
|
||||
|
|
@ -241,103 +124,18 @@ int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef TERM_TRUECOLOR
|
||||
static void format_24bit_line_color(unsigned char *out, int *pos, int bg, unsigned int color)
|
||||
{
|
||||
unsigned char rgb[] = { color >> 16, color >> 8, color };
|
||||
unsigned char x = bg ? 0x1 : 0;
|
||||
unsigned int i;
|
||||
out[(*pos)++] = LINE_COLOR_24;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (rgb[i] > 0x20)
|
||||
out[(*pos)++] = rgb[i];
|
||||
else {
|
||||
out[(*pos)++] = 0x20 + rgb[i];
|
||||
x |= 0x10 << i;
|
||||
}
|
||||
}
|
||||
out[(*pos)++] = 0x20 + x;
|
||||
}
|
||||
#endif
|
||||
|
||||
void textbuffer_line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line,
|
||||
int fg, int bg, int flags)
|
||||
{
|
||||
unsigned char data[22];
|
||||
int pos;
|
||||
GString *out = g_string_new(NULL);
|
||||
format_gui_flags(out, &buffer->last_fg, &buffer->last_bg, &buffer->last_flags, fg, bg,
|
||||
flags);
|
||||
|
||||
pos = 0;
|
||||
if (fg != buffer->last_fg
|
||||
|| (flags & GUI_PRINT_FLAG_COLOR_24_FG) != (buffer->last_flags & GUI_PRINT_FLAG_COLOR_24_FG)) {
|
||||
buffer->last_fg = fg;
|
||||
data[pos++] = 0;
|
||||
#ifdef TERM_TRUECOLOR
|
||||
if (flags & GUI_PRINT_FLAG_COLOR_24_FG)
|
||||
format_24bit_line_color(data, &pos, 0, fg);
|
||||
else
|
||||
#endif
|
||||
if (fg < 0)
|
||||
data[pos++] = LINE_COLOR_DEFAULT;
|
||||
else if (fg < 16)
|
||||
data[pos++] = fg == 0 ? LINE_CMD_COLOR0 : fg;
|
||||
else if (fg < 256) {
|
||||
data[pos++] = LINE_COLOR_EXT;
|
||||
data[pos++] = fg;
|
||||
}
|
||||
if (*(out->str) != '\0') {
|
||||
*line =
|
||||
textbuffer_insert(buffer, *line, (unsigned char *) out->str, out->len, NULL);
|
||||
}
|
||||
if (bg != buffer->last_bg
|
||||
|| (flags & GUI_PRINT_FLAG_COLOR_24_BG) != (buffer->last_flags & GUI_PRINT_FLAG_COLOR_24_BG)) {
|
||||
buffer->last_bg = bg;
|
||||
data[pos++] = 0;
|
||||
#ifdef TERM_TRUECOLOR
|
||||
if (flags & GUI_PRINT_FLAG_COLOR_24_BG)
|
||||
format_24bit_line_color(data, &pos, 1, bg);
|
||||
else
|
||||
#endif
|
||||
if (bg < 0)
|
||||
data[pos++] = LINE_COLOR_BG | LINE_COLOR_DEFAULT;
|
||||
else if (bg < 16)
|
||||
data[pos++] = LINE_COLOR_BG | bg;
|
||||
else if (bg < 256) {
|
||||
data[pos++] = LINE_COLOR_EXT_BG;
|
||||
data[pos++] = bg;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & GUI_PRINT_FLAG_UNDERLINE) != (buffer->last_flags & GUI_PRINT_FLAG_UNDERLINE)) {
|
||||
data[pos++] = 0;
|
||||
data[pos++] = LINE_CMD_UNDERLINE;
|
||||
}
|
||||
if ((flags & GUI_PRINT_FLAG_REVERSE) != (buffer->last_flags & GUI_PRINT_FLAG_REVERSE)) {
|
||||
data[pos++] = 0;
|
||||
data[pos++] = LINE_CMD_REVERSE;
|
||||
}
|
||||
if ((flags & GUI_PRINT_FLAG_BLINK) != (buffer->last_flags & GUI_PRINT_FLAG_BLINK)) {
|
||||
data[pos++] = 0;
|
||||
data[pos++] = LINE_CMD_BLINK;
|
||||
}
|
||||
if ((flags & GUI_PRINT_FLAG_BOLD) != (buffer->last_flags & GUI_PRINT_FLAG_BOLD)) {
|
||||
data[pos++] = 0;
|
||||
data[pos++] = LINE_CMD_BOLD;
|
||||
}
|
||||
if ((flags & GUI_PRINT_FLAG_ITALIC) != (buffer->last_flags & GUI_PRINT_FLAG_ITALIC)) {
|
||||
data[pos++] = 0;
|
||||
data[pos++] = LINE_CMD_ITALIC;
|
||||
}
|
||||
if ((flags & GUI_PRINT_FLAG_MONOSPACE) != (buffer->last_flags & GUI_PRINT_FLAG_MONOSPACE)) {
|
||||
data[pos++] = 0;
|
||||
data[pos++] = LINE_CMD_MONOSPACE;
|
||||
}
|
||||
if (flags & GUI_PRINT_FLAG_INDENT) {
|
||||
data[pos++] = 0;
|
||||
data[pos++] = LINE_CMD_INDENT;
|
||||
}
|
||||
|
||||
if (pos > 0) {
|
||||
*line = textbuffer_insert(buffer, *line, data, pos, NULL);
|
||||
}
|
||||
|
||||
buffer->last_flags = flags;
|
||||
g_string_free(out, TRUE);
|
||||
}
|
||||
|
||||
LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
|
||||
|
|
@ -368,6 +166,11 @@ LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after,
|
|||
data[len-2] == 0 && data[len-1] == LINE_CMD_EOL;
|
||||
|
||||
if (buffer->last_eol) {
|
||||
if (!line->info.format) {
|
||||
line->info.text = g_strdup(buffer->cur_text->str);
|
||||
g_string_truncate(buffer->cur_text, 0);
|
||||
}
|
||||
|
||||
buffer->last_fg = -1;
|
||||
buffer->last_bg = -1;
|
||||
buffer->last_flags = 0;
|
||||
|
|
@ -395,145 +198,50 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line)
|
|||
line->prev = line->next = NULL;
|
||||
|
||||
buffer->lines_count--;
|
||||
text_chunk_line_free(buffer, line);
|
||||
g_free(line->info.text);
|
||||
textbuffer_format_rec_free(line->info.format);
|
||||
g_slice_free(LINE_REC, line);
|
||||
}
|
||||
|
||||
/* Removes all lines from buffer */
|
||||
void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer)
|
||||
{
|
||||
GSList *tmp;
|
||||
LINE_REC *line;
|
||||
|
||||
g_return_if_fail(buffer != NULL);
|
||||
|
||||
for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next)
|
||||
g_slice_free(TEXT_CHUNK_REC, tmp->data);
|
||||
g_slist_free(buffer->text_chunks);
|
||||
buffer->text_chunks = NULL;
|
||||
|
||||
while (buffer->first_line != NULL) {
|
||||
line = buffer->first_line->next;
|
||||
g_free(buffer->first_line->info.text);
|
||||
textbuffer_format_rec_free(buffer->first_line->info.format);
|
||||
g_slice_free(LINE_REC, buffer->first_line);
|
||||
buffer->first_line = line;
|
||||
}
|
||||
buffer->lines_count = 0;
|
||||
|
||||
buffer->cur_line = NULL;
|
||||
buffer->cur_text = NULL;
|
||||
g_string_truncate(buffer->cur_text, 0);
|
||||
|
||||
buffer->last_eol = TRUE;
|
||||
}
|
||||
|
||||
static void set_color(GString *str, int cmd)
|
||||
void textbuffer_line2text(TEXT_BUFFER_REC *buffer, LINE_REC *line, int coloring, GString *str)
|
||||
{
|
||||
int color = -1;
|
||||
|
||||
if (!(cmd & LINE_COLOR_DEFAULT))
|
||||
color = (cmd & 0x0f)+'0';
|
||||
|
||||
if ((cmd & LINE_COLOR_BG) == 0) {
|
||||
/* change foreground color */
|
||||
g_string_append_printf(str, "\004%c%c",
|
||||
color, FORMAT_COLOR_NOCHANGE);
|
||||
} else {
|
||||
/* change background color */
|
||||
g_string_append_printf(str, "\004%c%c",
|
||||
FORMAT_COLOR_NOCHANGE, color);
|
||||
}
|
||||
}
|
||||
|
||||
void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
|
||||
{
|
||||
unsigned char cmd, *ptr, *tmp;
|
||||
char *ptr, *tmp;
|
||||
|
||||
g_return_if_fail(line != NULL);
|
||||
g_return_if_fail(str != NULL);
|
||||
|
||||
g_string_truncate(str, 0);
|
||||
|
||||
for (ptr = line->text;;) {
|
||||
if (*ptr != 0) {
|
||||
g_string_append_c(str, (char) *ptr);
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
ptr++;
|
||||
cmd = *ptr;
|
||||
ptr++;
|
||||
|
||||
if (cmd == LINE_CMD_EOL) {
|
||||
/* end of line */
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd == LINE_CMD_CONTINUE) {
|
||||
/* line continues in another address.. */
|
||||
memcpy(&tmp, ptr, sizeof(unsigned char *));
|
||||
ptr = tmp;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!coloring) {
|
||||
/* no colors, skip coloring commands */
|
||||
if (cmd == LINE_COLOR_EXT || cmd == LINE_COLOR_EXT_BG)
|
||||
ptr++;
|
||||
#ifdef TERM_TRUECOLOR
|
||||
else if (cmd == LINE_COLOR_24)
|
||||
ptr+=4;
|
||||
#endif
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((cmd & LINE_CMD_EOL) == 0) {
|
||||
/* set color */
|
||||
set_color(str, cmd);
|
||||
} else switch (cmd) {
|
||||
case LINE_CMD_UNDERLINE:
|
||||
g_string_append_c(str, 31);
|
||||
break;
|
||||
case LINE_CMD_REVERSE:
|
||||
g_string_append_c(str, 22);
|
||||
break;
|
||||
case LINE_CMD_BLINK:
|
||||
g_string_append_printf(str, "\004%c",
|
||||
FORMAT_STYLE_BLINK);
|
||||
break;
|
||||
case LINE_CMD_BOLD:
|
||||
g_string_append_printf(str, "\004%c",
|
||||
FORMAT_STYLE_BOLD);
|
||||
break;
|
||||
case LINE_CMD_ITALIC:
|
||||
g_string_append_printf(str, "\004%c",
|
||||
FORMAT_STYLE_ITALIC);
|
||||
break;
|
||||
case LINE_CMD_MONOSPACE:
|
||||
g_string_append_printf(str, "\004%c",
|
||||
FORMAT_STYLE_MONOSPACE);
|
||||
break;
|
||||
case LINE_CMD_COLOR0:
|
||||
g_string_append_printf(str, "\004%c%c",
|
||||
'0', FORMAT_COLOR_NOCHANGE);
|
||||
break;
|
||||
case LINE_CMD_INDENT:
|
||||
g_string_append_printf(str, "\004%c",
|
||||
FORMAT_STYLE_INDENT);
|
||||
break;
|
||||
case LINE_COLOR_EXT:
|
||||
format_ext_color(str, 0, *ptr++);
|
||||
break;
|
||||
case LINE_COLOR_EXT_BG:
|
||||
format_ext_color(str, 1, *ptr++);
|
||||
break;
|
||||
#ifdef TERM_TRUECOLOR
|
||||
case LINE_COLOR_24:
|
||||
g_string_append_printf(str, "\004%c", FORMAT_COLOR_24);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
ptr = textbuffer_line_get_text(buffer, line);
|
||||
if (coloring == 0) {
|
||||
tmp = ptr;
|
||||
ptr = strip_codes(tmp);
|
||||
g_free(tmp);
|
||||
}
|
||||
g_string_append(str, ptr);
|
||||
g_free(ptr);
|
||||
}
|
||||
|
||||
GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
|
||||
|
|
@ -575,7 +283,7 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
|
|||
(line->info.level & nolevel) == 0;
|
||||
|
||||
if (*text != '\0') {
|
||||
textbuffer_line2text(line, FALSE, str);
|
||||
textbuffer_line2text(buffer, line, FALSE, str);
|
||||
|
||||
if (line_matched) {
|
||||
line_matched = regexp ?
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue