forked from PsychoticNinja/irssi
256 colour patch is cleaned up and the remaining cases are made work, this includes especially Theme support, which was not implemented before. Changes not related to colours were reverted again, making a review of the two patches against master easier to follow. As a byproduct of the Hex-colour code parser, the 24bit colours are also implemented. Actually using them in the terminal is guarded by a compile time switch (as well as a run time switch), as it breaks the existing colour protocol and requires additional storage. To make a seamless usage, down-conversion is provided for 8 and 16 colours. Diverging from Tom's approach, the colour protocol is reverted back to the original one. Unfortunately, the changes required in the Theme engine will break the API. For more details, please refer to the patch documentation at either http://irssi-docs.wikispaces.com/Notes-256-Colour or https://github.com/shabble/irssi-docs/wiki/Notes-256-Colour
1369 lines
35 KiB
C
1369 lines
35 KiB
C
/*
|
|
textbuffer-view.c : Text buffer handling
|
|
|
|
Copyright (C) 1999-2001 Timo Sirainen
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 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.
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "TextBufferView"
|
|
|
|
#include "module.h"
|
|
#include "textbuffer-view.h"
|
|
#include "utf8.h"
|
|
|
|
typedef struct {
|
|
char *name;
|
|
LINE_REC *line;
|
|
} BOOKMARK_REC;
|
|
|
|
/* how often to scan line cache for lines not accessed for a while (ms) */
|
|
#define LINE_CACHE_CHECK_TIME (5*60*1000)
|
|
/* how long to keep line cache in memory (seconds) */
|
|
#define LINE_CACHE_KEEP_TIME (10*60)
|
|
|
|
static int linecache_tag;
|
|
static GSList *views;
|
|
|
|
#define view_is_bottom(view) \
|
|
((view)->ypos >= -1 && (view)->ypos < (view)->height)
|
|
|
|
#define view_get_linecount(view, line) \
|
|
textbuffer_view_get_line_cache(view, line)->count
|
|
|
|
static GSList *textbuffer_get_views(TEXT_BUFFER_REC *buffer)
|
|
{
|
|
GSList *tmp, *list;
|
|
|
|
for (tmp = views; tmp != NULL; tmp = tmp->next) {
|
|
TEXT_BUFFER_VIEW_REC *view = tmp->data;
|
|
|
|
if (view->buffer == buffer) {
|
|
list = g_slist_copy(view->siblings);
|
|
return g_slist_prepend(list, view);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static TEXT_BUFFER_CACHE_REC *
|
|
textbuffer_cache_get(GSList *views, int width)
|
|
{
|
|
TEXT_BUFFER_CACHE_REC *cache;
|
|
|
|
/* check if there's existing cache with correct width */
|
|
while (views != NULL) {
|
|
TEXT_BUFFER_VIEW_REC *view = views->data;
|
|
|
|
if (view->width == width) {
|
|
view->cache->refcount++;
|
|
return view->cache;
|
|
}
|
|
views = views->next;
|
|
}
|
|
|
|
/* create new cache */
|
|
cache = g_new0(TEXT_BUFFER_CACHE_REC, 1);
|
|
cache->refcount = 1;
|
|
cache->width = width;
|
|
cache->line_cache = g_hash_table_new((GHashFunc) g_direct_hash,
|
|
(GCompareFunc) g_direct_equal);
|
|
return cache;
|
|
}
|
|
|
|
static int line_cache_destroy(void *key, LINE_CACHE_REC *cache)
|
|
{
|
|
g_free(cache);
|
|
return TRUE;
|
|
}
|
|
|
|
static void textbuffer_cache_destroy(TEXT_BUFFER_CACHE_REC *cache)
|
|
{
|
|
g_hash_table_foreach(cache->line_cache,
|
|
(GHFunc) line_cache_destroy, NULL);
|
|
g_hash_table_destroy(cache->line_cache);
|
|
g_free(cache);
|
|
}
|
|
|
|
static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *cache)
|
|
{
|
|
if (--cache->refcount == 0)
|
|
textbuffer_cache_destroy(cache);
|
|
}
|
|
|
|
#define FGATTR (ATTR_NOCOLORS | ATTR_RESETFG | FG_MASK | ATTR_FGCOLOR24)
|
|
#define BGATTR (ATTR_NOCOLORS | ATTR_RESETBG | BG_MASK | ATTR_BGCOLOR24)
|
|
|
|
static void update_cmd_color(unsigned char cmd, int *color)
|
|
{
|
|
if ((cmd & 0x80) == 0) {
|
|
if (cmd & LINE_COLOR_BG) {
|
|
/* set background color */
|
|
*color &= FGATTR;
|
|
*color &= ~ATTR_FGCOLOR24;
|
|
if ((cmd & LINE_COLOR_DEFAULT) == 0)
|
|
*color |= (cmd & 0x0f) << BG_SHIFT;
|
|
else {
|
|
*color = (*color & FGATTR) | ATTR_RESETBG;
|
|
}
|
|
} else {
|
|
/* set foreground color */
|
|
*color &= BGATTR;
|
|
*color &= ~ATTR_BGCOLOR24;
|
|
if ((cmd & LINE_COLOR_DEFAULT) == 0)
|
|
*color |= cmd & 0x0f;
|
|
else {
|
|
*color = (*color & BGATTR) | ATTR_RESETFG;
|
|
}
|
|
}
|
|
} else switch (cmd) {
|
|
case LINE_CMD_UNDERLINE:
|
|
*color ^= ATTR_UNDERLINE;
|
|
break;
|
|
case LINE_CMD_REVERSE:
|
|
*color ^= ATTR_REVERSE;
|
|
break;
|
|
case LINE_CMD_BLINK:
|
|
*color ^= ATTR_BLINK;
|
|
break;
|
|
case LINE_CMD_BOLD:
|
|
*color ^= ATTR_BOLD;
|
|
break;
|
|
case LINE_CMD_COLOR0:
|
|
*color &= BGATTR;
|
|
*color &= ~ATTR_FGCOLOR24;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef TERM_TRUECOLOR
|
|
static void unformat_24bit_line_color(const unsigned char **ptr, int off, int *flags, int *fg, int *bg)
|
|
{
|
|
unsigned int color;
|
|
unsigned char rgbx[4];
|
|
unsigned int i;
|
|
for (i = 0; i < 4; ++i) {
|
|
rgbx[i] = (*ptr)[i + off];
|
|
}
|
|
rgbx[3] -= 0x20;
|
|
*ptr += 4;
|
|
for (i = 0; i < 3; ++i) {
|
|
if (rgbx[3] & (0x10 << i))
|
|
rgbx[i] -= 0x20;
|
|
}
|
|
color = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
|
|
if (rgbx[3] & 0x1) {
|
|
*flags = (*flags & FGATTR) | ATTR_BGCOLOR24;
|
|
*bg = color;
|
|
}
|
|
else {
|
|
*flags = (*flags & BGATTR) | ATTR_FGCOLOR24;
|
|
*fg = color;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static inline unichar read_unichar(const unsigned char *data, const unsigned char **next, int *width)
|
|
{
|
|
unichar chr = g_utf8_get_char_validated(data, -1);
|
|
|
|
if (chr & 0x80000000) {
|
|
chr = 0xfffd;
|
|
*next = data + 1;
|
|
*width = 1;
|
|
} else {
|
|
*next = g_utf8_next_char(data);
|
|
*width = unichar_isprint(chr) ? mk_wcwidth(chr) : 1;
|
|
}
|
|
return chr;
|
|
}
|
|
|
|
static LINE_CACHE_REC *
|
|
view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
|
|
{
|
|
INDENT_FUNC indent_func;
|
|
LINE_CACHE_REC *rec;
|
|
LINE_CACHE_SUB_REC *sub;
|
|
GSList *lines;
|
|
unsigned char cmd;
|
|
const unsigned char *ptr, *next_ptr, *last_space_ptr;
|
|
int xpos, pos, indent_pos, last_space, last_color, color, linecount;
|
|
int last_bg24, last_fg24, bg24, fg24;
|
|
int char_width;
|
|
|
|
g_return_val_if_fail(line->text != NULL, NULL);
|
|
|
|
color = ATTR_RESETFG | ATTR_RESETBG;
|
|
xpos = 0; indent_pos = view->default_indent;
|
|
last_space = last_color = 0; last_space_ptr = NULL; sub = NULL;
|
|
|
|
indent_func = view->default_indent_func;
|
|
linecount = 1;
|
|
lines = NULL;
|
|
for (ptr = line->text;;) {
|
|
if (*ptr == '\0') {
|
|
/* command */
|
|
ptr++;
|
|
cmd = *ptr;
|
|
ptr++;
|
|
|
|
if (cmd == LINE_CMD_EOL)
|
|
break;
|
|
|
|
if (cmd == LINE_CMD_CONTINUE) {
|
|
unsigned char *tmp;
|
|
|
|
memcpy(&tmp, ptr, sizeof(char *));
|
|
ptr = tmp;
|
|
continue;
|
|
}
|
|
|
|
if (cmd == LINE_CMD_INDENT) {
|
|
/* set indentation position here - don't do
|
|
it if we're too close to right border */
|
|
if (xpos < view->width-5) indent_pos = xpos;
|
|
} else if (cmd == LINE_COLOR_EXT) {
|
|
color &= ~ATTR_FGCOLOR24;
|
|
color = (color & BGATTR) | *ptr++;
|
|
} else if (cmd == LINE_COLOR_EXT_BG) {
|
|
color &= ~ATTR_BGCOLOR24;
|
|
color = (color & FGATTR) | (*ptr++ << BG_SHIFT);
|
|
}
|
|
#ifdef TERM_TRUECOLOR
|
|
else if (cmd == LINE_COLOR_24)
|
|
unformat_24bit_line_color(&ptr, 0, &color, &fg24, &bg24);
|
|
#endif
|
|
else
|
|
update_cmd_color(cmd, &color);
|
|
continue;
|
|
}
|
|
|
|
if (!view->utf8) {
|
|
/* MH */
|
|
if (term_type != TERM_TYPE_BIG5 ||
|
|
ptr[1] == '\0' || !is_big5(ptr[0], ptr[1]))
|
|
char_width = 1;
|
|
else
|
|
char_width = 2;
|
|
next_ptr = ptr+char_width;
|
|
} else {
|
|
read_unichar(ptr, &next_ptr, &char_width);
|
|
}
|
|
|
|
if (xpos + char_width > view->width && sub != NULL &&
|
|
(last_space <= indent_pos || last_space <= 10) &&
|
|
view->longword_noindent) {
|
|
/* long word, remove the indentation from this line */
|
|
xpos -= sub->indent;
|
|
sub->indent = 0;
|
|
}
|
|
|
|
if (xpos + char_width > view->width) {
|
|
xpos = indent_func == NULL ? indent_pos :
|
|
indent_func(view, line, -1);
|
|
|
|
sub = g_new0(LINE_CACHE_SUB_REC, 1);
|
|
if (last_space > indent_pos && last_space > 10) {
|
|
/* go back to last space */
|
|
color = last_color; fg24 = last_fg24; bg24 = last_bg24;
|
|
ptr = last_space_ptr;
|
|
while (*ptr == ' ') ptr++;
|
|
} else if (view->longword_noindent) {
|
|
/* long word, no indentation in next line */
|
|
xpos = 0;
|
|
sub->continues = TRUE;
|
|
}
|
|
|
|
sub->start = ptr;
|
|
sub->indent = xpos;
|
|
sub->indent_func = indent_func;
|
|
sub->color = color;
|
|
#ifdef TERM_TRUECOLOR
|
|
sub->fg24 = fg24; sub->bg24 = bg24;
|
|
#endif
|
|
|
|
lines = g_slist_append(lines, sub);
|
|
linecount++;
|
|
|
|
last_space = 0;
|
|
continue;
|
|
}
|
|
|
|
if (!view->utf8 && char_width > 1) {
|
|
last_space = xpos;
|
|
last_space_ptr = next_ptr;
|
|
last_color = color; last_fg24 = fg24; last_bg24 = bg24;
|
|
} else if (*ptr == ' ') {
|
|
last_space = xpos;
|
|
last_space_ptr = ptr;
|
|
last_color = color; last_fg24 = fg24; last_bg24 = bg24;
|
|
}
|
|
|
|
xpos += char_width;
|
|
ptr = next_ptr;
|
|
}
|
|
|
|
rec = g_malloc(sizeof(LINE_CACHE_REC)-sizeof(LINE_CACHE_SUB_REC) +
|
|
sizeof(LINE_CACHE_SUB_REC) * (linecount-1));
|
|
rec->last_access = time(NULL);
|
|
rec->count = linecount;
|
|
|
|
if (rec->count > 1) {
|
|
for (pos = 0; lines != NULL; pos++) {
|
|
void *data = lines->data;
|
|
|
|
memcpy(&rec->lines[pos], data,
|
|
sizeof(LINE_CACHE_SUB_REC));
|
|
|
|
lines = g_slist_remove(lines, data);
|
|
g_free(data);
|
|
}
|
|
}
|
|
|
|
g_hash_table_insert(view->cache->line_cache, line, rec);
|
|
return rec;
|
|
}
|
|
|
|
static void view_remove_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
|
|
unsigned char update_counter)
|
|
{
|
|
LINE_CACHE_REC *cache;
|
|
|
|
if (view->cache->update_counter == update_counter)
|
|
return;
|
|
view->cache->update_counter = update_counter;
|
|
|
|
cache = g_hash_table_lookup(view->cache->line_cache, line);
|
|
if (cache != NULL) {
|
|
g_free(cache);
|
|
g_hash_table_remove(view->cache->line_cache, line);
|
|
}
|
|
}
|
|
|
|
static void view_update_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
|
|
unsigned char update_counter)
|
|
{
|
|
view_remove_cache(view, line, update_counter);
|
|
|
|
if (view->buffer->cur_line == line)
|
|
view->cache->last_linecount = view_get_linecount(view, line);
|
|
}
|
|
|
|
static void view_reset_cache(TEXT_BUFFER_VIEW_REC *view)
|
|
{
|
|
GSList *tmp;
|
|
|
|
/* destroy line caches - note that you can't do simultaneously
|
|
unrefs + cache_get()s or it will keep using the old caches */
|
|
textbuffer_cache_unref(view->cache);
|
|
g_slist_foreach(view->siblings, (GFunc) textbuffer_cache_unref, NULL);
|
|
|
|
view->cache = textbuffer_cache_get(view->siblings, view->width);
|
|
for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
|
|
TEXT_BUFFER_VIEW_REC *rec = tmp->data;
|
|
|
|
rec->cache = textbuffer_cache_get(rec->siblings, rec->width);
|
|
}
|
|
}
|
|
|
|
static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
|
|
int subline, int ypos, int max)
|
|
{
|
|
INDENT_FUNC indent_func;
|
|
LINE_CACHE_REC *cache;
|
|
const unsigned char *text, *end, *text_newline;
|
|
unsigned char *tmp;
|
|
unichar chr;
|
|
int xpos, color, fg24, bg24, drawcount, first, need_move, need_clrtoeol, char_width;
|
|
|
|
if (view->dirty) /* don't bother drawing anything - redraw is coming */
|
|
return 0;
|
|
|
|
cache = textbuffer_view_get_line_cache(view, line);
|
|
if (subline >= cache->count)
|
|
return 0;
|
|
|
|
color = ATTR_RESET;
|
|
need_move = TRUE; need_clrtoeol = FALSE;
|
|
xpos = drawcount = 0; first = TRUE;
|
|
text_newline = text =
|
|
subline == 0 ? line->text : cache->lines[subline-1].start;
|
|
for (;;) {
|
|
if (text == text_newline) {
|
|
if (need_clrtoeol && xpos < term_width) {
|
|
term_set_color(view->window, ATTR_RESET);
|
|
term_clrtoeol(view->window);
|
|
}
|
|
|
|
if (first)
|
|
first = FALSE;
|
|
else {
|
|
ypos++;
|
|
if (--max == 0)
|
|
break;
|
|
}
|
|
|
|
if (subline > 0) {
|
|
/* continuing previous line - indent it */
|
|
indent_func = cache->lines[subline-1].indent_func;
|
|
if (indent_func == NULL)
|
|
xpos = cache->lines[subline-1].indent;
|
|
color = cache->lines[subline-1].color;
|
|
#ifdef TERM_TRUECOLOR
|
|
fg24 = cache->lines[subline-1].fg24;
|
|
bg24 = cache->lines[subline-1].bg24;
|
|
#endif
|
|
} else {
|
|
indent_func = NULL;
|
|
}
|
|
|
|
if (xpos == 0 && indent_func == NULL)
|
|
need_clrtoeol = TRUE;
|
|
else {
|
|
/* line was indented - need to clear the
|
|
indented area first */
|
|
term_set_color(view->window, ATTR_RESET);
|
|
term_move(view->window, 0, ypos);
|
|
term_clrtoeol(view->window);
|
|
|
|
if (indent_func != NULL)
|
|
xpos = indent_func(view, line, ypos);
|
|
}
|
|
|
|
if (need_move || xpos > 0)
|
|
term_move(view->window, xpos, ypos);
|
|
|
|
term_set_color2(view->window, color, fg24, bg24);
|
|
|
|
if (subline == cache->count-1) {
|
|
text_newline = NULL;
|
|
need_move = FALSE;
|
|
} else {
|
|
/* get the beginning of the next subline */
|
|
text_newline = cache->lines[subline].start;
|
|
need_move = !cache->lines[subline].continues;
|
|
}
|
|
drawcount++;
|
|
subline++;
|
|
}
|
|
|
|
if (*text == '\0') {
|
|
/* command */
|
|
text++;
|
|
if (*text == LINE_CMD_EOL)
|
|
break;
|
|
|
|
if (*text == LINE_CMD_CONTINUE) {
|
|
/* jump to next block */
|
|
memcpy(&tmp, text+1, sizeof(unsigned char *));
|
|
text = tmp;
|
|
continue;
|
|
} else {
|
|
if (*text == LINE_COLOR_EXT)
|
|
color = (color & BGATTR & ~ATTR_FGCOLOR24) | *++text;
|
|
else if (*text == LINE_COLOR_EXT_BG)
|
|
color = (color & FGATTR & ~ATTR_BGCOLOR24) | (*++text << BG_SHIFT);
|
|
#ifdef TERM_TRUECOLOR
|
|
else if (*text == LINE_COLOR_24)
|
|
unformat_24bit_line_color(&text, 1, &color, &fg24, &bg24);
|
|
#endif
|
|
else
|
|
update_cmd_color(*text, &color);
|
|
term_set_color2(view->window, color, fg24, bg24);
|
|
}
|
|
text++;
|
|
continue;
|
|
}
|
|
|
|
if (view->utf8) {
|
|
chr = read_unichar(text, &end, &char_width);
|
|
} else {
|
|
chr = *text;
|
|
end = text;
|
|
if (term_type == TERM_TYPE_BIG5 &&
|
|
is_big5(end[0], end[1]))
|
|
char_width = 2;
|
|
else
|
|
char_width = 1;
|
|
end += char_width;
|
|
}
|
|
|
|
xpos += char_width;
|
|
if (xpos <= term_width) {
|
|
if (unichar_isprint(chr)) {
|
|
if (view->utf8)
|
|
term_add_unichar(view->window, chr);
|
|
else
|
|
for (; text < end; text++)
|
|
term_addch(view->window, *text);
|
|
} else {
|
|
/* low-ascii */
|
|
term_set_color(view->window, ATTR_RESET|ATTR_REVERSE);
|
|
term_addch(view->window, (chr & 127)+'A'-1);
|
|
term_set_color2(view->window, color, fg24, bg24);
|
|
}
|
|
}
|
|
text = end;
|
|
}
|
|
|
|
if (need_clrtoeol && xpos < term_width) {
|
|
term_set_color(view->window, ATTR_RESET);
|
|
term_clrtoeol(view->window);
|
|
}
|
|
|
|
return drawcount;
|
|
}
|
|
|
|
/* Recalculate view's bottom line information - try to keep the
|
|
original if possible */
|
|
static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
|
|
{
|
|
LINE_REC *line;
|
|
int linecount, total;
|
|
|
|
if (view->empty_linecount == 0) {
|
|
/* no empty lines in screen, no need to try to keep
|
|
the old bottom startline */
|
|
view->bottom_startline = NULL;
|
|
}
|
|
|
|
total = 0;
|
|
line = textbuffer_line_last(view->buffer);
|
|
for (; line != NULL; line = line->prev) {
|
|
linecount = view_get_linecount(view, line);
|
|
if (line == view->bottom_startline) {
|
|
/* keep the old one, make sure that subline is ok */
|
|
if (view->bottom_subline > linecount)
|
|
view->bottom_subline = linecount;
|
|
view->empty_linecount = view->height - total -
|
|
(linecount-view->bottom_subline);
|
|
return;
|
|
}
|
|
|
|
total += linecount;
|
|
if (total >= view->height) {
|
|
view->bottom_startline = line;
|
|
view->bottom_subline = total - view->height;
|
|
view->empty_linecount = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* not enough lines so we must be at the beginning of the buffer */
|
|
view->bottom_startline = view->buffer->first_line;
|
|
view->bottom_subline = 0;
|
|
view->empty_linecount = view->height - total;
|
|
}
|
|
|
|
static void textbuffer_view_init_ypos(TEXT_BUFFER_VIEW_REC *view)
|
|
{
|
|
LINE_REC *line;
|
|
|
|
g_return_if_fail(view != NULL);
|
|
|
|
view->ypos = -view->subline-1;
|
|
for (line = view->startline; line != NULL; line = line->next)
|
|
view->ypos += view_get_linecount(view, line);
|
|
}
|
|
|
|
/* Create new view. */
|
|
TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
|
|
int width, int height,
|
|
int scroll, int utf8)
|
|
{
|
|
TEXT_BUFFER_VIEW_REC *view;
|
|
|
|
g_return_val_if_fail(buffer != NULL, NULL);
|
|
g_return_val_if_fail(width > 0, NULL);
|
|
|
|
view = g_new0(TEXT_BUFFER_VIEW_REC, 1);
|
|
view->buffer = buffer;
|
|
view->siblings = textbuffer_get_views(buffer);
|
|
|
|
view->width = width;
|
|
view->height = height;
|
|
view->scroll = scroll;
|
|
view->utf8 = utf8;
|
|
|
|
view->cache = textbuffer_cache_get(view->siblings, width);
|
|
textbuffer_view_init_bottom(view);
|
|
|
|
view->startline = view->bottom_startline;
|
|
view->subline = view->bottom_subline;
|
|
view->bottom = TRUE;
|
|
|
|
textbuffer_view_init_ypos(view);
|
|
|
|
view->bookmarks = g_hash_table_new((GHashFunc) g_str_hash,
|
|
(GCompareFunc) g_str_equal);
|
|
|
|
views = g_slist_append(views, view);
|
|
return view;
|
|
}
|
|
|
|
/* Destroy the view. */
|
|
void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view)
|
|
{
|
|
GSList *tmp;
|
|
|
|
g_return_if_fail(view != NULL);
|
|
|
|
views = g_slist_remove(views, view);
|
|
|
|
if (view->siblings == NULL) {
|
|
/* last view for textbuffer, destroy */
|
|
textbuffer_destroy(view->buffer);
|
|
} else {
|
|
/* remove ourself from siblings lists */
|
|
for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
|
|
TEXT_BUFFER_VIEW_REC *rec = tmp->data;
|
|
|
|
rec->siblings = g_slist_remove(rec->siblings, view);
|
|
}
|
|
g_slist_free(view->siblings);
|
|
}
|
|
|
|
g_hash_table_foreach(view->bookmarks, (GHFunc) g_free, NULL);
|
|
g_hash_table_destroy(view->bookmarks);
|
|
|
|
textbuffer_cache_unref(view->cache);
|
|
g_free(view);
|
|
}
|
|
|
|
/* Change the default indent position */
|
|
void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
|
|
int default_indent,
|
|
int longword_noindent,
|
|
INDENT_FUNC indent_func)
|
|
{
|
|
if (default_indent != -1)
|
|
view->default_indent = default_indent;
|
|
if (longword_noindent != -1)
|
|
view->longword_noindent = longword_noindent;
|
|
|
|
view->default_indent_func = indent_func;
|
|
}
|
|
|
|
static void view_unregister_indent_func(TEXT_BUFFER_VIEW_REC *view,
|
|
INDENT_FUNC indent_func)
|
|
{
|
|
if (view->default_indent_func == indent_func)
|
|
view->default_indent_func = NULL;
|
|
|
|
/* recreate cache so it won't contain references
|
|
to the indent function */
|
|
view_reset_cache(view);
|
|
view->cache = textbuffer_cache_get(view->siblings, view->width);
|
|
}
|
|
|
|
void textbuffer_views_unregister_indent_func(INDENT_FUNC indent_func)
|
|
{
|
|
g_slist_foreach(views, (GFunc) view_unregister_indent_func,
|
|
(void *) indent_func);
|
|
}
|
|
|
|
void textbuffer_view_set_scroll(TEXT_BUFFER_VIEW_REC *view, int scroll)
|
|
{
|
|
view->scroll = scroll;
|
|
}
|
|
|
|
void textbuffer_view_set_utf8(TEXT_BUFFER_VIEW_REC *view, int utf8)
|
|
{
|
|
view->utf8 = utf8;
|
|
}
|
|
|
|
static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
|
|
{
|
|
int linecount;
|
|
|
|
linecount = 0;
|
|
while (line != NULL) {
|
|
linecount += view_get_linecount(view, line);
|
|
line = line->next;
|
|
}
|
|
|
|
return linecount;
|
|
}
|
|
|
|
static void view_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
|
|
int subline, int ypos, int lines, int fill_bottom)
|
|
{
|
|
int linecount;
|
|
|
|
if (view->dirty) /* don't bother drawing anything - redraw is coming */
|
|
return;
|
|
|
|
while (line != NULL && lines > 0) {
|
|
linecount = view_line_draw(view, line, subline, ypos, lines);
|
|
ypos += linecount; lines -= linecount;
|
|
|
|
subline = 0;
|
|
line = line->next;
|
|
}
|
|
|
|
if (fill_bottom) {
|
|
/* clear the rest of the view */
|
|
term_set_color(view->window, ATTR_RESET);
|
|
while (lines > 0) {
|
|
term_move(view->window, 0, ypos);
|
|
term_clrtoeol(view->window);
|
|
ypos++; lines--;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define view_draw_top(view, lines, fill_bottom) \
|
|
view_draw(view, (view)->startline, (view)->subline, \
|
|
0, lines, fill_bottom)
|
|
|
|
static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines)
|
|
{
|
|
LINE_REC *line;
|
|
int ypos, maxline, subline, linecount;
|
|
|
|
maxline = view->height-lines;
|
|
line = view->startline; ypos = -view->subline; subline = 0;
|
|
while (line != NULL && ypos < maxline) {
|
|
linecount = view_get_linecount(view, line);
|
|
ypos += linecount;
|
|
if (ypos > maxline) {
|
|
subline = maxline-(ypos-linecount);
|
|
break;
|
|
}
|
|
line = line->next;
|
|
}
|
|
|
|
view_draw(view, line, subline, maxline, lines, TRUE);
|
|
}
|
|
|
|
/* Returns number of lines actually scrolled */
|
|
static int view_scroll(TEXT_BUFFER_VIEW_REC *view, LINE_REC **lines,
|
|
int *subline, int scrollcount, int draw_nonclean)
|
|
{
|
|
int linecount, realcount, scroll_visible;
|
|
|
|
if (*lines == NULL)
|
|
return 0;
|
|
|
|
/* scroll down */
|
|
scroll_visible = lines == &view->startline;
|
|
|
|
realcount = -*subline;
|
|
scrollcount += *subline;
|
|
*subline = 0;
|
|
while (scrollcount > 0) {
|
|
linecount = view_get_linecount(view, *lines);
|
|
|
|
if ((scroll_visible && *lines == view->bottom_startline) &&
|
|
(scrollcount >= view->bottom_subline)) {
|
|
*subline = view->bottom_subline;
|
|
realcount += view->bottom_subline;
|
|
scrollcount = 0;
|
|
break;
|
|
}
|
|
|
|
realcount += linecount;
|
|
scrollcount -= linecount;
|
|
if (scrollcount < 0) {
|
|
realcount += scrollcount;
|
|
*subline = linecount+scrollcount;
|
|
scrollcount = 0;
|
|
break;
|
|
}
|
|
|
|
if ((*lines)->next == NULL)
|
|
break;
|
|
|
|
*lines = (*lines)->next;
|
|
}
|
|
|
|
/* scroll up */
|
|
while (scrollcount < 0 && (*lines)->prev != NULL) {
|
|
*lines = (*lines)->prev;
|
|
linecount = view_get_linecount(view, *lines);
|
|
|
|
realcount -= linecount;
|
|
scrollcount += linecount;
|
|
if (scrollcount > 0) {
|
|
realcount += scrollcount;
|
|
*subline = scrollcount;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (scroll_visible && realcount != 0 && view->window != NULL) {
|
|
if (realcount <= -view->height || realcount >= view->height) {
|
|
/* scrolled more than screenful, redraw the
|
|
whole view */
|
|
textbuffer_view_redraw(view);
|
|
} else {
|
|
term_set_color(view->window, ATTR_RESET);
|
|
term_window_scroll(view->window, realcount);
|
|
|
|
if (draw_nonclean) {
|
|
if (realcount < 0)
|
|
view_draw_top(view, -realcount, TRUE);
|
|
else
|
|
view_draw_bottom(view, realcount);
|
|
}
|
|
|
|
term_refresh(view->window);
|
|
}
|
|
}
|
|
|
|
return realcount >= 0 ? realcount : -realcount;
|
|
}
|
|
|
|
/* Resize the view. */
|
|
void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height)
|
|
{
|
|
int linecount;
|
|
|
|
g_return_if_fail(view != NULL);
|
|
g_return_if_fail(width > 0);
|
|
|
|
if (view->width != width) {
|
|
/* line cache needs to be recreated */
|
|
textbuffer_cache_unref(view->cache);
|
|
view->cache = textbuffer_cache_get(view->siblings, width);
|
|
}
|
|
|
|
view->width = width > 10 ? width : 10;
|
|
view->height = height > 1 ? height : 1;
|
|
|
|
if (view->buffer->first_line == NULL) {
|
|
view->empty_linecount = height;
|
|
return;
|
|
}
|
|
|
|
textbuffer_view_init_bottom(view);
|
|
|
|
/* check that we didn't scroll lower than bottom startline.. */
|
|
if (textbuffer_line_exists_after(view->bottom_startline->next,
|
|
view->startline)) {
|
|
view->startline = view->bottom_startline;
|
|
view->subline = view->bottom_subline;
|
|
} else if (view->startline == view->bottom_startline &&
|
|
view->subline > view->bottom_subline) {
|
|
view->subline = view->bottom_subline;
|
|
} else {
|
|
/* make sure the subline is still in allowed range */
|
|
linecount = view_get_linecount(view, view->startline);
|
|
if (view->subline > linecount)
|
|
view->subline = linecount;
|
|
}
|
|
|
|
textbuffer_view_init_ypos(view);
|
|
if (view->bottom && !view_is_bottom(view)) {
|
|
/* we scrolled to far up, need to get down. go right over
|
|
the empty lines if there's any */
|
|
view->startline = view->bottom_startline;
|
|
view->subline = view->bottom_subline;
|
|
if (view->empty_linecount > 0) {
|
|
view_scroll(view, &view->startline, &view->subline,
|
|
-view->empty_linecount, FALSE);
|
|
}
|
|
textbuffer_view_init_ypos(view);
|
|
}
|
|
|
|
view->bottom = view_is_bottom(view);
|
|
if (view->bottom) {
|
|
/* check if we left empty space at the bottom.. */
|
|
linecount = view_get_linecount_all(view, view->startline) -
|
|
view->subline;
|
|
if (view->empty_linecount < view->height-linecount)
|
|
view->empty_linecount = view->height-linecount;
|
|
view->more_text = FALSE;
|
|
}
|
|
|
|
view->dirty = TRUE;
|
|
}
|
|
|
|
/* Clear the view, don't actually remove any lines from buffer. */
|
|
void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view)
|
|
{
|
|
g_return_if_fail(view != NULL);
|
|
|
|
view->ypos = -1;
|
|
view->bottom_startline = view->startline =
|
|
textbuffer_line_last(view->buffer);
|
|
view->bottom_subline = view->subline =
|
|
view->buffer->cur_line == NULL ? 0 :
|
|
view_get_linecount(view, view->buffer->cur_line);
|
|
view->empty_linecount = view->height;
|
|
view->bottom = TRUE;
|
|
view->more_text = FALSE;
|
|
|
|
textbuffer_view_redraw(view);
|
|
}
|
|
|
|
/* Scroll the view up/down */
|
|
void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines)
|
|
{
|
|
int count;
|
|
|
|
g_return_if_fail(view != NULL);
|
|
|
|
count = view_scroll(view, &view->startline, &view->subline,
|
|
lines, TRUE);
|
|
view->ypos += lines < 0 ? count : -count;
|
|
view->bottom = view_is_bottom(view);
|
|
if (view->bottom) view->more_text = FALSE;
|
|
|
|
if (view->window != NULL)
|
|
term_refresh(view->window);
|
|
}
|
|
|
|
/* Scroll to specified line */
|
|
void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
|
|
{
|
|
g_return_if_fail(view != NULL);
|
|
|
|
if (textbuffer_line_exists_after(view->bottom_startline->next, line)) {
|
|
view->startline = view->bottom_startline;
|
|
view->subline = view->bottom_subline;
|
|
} else {
|
|
view->startline = line;
|
|
view->subline = 0;
|
|
}
|
|
|
|
textbuffer_view_init_ypos(view);
|
|
view->bottom = view_is_bottom(view);
|
|
if (view->bottom) view->more_text = FALSE;
|
|
|
|
textbuffer_view_redraw(view);
|
|
}
|
|
|
|
/* Return line cache */
|
|
LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view,
|
|
LINE_REC *line)
|
|
{
|
|
LINE_CACHE_REC *cache;
|
|
|
|
g_assert(view != NULL);
|
|
g_assert(line != NULL);
|
|
|
|
cache = g_hash_table_lookup(view->cache->line_cache, line);
|
|
if (cache == NULL)
|
|
cache = view_update_line_cache(view, line);
|
|
else
|
|
cache->last_access = time(NULL);
|
|
|
|
return cache;
|
|
}
|
|
|
|
static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
|
|
{
|
|
int linecount, ypos, subline;
|
|
|
|
if (!view->bottom)
|
|
view->more_text = TRUE;
|
|
|
|
if (view->bottom_startline == NULL) {
|
|
view->startline = view->bottom_startline =
|
|
view->buffer->first_line;
|
|
}
|
|
|
|
if (view->buffer->cur_line != line &&
|
|
!textbuffer_line_exists_after(view->bottom_startline, line))
|
|
return;
|
|
|
|
linecount = view_get_linecount(view, line);
|
|
view->ypos += linecount;
|
|
if (view->empty_linecount > 0) {
|
|
view->empty_linecount -= linecount;
|
|
if (view->empty_linecount >= 0)
|
|
linecount = 0;
|
|
else {
|
|
linecount = -view->empty_linecount;
|
|
view->empty_linecount = 0;
|
|
}
|
|
}
|
|
|
|
if (linecount > 0) {
|
|
view_scroll(view, &view->bottom_startline,
|
|
&view->bottom_subline, linecount, FALSE);
|
|
}
|
|
|
|
if (view->bottom) {
|
|
if (view->scroll && view->ypos >= view->height) {
|
|
linecount = view->ypos-view->height+1;
|
|
view_scroll(view, &view->startline,
|
|
&view->subline, linecount, FALSE);
|
|
view->ypos -= linecount;
|
|
} else {
|
|
view->bottom = view_is_bottom(view);
|
|
}
|
|
|
|
if (view->window != NULL) {
|
|
ypos = view->ypos+1 - view_get_linecount(view, line);
|
|
if (ypos >= 0)
|
|
subline = 0;
|
|
else {
|
|
subline = -ypos;
|
|
ypos = 0;
|
|
}
|
|
if (ypos < view->height) {
|
|
view_line_draw(view, line, subline, ypos,
|
|
view->height - ypos);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (view->window != NULL)
|
|
term_refresh(view->window);
|
|
}
|
|
|
|
/* Update some line in the buffer which has been modified using
|
|
textbuffer_append() or textbuffer_insert(). */
|
|
void textbuffer_view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
|
|
{
|
|
GSList *tmp;
|
|
unsigned char update_counter;
|
|
|
|
g_return_if_fail(view != NULL);
|
|
g_return_if_fail(line != NULL);
|
|
|
|
if (!view->buffer->last_eol)
|
|
return;
|
|
|
|
update_counter = view->cache->update_counter+1;
|
|
view_update_cache(view, line, update_counter);
|
|
view_insert_line(view, line);
|
|
|
|
for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
|
|
TEXT_BUFFER_VIEW_REC *rec = tmp->data;
|
|
|
|
view_update_cache(rec, line, update_counter);
|
|
view_insert_line(rec, line);
|
|
}
|
|
}
|
|
|
|
typedef struct {
|
|
LINE_REC *remove_line;
|
|
GSList *remove_list;
|
|
} BOOKMARK_FIND_REC;
|
|
|
|
static void bookmark_check_remove(char *key, LINE_REC *line,
|
|
BOOKMARK_FIND_REC *rec)
|
|
{
|
|
if (line == rec->remove_line)
|
|
rec->remove_list = g_slist_append(rec->remove_list, key);
|
|
}
|
|
|
|
static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
|
|
{
|
|
BOOKMARK_FIND_REC rec;
|
|
GSList *tmp;
|
|
|
|
rec.remove_line = line;
|
|
rec.remove_list = NULL;
|
|
g_hash_table_foreach(view->bookmarks,
|
|
(GHFunc) bookmark_check_remove, &rec);
|
|
|
|
if (rec.remove_list != NULL) {
|
|
for (tmp = rec.remove_list; tmp != NULL; tmp = tmp->next) {
|
|
g_hash_table_remove(view->bookmarks, tmp->data);
|
|
g_free(tmp->data);
|
|
}
|
|
g_slist_free(rec.remove_list);
|
|
}
|
|
}
|
|
|
|
/* Return number of real lines `lines' list takes -
|
|
stops counting when the height reaches the view height */
|
|
static int view_get_lines_height(TEXT_BUFFER_VIEW_REC *view,
|
|
LINE_REC *line, int subline,
|
|
LINE_REC *skip_line)
|
|
{
|
|
int height, linecount;
|
|
|
|
height = -subline;
|
|
while (line != NULL && height < view->height) {
|
|
if (line != skip_line) {
|
|
linecount = view_get_linecount(view, line);
|
|
height += linecount;
|
|
}
|
|
line = line->next;
|
|
}
|
|
|
|
return height < view->height ? height : view->height;
|
|
}
|
|
|
|
static void view_remove_line_update_startline(TEXT_BUFFER_VIEW_REC *view,
|
|
LINE_REC *line, int linecount)
|
|
{
|
|
int scroll;
|
|
|
|
if (view->startline == line) {
|
|
view->startline = view->startline->prev != NULL ?
|
|
view->startline->prev : view->startline->next;
|
|
view->subline = 0;
|
|
} else {
|
|
scroll = view->height -
|
|
view_get_lines_height(view, view->startline,
|
|
view->subline, line);
|
|
if (scroll > 0) {
|
|
view_scroll(view, &view->startline,
|
|
&view->subline, -scroll, FALSE);
|
|
}
|
|
}
|
|
|
|
/* FIXME: this is slow and unnecessary, but it's easy and
|
|
really works :) */
|
|
textbuffer_view_init_ypos(view);
|
|
if (textbuffer_line_exists_after(view->startline, line))
|
|
view->ypos -= linecount;
|
|
}
|
|
|
|
static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
|
|
int linecount)
|
|
{
|
|
int realcount;
|
|
|
|
view_bookmarks_check(view, line);
|
|
|
|
if (view->buffer->cur_line == line) {
|
|
/* the last line is being removed */
|
|
LINE_REC *prevline;
|
|
|
|
prevline = view->buffer->first_line == line ? NULL :
|
|
textbuffer_line_last(view->buffer);
|
|
view->cache->last_linecount = prevline == NULL ? 0 :
|
|
view_get_linecount(view, prevline);
|
|
}
|
|
|
|
if (view->buffer->first_line == line) {
|
|
/* first line in the buffer - this is the most commonly
|
|
removed line.. */
|
|
if (view->bottom_startline == line) {
|
|
/* very small scrollback.. */
|
|
view->bottom_startline = view->bottom_startline->next;
|
|
view->bottom_subline = 0;
|
|
}
|
|
|
|
if (view->startline == line) {
|
|
/* removing the first line in screen */
|
|
int is_last = view->startline->next == NULL;
|
|
|
|
realcount = view_scroll(view, &view->startline,
|
|
&view->subline,
|
|
linecount, FALSE);
|
|
view->ypos -= realcount;
|
|
view->empty_linecount += linecount-realcount;
|
|
if (is_last == 1)
|
|
view->startline = NULL;
|
|
}
|
|
} else {
|
|
if (textbuffer_line_exists_after(view->bottom_startline,
|
|
line)) {
|
|
realcount = view_scroll(view, &view->bottom_startline,
|
|
&view->bottom_subline,
|
|
-linecount, FALSE);
|
|
view->empty_linecount += linecount-realcount;
|
|
}
|
|
|
|
if (textbuffer_line_exists_after(view->startline,
|
|
line)) {
|
|
view_remove_line_update_startline(view, line,
|
|
linecount);
|
|
}
|
|
}
|
|
|
|
view->bottom = view_is_bottom(view);
|
|
if (view->bottom) view->more_text = FALSE;
|
|
if (view->window != NULL)
|
|
term_refresh(view->window);
|
|
}
|
|
|
|
/* Remove one line from buffer. */
|
|
void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
|
|
{
|
|
GSList *tmp;
|
|
unsigned char update_counter;
|
|
int linecount;
|
|
|
|
g_return_if_fail(view != NULL);
|
|
g_return_if_fail(line != NULL);
|
|
|
|
signal_emit("gui textbuffer line removed", 3, view, line, line->prev);
|
|
|
|
linecount = view_get_linecount(view, line);
|
|
update_counter = view->cache->update_counter+1;
|
|
|
|
view_remove_line(view, line, linecount);
|
|
view_remove_cache(view, line, update_counter);
|
|
|
|
for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
|
|
TEXT_BUFFER_VIEW_REC *rec = tmp->data;
|
|
|
|
view_remove_line(rec, line, linecount);
|
|
view_remove_cache(rec, line, update_counter);
|
|
}
|
|
|
|
textbuffer_remove(view->buffer, line);
|
|
}
|
|
|
|
void textbuffer_view_remove_lines_by_level(TEXT_BUFFER_VIEW_REC *view, int level)
|
|
{
|
|
LINE_REC *line, *next;
|
|
|
|
term_refresh_freeze();
|
|
line = textbuffer_view_get_lines(view);
|
|
|
|
while (line != NULL) {
|
|
next = line->next;
|
|
|
|
if (line->info.level & level)
|
|
textbuffer_view_remove_line(view, line);
|
|
line = next;
|
|
}
|
|
textbuffer_view_redraw(view);
|
|
term_refresh_thaw();
|
|
}
|
|
|
|
static int g_free_true(void *data)
|
|
{
|
|
g_free(data);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Remove all lines from buffer. */
|
|
void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view)
|
|
{
|
|
g_return_if_fail(view != NULL);
|
|
|
|
textbuffer_remove_all_lines(view->buffer);
|
|
|
|
g_hash_table_foreach_remove(view->bookmarks,
|
|
(GHRFunc) g_free_true, NULL);
|
|
|
|
view_reset_cache(view);
|
|
textbuffer_view_clear(view);
|
|
g_slist_foreach(view->siblings, (GFunc) textbuffer_view_clear, NULL);
|
|
}
|
|
|
|
/* Set a bookmark in view */
|
|
void textbuffer_view_set_bookmark(TEXT_BUFFER_VIEW_REC *view,
|
|
const char *name, LINE_REC *line)
|
|
{
|
|
gpointer key, value;
|
|
|
|
g_return_if_fail(view != NULL);
|
|
g_return_if_fail(name != NULL);
|
|
|
|
if (g_hash_table_lookup_extended(view->bookmarks, name,
|
|
&key, &value)) {
|
|
g_hash_table_remove(view->bookmarks, key);
|
|
g_free(key);
|
|
}
|
|
|
|
g_hash_table_insert(view->bookmarks, g_strdup(name), line);
|
|
}
|
|
|
|
/* Set a bookmark in view to the bottom line */
|
|
void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view,
|
|
const char *name)
|
|
{
|
|
LINE_REC *line;
|
|
|
|
g_return_if_fail(view != NULL);
|
|
g_return_if_fail(name != NULL);
|
|
|
|
if (view->bottom_startline != NULL) {
|
|
line = textbuffer_line_last(view->buffer);
|
|
textbuffer_view_set_bookmark(view, name, line);
|
|
}
|
|
}
|
|
|
|
/* Return the line for bookmark */
|
|
LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
|
|
const char *name)
|
|
{
|
|
g_return_val_if_fail(view != NULL, NULL);
|
|
g_return_val_if_fail(name != NULL, NULL);
|
|
|
|
return g_hash_table_lookup(view->bookmarks, name);
|
|
}
|
|
|
|
/* Specify window where the changes in view should be drawn,
|
|
NULL disables it. */
|
|
void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view,
|
|
TERM_WINDOW *window)
|
|
{
|
|
g_return_if_fail(view != NULL);
|
|
|
|
if (view->window != window) {
|
|
view->window = window;
|
|
if (window != NULL)
|
|
view->dirty = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Redraw a view to window */
|
|
void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view)
|
|
{
|
|
g_return_if_fail(view != NULL);
|
|
|
|
if (view->window != NULL) {
|
|
view->dirty = FALSE;
|
|
view_draw_top(view, view->height, TRUE);
|
|
term_refresh(view->window);
|
|
}
|
|
}
|
|
|
|
static int line_cache_check_remove(void *key, LINE_CACHE_REC *cache,
|
|
time_t *now)
|
|
{
|
|
if (cache->last_access+LINE_CACHE_KEEP_TIME > *now)
|
|
return FALSE;
|
|
|
|
line_cache_destroy(NULL, cache);
|
|
return TRUE;
|
|
}
|
|
|
|
static int sig_check_linecache(void)
|
|
{
|
|
GSList *tmp, *caches;
|
|
time_t now;
|
|
|
|
now = time(NULL); caches = NULL;
|
|
for (tmp = views; tmp != NULL; tmp = tmp->next) {
|
|
TEXT_BUFFER_VIEW_REC *rec = tmp->data;
|
|
|
|
if (g_slist_find(caches, rec->cache) != NULL)
|
|
continue;
|
|
|
|
caches = g_slist_append(caches, rec->cache);
|
|
g_hash_table_foreach_remove(rec->cache->line_cache,
|
|
(GHRFunc) line_cache_check_remove,
|
|
&now);
|
|
}
|
|
|
|
g_slist_free(caches);
|
|
return 1;
|
|
}
|
|
|
|
void textbuffer_view_init(void)
|
|
{
|
|
linecache_tag = g_timeout_add(LINE_CACHE_CHECK_TIME, (GSourceFunc) sig_check_linecache, NULL);
|
|
}
|
|
|
|
void textbuffer_view_deinit(void)
|
|
{
|
|
g_source_remove(linecache_tag);
|
|
}
|