summaryrefslogtreecommitdiff
path: root/src/fe-text
diff options
context:
space:
mode:
Diffstat (limited to 'src/fe-text')
-rw-r--r--src/fe-text/Makefile.am10
-rw-r--r--src/fe-text/gui-printtext.c478
-rw-r--r--src/fe-text/gui-printtext.h4
-rw-r--r--src/fe-text/gui-readline.c2
-rw-r--r--src/fe-text/gui-textwidget.c282
-rw-r--r--src/fe-text/gui-windows.c1056
-rw-r--r--src/fe-text/gui-windows.h109
-rw-r--r--src/fe-text/irssi.c12
-rw-r--r--src/fe-text/lastlog.c38
-rw-r--r--src/fe-text/mainwindows-save.c4
-rw-r--r--src/fe-text/mainwindows.c146
-rw-r--r--src/fe-text/mainwindows.h6
-rw-r--r--src/fe-text/screen.c14
-rw-r--r--src/fe-text/statusbar-items.c8
-rw-r--r--src/fe-text/statusbar.c2
-rw-r--r--src/fe-text/textbuffer-commands.c297
-rw-r--r--src/fe-text/textbuffer-view.c1059
-rw-r--r--src/fe-text/textbuffer-view.h131
-rw-r--r--src/fe-text/textbuffer.c601
-rw-r--r--src/fe-text/textbuffer.h90
20 files changed, 2424 insertions, 1925 deletions
diff --git a/src/fe-text/Makefile.am b/src/fe-text/Makefile.am
index 80149f2e..0da9d03e 100644
--- a/src/fe-text/Makefile.am
+++ b/src/fe-text/Makefile.am
@@ -24,17 +24,19 @@ irssi_SOURCES = \
gui-expandos.c \
gui-printtext.c \
gui-readline.c \
- gui-textwidget.c \
gui-windows.c \
lastlog.c \
mainwindows.c \
mainwindow-activity.c \
mainwindows-save.c \
+ screen.c \
statusbar.c \
statusbar-items.c \
+ textbuffer.c \
+ textbuffer-commands.c \
+ textbuffer-view.c \
irssi.c \
- module-formats.c \
- screen.c
+ module-formats.c
noinst_HEADERS = \
gui-entry.h \
@@ -44,5 +46,7 @@ noinst_HEADERS = \
mainwindows.h \
statusbar.h \
screen.h \
+ textbuffer.h \
+ textbuffer-view.h \
module.h \
module-formats.h
diff --git a/src/fe-text/gui-printtext.c b/src/fe-text/gui-printtext.c
index 2e839dcd..5faae0fa 100644
--- a/src/fe-text/gui-printtext.c
+++ b/src/fe-text/gui-printtext.c
@@ -20,7 +20,6 @@
#include "module.h"
#include "signals.h"
-#include "commands.h"
#include "settings.h"
#include "formats.h"
@@ -29,220 +28,15 @@
#include "screen.h"
#include "gui-windows.h"
-#define TEXT_CHUNK_USABLE_SIZE (LINE_TEXT_CHUNK_SIZE-2-(int)sizeof(char*))
-
int mirc_colors[] = { 15, 0, 1, 2, 12, 6, 5, 4, 14, 10, 3, 11, 9, 13, 8, 7 };
-static int scrollback_lines, scrollback_hours;
+static int scrollback_lines, scrollback_hours, scrollback_burst_remove;
static int scrollback_save_formats;
static GString *format;
+static int last_color, last_flags;
static int next_xpos, next_ypos;
-#define mark_temp_eol(text) \
- memcpy((text)->buffer + (text)->pos, "\0\200", 2);
-
-static LINE_REC *create_line(GUI_WINDOW_REC *gui, int level)
-{
- LINE_REC *rec;
-
- g_return_val_if_fail(gui != NULL, NULL);
- g_return_val_if_fail(gui->cur_text != NULL, NULL);
-
- rec = g_mem_chunk_alloc(gui->line_chunk);
- rec->text = gui->cur_text->buffer+gui->cur_text->pos;
- rec->level = GPOINTER_TO_INT(level);
- rec->time = time(NULL);
-
- mark_temp_eol(gui->cur_text);
- gui->cur_text->lines++;
-
- gui->last_color = -1;
- gui->last_flags = 0;
-
- if (gui->temp_line != NULL) {
- int pos = g_list_index(gui->lines, gui->temp_line);
- gui->lines = g_list_insert(gui->lines, rec, pos+1);
- gui->temp_line = rec;
- } else {
- gui->cur_line = rec;
- gui->lines = g_list_append(gui->lines, rec);
- if (gui->startline == NULL) {
- /* first line */
- gui->startline = gui->lines;
- gui->bottom_startline = gui->lines;
- }
- }
- return rec;
-}
-
-static TEXT_CHUNK_REC *create_text_chunk(GUI_WINDOW_REC *gui)
-{
- TEXT_CHUNK_REC *rec;
- char *buffer, *ptr, **pptr;
-
- g_return_val_if_fail(gui != NULL, NULL);
-
- rec = g_new(TEXT_CHUNK_REC, 1);
- rec->pos = 0;
- rec->lines = 0;
-
- if (gui->cur_line != NULL && gui->cur_line->text != NULL) {
- /* create a link to new block from the old block */
- buffer = gui->cur_text->buffer + gui->cur_text->pos;
- *buffer++ = 0; *buffer++ = (char) LINE_CMD_CONTINUE;
-
- ptr = rec->buffer; pptr = &ptr;
- memcpy(buffer, pptr, sizeof(char *));
- } else {
- /* just to be safe */
- mark_temp_eol(rec);
- }
-
- gui->cur_text = rec;
- gui->text_chunks = g_slist_append(gui->text_chunks, rec);
- return rec;
-}
-
-static void text_chunk_free(GUI_WINDOW_REC *gui, TEXT_CHUNK_REC *chunk)
-{
- g_return_if_fail(gui != NULL);
- g_return_if_fail(chunk != NULL);
-
- gui->text_chunks = g_slist_remove(gui->text_chunks, chunk);
- g_free(chunk);
-}
-
-static TEXT_CHUNK_REC *text_chunk_find(GUI_WINDOW_REC *gui, const char *data)
-{
- GSList *tmp;
-
- for (tmp = gui->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;
- }
-
- return NULL;
-}
-
-void gui_window_line_text_free(GUI_WINDOW_REC *gui, LINE_REC *line)
-{
- TEXT_CHUNK_REC *chunk;
- const char *text;
-
- text = line->text;
- for (;;) {
- if (*text == '\0') {
- text++;
- if ((unsigned char) *text == LINE_CMD_EOL)
- break;
-
- if ((unsigned char) *text == LINE_CMD_CONTINUE) {
- char *tmp;
-
- memcpy(&tmp, text+1, sizeof(char *));
-
- /* free the previous block */
- chunk = text_chunk_find(gui, text);
- if (--chunk->lines == 0)
- text_chunk_free(gui, chunk);
-
- text = tmp;
- continue;
- }
- if ((unsigned char) *text & 0x80)
- text++;
- continue;
- }
-
- text++;
- }
-
- /* free the last block */
- chunk = text_chunk_find(gui, text);
- if (--chunk->lines == 0) {
- if (gui->cur_text == chunk)
- chunk->pos = 0;
- else
- text_chunk_free(gui, chunk);
- }
-}
-
-void gui_window_line_remove(WINDOW_REC *window, LINE_REC *line, int redraw)
-{
- GUI_WINDOW_REC *gui;
- GList *last;
- int screenchange;
-
- g_return_if_fail(window != NULL);
- g_return_if_fail(line != NULL);
-
- gui = WINDOW_GUI(window);
-
- if (gui->lines->next == NULL) {
- /* last line in window */
- gui_window_clear(window);
- return;
- }
-
- screenchange = g_list_find(gui->startline, line) != NULL;
- if (screenchange) gui->ypos -= gui_window_get_linecount(gui, line);
-
- gui_window_cache_remove(gui, line);
- gui_window_line_text_free(gui, line);
- if (gui->lastlog_last_check != NULL &&
- gui->lastlog_last_check->data == line)
- gui->lastlog_last_check = NULL;
- if (gui->lastlog_last_away != NULL &&
- gui->lastlog_last_away->data == line)
- gui->lastlog_last_away = NULL;
-
- last = g_list_last(gui->bottom_startline);
- if (last->data == line) {
- /* removing last line */
- gui->last_subline =
- gui_window_get_linecount(gui, last->prev->data)-1;
- }
-
- if (gui->bottom_startline->data == line) {
- /* bottom line removed */
- if (gui->bottom_startline->next != NULL) {
- gui->bottom_startline = gui->bottom_startline->next;
- gui->bottom_subline = 0;
- } else {
- gui->bottom_startline = gui->bottom_startline->prev;
- gui->bottom_subline = gui->last_subline+1;
- }
- }
-
- if (gui->startline->data == line) {
- /* first line in screen removed */
- if (gui->startline->next != NULL) {
- gui->startline = gui->startline->next;
- gui->subline = 0;
- } else {
- gui->startline = gui->startline->prev;
- gui->subline = gui->last_subline+1;
- gui->ypos = -1;
- gui->empty_linecount = gui->parent->lines;
- gui->bottom = TRUE;
- }
- }
-
- window->lines--;
- g_mem_chunk_free(gui->line_chunk, line);
- gui->lines = g_list_remove(gui->lines, line);
-
- if (window->lines == 0)
- gui_window_clear(window);
-
- if (redraw && screenchange && is_window_visible(window))
- gui_window_redraw(window);
-}
-
void gui_printtext(int xpos, int ypos, const char *str)
{
next_xpos = xpos;
@@ -253,24 +47,22 @@ void gui_printtext(int xpos, int ypos, const char *str)
next_xpos = next_ypos = -1;
}
-static void remove_old_lines(WINDOW_REC *window)
+static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
{
- GUI_WINDOW_REC *gui;
LINE_REC *line;
time_t old_time;
- gui = WINDOW_GUI(window);
-
old_time = time(NULL)-(scrollback_hours*3600)+1;
- if (scrollback_lines > 0) {
+ if (view->buffer->lines_count >=
+ scrollback_lines+scrollback_burst_remove) {
/* remove lines by line count */
- while (window->lines > scrollback_lines) {
- line = gui->lines->data;
- if (line->time >= old_time) {
+ while (view->buffer->lines_count > scrollback_lines) {
+ line = view->buffer->lines->data;
+ if (line->info.time >= old_time) {
/* too new line, don't remove yet */
break;
}
- gui_window_line_remove(window, line, TRUE);
+ textbuffer_view_remove_line(view, line);
}
}
}
@@ -308,37 +100,10 @@ static void get_colors(int flags, int *fg, int *bg)
if (flags & PRINTFLAG_BLINK) *bg |= 0x08;
}
-static void linebuf_add(GUI_WINDOW_REC *gui, const char *str, int len)
-{
- int left;
-
- if (len == 0) return;
-
- while (gui->cur_text->pos + len >= TEXT_CHUNK_USABLE_SIZE) {
- left = TEXT_CHUNK_USABLE_SIZE - gui->cur_text->pos;
- if (str[left-1] == 0) left--; /* don't split the commands */
-
- memcpy(gui->cur_text->buffer + gui->cur_text->pos, str, left);
- gui->cur_text->pos += left;
-
- create_text_chunk(gui);
- gui->cur_text->lines++;
- len -= left; str += left;
- }
-
- memcpy(gui->cur_text->buffer + gui->cur_text->pos, str, len);
- gui->cur_text->pos += len;
-}
-
-void gui_window_line_append(GUI_WINDOW_REC *gui, const char *str, int len)
-{
- linebuf_add(gui, str, len);
- mark_temp_eol(gui->cur_text);
-}
-
-static void line_add_colors(GUI_WINDOW_REC *gui, int fg, int bg, int flags)
+static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line,
+ int fg, int bg, int flags)
{
- unsigned char buffer[12];
+ unsigned char data[12];
int color, pos;
/* color should never have last bit on or it would be treated as a
@@ -346,49 +111,60 @@ static void line_add_colors(GUI_WINDOW_REC *gui, int fg, int bg, int flags)
color = (fg & 0x0f) | ((bg & 0x07) << 4);
pos = 0;
- if (((fg & ATTR_COLOR8) == 0 && (fg|(bg << 4)) != gui->last_color) ||
- ((fg & ATTR_COLOR8) && (fg & 0xf0) != (gui->last_color & 0xf0))) {
- buffer[pos++] = 0;
- buffer[pos++] = color == 0 ? LINE_CMD_COLOR0 : color;
+ if (((fg & ATTR_COLOR8) == 0 && (fg|(bg << 4)) != last_color) ||
+ ((fg & ATTR_COLOR8) && (fg & 0xf0) != (last_color & 0xf0))) {
+ data[pos++] = 0;
+ data[pos++] = color == 0 ? LINE_CMD_COLOR0 : color;
}
- if ((flags & PRINTFLAG_UNDERLINE) != (gui->last_flags & PRINTFLAG_UNDERLINE)) {
- buffer[pos++] = 0;
- buffer[pos++] = LINE_CMD_UNDERLINE;
+ if ((flags & PRINTFLAG_UNDERLINE) != (last_flags & PRINTFLAG_UNDERLINE)) {
+ data[pos++] = 0;
+ data[pos++] = LINE_CMD_UNDERLINE;
}
if (fg & ATTR_COLOR8) {
- buffer[pos++] = 0;
- buffer[pos++] = LINE_CMD_COLOR8;
+ data[pos++] = 0;
+ data[pos++] = LINE_CMD_COLOR8;
}
if (bg & 0x08) {
- buffer[pos++] = 0;
- buffer[pos++] = LINE_CMD_BLINK;
+ data[pos++] = 0;
+ data[pos++] = LINE_CMD_BLINK;
}
if (flags & PRINTFLAG_INDENT) {
- buffer[pos++] = 0;
- buffer[pos++] = LINE_CMD_INDENT;
+ data[pos++] = 0;
+ data[pos++] = LINE_CMD_INDENT;
}
- linebuf_add(gui, (char *) buffer, pos);
+ *line = textbuffer_insert(buffer, *line, data, pos, NULL);
- gui->last_flags = flags;
- gui->last_color = fg | (bg << 4);
+ last_flags = flags;
+ last_color = fg | (bg << 4);
+}
+
+static void view_add_eol(TEXT_BUFFER_VIEW_REC *view, LINE_REC **line)
+{
+ static const unsigned char eol[] = { 0, LINE_CMD_EOL };
+
+ *line = textbuffer_insert(view->buffer, *line, eol, 2, NULL);
+ textbuffer_view_insert_line(view, *line);
}
static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
void *bgcolor, void *pflags,
char *str, void *level)
{
- GUI_WINDOW_REC *gui;
- LINE_REC *line;
- int fg, bg, flags, new_lines, n, visible, ypos, subline;
+ TEXT_BUFFER_VIEW_REC *view;
+ LINE_REC *insert_after;
+ LINE_INFO_REC lineinfo;
+ int fg, bg, flags;
flags = GPOINTER_TO_INT(pflags);
fg = GPOINTER_TO_INT(fgcolor);
bg = GPOINTER_TO_INT(bgcolor);
get_colors(flags, &fg, &bg);
- if (window == NULL && next_xpos != -1) {
+ if (window == NULL) {
+ g_return_if_fail(next_xpos != -1);
+
wmove(stdscr, next_ypos, next_xpos);
set_color(stdscr, fg | (bg << 4));
addstr(str);
@@ -396,159 +172,34 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
return;
}
- g_return_if_fail(window != NULL);
-
- gui = WINDOW_GUI(window);
- visible = is_window_visible(window) && gui->bottom;
-
- if (gui->cur_text == NULL)
- create_text_chunk(gui);
-
- /* newline can be only at the start of the line.. */
- if (flags & PRINTFLAG_NEWLINE) {
- remove_old_lines(window);
- if (!gui->eol_marked) {
- if (format->len > 0 || gui->temp_line != NULL) {
- /* mark format continuing to next line */
- char tmp[2] = { 0, (char)LINE_CMD_FORMAT_CONT };
- linebuf_add(gui, tmp, 2);
- }
- linebuf_add(gui, "\0\200", 2); /* mark EOL */
- }
- gui->eol_marked = FALSE;
-
- line = create_line(gui, 0);
- if (gui->temp_line == NULL ||
- g_list_find(gui->startline, gui->temp_line) != NULL)
- gui_window_newline(gui, visible);
-
- gui->last_subline = 0;
- } else {
- line = gui->temp_line != NULL ? gui->temp_line :
- gui->cur_line != NULL ? gui->cur_line :
- create_line(gui, 0);
- if (line->level == 0) line->level = GPOINTER_TO_INT(level);
- }
+ lineinfo.level = GPOINTER_TO_INT(level);
+ lineinfo.time = time(NULL);
- line_add_colors(gui, fg, bg, flags);
- linebuf_add(gui, str, strlen(str));
- mark_temp_eol(gui->cur_text);
+ view = WINDOW_GUI(window)->view;
+ insert_after = WINDOW_GUI(window)->use_insert_after ?
+ WINDOW_GUI(window)->insert_after : view->buffer->cur_line;
- gui_window_cache_remove(gui, line);
-
- if (gui->temp_line != NULL) {
- /* updating existing line - don't even
- try to print it to screen */
- return;
- }
-
- new_lines = gui_window_get_linecount(gui, line)-1 - gui->last_subline;
-
- for (n = 0; n < new_lines; n++)
- gui_window_newline(gui, visible);
-
- if (visible) {
- /* draw the line to screen. */
- ypos = gui->ypos-new_lines;
- if (new_lines > 0) {
-#ifdef USE_CURSES_WINDOWS
- set_color(gui->parent->curses_win, 0);
- wmove(gui->parent->curses_win, ypos, 0);
- wclrtoeol(gui->parent->curses_win);
-#else
- set_color(stdscr, 0);
- move(ypos + gui->parent->first_line, 0);
- wclrtoeol(stdscr);
-#endif
- }
-
- if (ypos >= 0)
- subline = gui->last_subline;
- else {
- /* *LONG* line - longer than screen height */
- subline = -ypos+gui->last_subline;
- ypos = 0;
- }
- gui_window_line_draw(gui, line, ypos, subline, -1);
- }
-
- gui->last_subline += new_lines;
-}
-
-static void window_clear_screen(GUI_WINDOW_REC *gui)
-{
-#ifdef USE_CURSES_WINDOWS
- wclear(gui->parent->curses_win);
- screen_refresh(gui->parent->curses_win);
-#else
- int n;
-
- for (n = gui->parent->first_line; n < gui->parent->last_line; n++) {
- move(n, 0);
- clrtoeol();
- }
- screen_refresh(NULL);
-#endif
-}
-
-static void window_clear(WINDOW_REC *window)
-{
- GUI_WINDOW_REC *gui = WINDOW_GUI(window);
-
- if (is_window_visible(window))
- window_clear_screen(gui);
-
- gui->ypos = -1;
- gui->bottom_startline = gui->startline = g_list_last(gui->lines);
- gui->bottom_subline = gui->subline = gui->last_subline+1;
- gui->empty_linecount = gui->parent->lines;
- gui->bottom = TRUE;
-}
-
-/* SYNTAX: CLEAR */
-static void cmd_clear(const char *data)
-{
- GHashTable *optlist;
- void *free_arg;
-
- g_return_if_fail(data != NULL);
-
- if (!cmd_get_params(data, &free_arg, PARAM_FLAG_OPTIONS,
- "clear", &optlist)) return;
-
- if (g_hash_table_lookup(optlist, "all") != NULL)
- g_slist_foreach(windows, (GFunc) window_clear, NULL);
- else
- window_clear(active_win);
-
- cmd_params_free(free_arg);
+ if (flags & PRINTFLAG_NEWLINE)
+ view_add_eol(view, &insert_after);
+ line_add_colors(view->buffer, &insert_after, fg, bg, flags);
+ textbuffer_insert(view->buffer, insert_after,
+ str, strlen(str), &lineinfo);
}
static void sig_printtext_finished(WINDOW_REC *window)
{
- GUI_WINDOW_REC *gui;
+ TEXT_BUFFER_VIEW_REC *view;
+ LINE_REC *insert_after;
- gui = WINDOW_GUI(window);
- if (gui->cur_line == NULL)
- return;
+ last_color = 0;
+ last_flags = 0;
- if (format->len > 0) {
- /* save format of the line */
- linebuf_add(gui, format->str, format->len);
+ view = WINDOW_GUI(window)->view;
+ insert_after = WINDOW_GUI(window)->use_insert_after ?
+ WINDOW_GUI(window)->insert_after : view->buffer->cur_line;
- g_string_truncate(format, 0);
- }
-
- linebuf_add(gui, "\0\200", 2); /* mark EOL */
- gui->eol_marked = TRUE;
-
- if (is_window_visible(window)) {
-#ifdef USE_CURSES_WINDOWS
- screen_refresh(gui->parent->curses_win);
-#else
- screen_refresh(NULL);
-#endif
- }
+ view_add_eol(view, &insert_after);
+ remove_old_lines(view);
}
static void sig_print_format(THEME_REC *theme, const char *module,
@@ -589,6 +240,7 @@ static void read_settings(void)
{
scrollback_lines = settings_get_int("scrollback_lines");
scrollback_hours = settings_get_int("scrollback_hours");
+ scrollback_burst_remove = settings_get_int("scrollback_burst_remove");
scrollback_save_formats = settings_get_bool("scrollback_save_formats");
}
@@ -599,6 +251,7 @@ void gui_printtext_init(void)
settings_add_int("history", "scrollback_lines", 500);
settings_add_int("history", "scrollback_hours", 24);
+ settings_add_int("history", "scrollback_burst_remove", 10);
settings_add_bool("history", "scrollback_save_formats", FALSE);
signal_add("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
@@ -606,8 +259,6 @@ void gui_printtext_init(void)
signal_add("print format", (SIGNAL_FUNC) sig_print_format);
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
signal_add("beep", (SIGNAL_FUNC) beep);
- command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear);
- command_set_options("clear", "all");
read_settings();
}
@@ -621,5 +272,4 @@ void gui_printtext_deinit(void)
signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
signal_remove("beep", (SIGNAL_FUNC) beep);
- command_unbind("clear", (SIGNAL_FUNC) cmd_clear);
}
diff --git a/src/fe-text/gui-printtext.h b/src/fe-text/gui-printtext.h
index f30a1fcd..3b2098b7 100644
--- a/src/fe-text/gui-printtext.h
+++ b/src/fe-text/gui-printtext.h
@@ -8,10 +8,6 @@ extern int mirc_colors[];
void gui_printtext_init(void);
void gui_printtext_deinit(void);
-void gui_window_line_append(GUI_WINDOW_REC *gui, const char *str, int len);
-void gui_window_line_remove(WINDOW_REC *window, LINE_REC *line, int redraw);
-void gui_window_line_text_free(GUI_WINDOW_REC *gui, LINE_REC *line);
-
void gui_printtext(int xpos, int ypos, const char *str);
#endif
diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c
index 9f2e22e8..1f54190a 100644
--- a/src/fe-text/gui-readline.c
+++ b/src/fe-text/gui-readline.c
@@ -101,7 +101,7 @@ static int get_scroll_count(void)
count = 1.0/count;
if (*str == '/')
- count = WINDOW_GUI(active_win)->parent->lines/count;
+ count = WINDOW_GUI(active_win)->parent->height/count;
return (int)count;
}
diff --git a/src/fe-text/gui-textwidget.c b/src/fe-text/gui-textwidget.c
deleted file mode 100644
index 7ac2b862..00000000
--- a/src/fe-text/gui-textwidget.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- gui-textwidget.c : irssi
-
- 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*/
-
-#include "module.h"
-#include "signals.h"
-#include "commands.h"
-#include "levels.h"
-
-#include "printtext.h"
-#include "gui-windows.h"
-
-static void cmd_scrollback(gchar *data, SERVER_REC *server, WI_ITEM_REC *item)
-{
- command_runsub("scrollback", data, server, item);
-}
-
-/* SYNTAX: SCROLLBACK CLEAR */
-static void cmd_scrollback_clear(gchar *data)
-{
- gui_window_clear(active_win);
-}
-
-static void scrollback_goto_pos(WINDOW_REC *window, GList *pos)
-{
- GUI_WINDOW_REC *gui;
-
- g_return_if_fail(window != NULL);
- g_return_if_fail(pos != NULL);
-
- gui = WINDOW_GUI(window);
-
- if (g_list_find(gui->bottom_startline, pos->data) == NULL) {
- gui->startline = pos;
- gui->subline = 0;
- gui_window_update_ypos(gui);
- gui->bottom = is_window_bottom(gui);
- } else {
- /* reached the last line */
- if (gui->bottom) return;
-
- gui->startline = gui->bottom_startline;
- gui->subline = gui->bottom_subline;
- gui->ypos = gui->parent->lines-1;
- gui->bottom = TRUE;
- }
-
- if (is_window_visible(window))
- gui_window_redraw(window);
- signal_emit("gui page scrolled", 1, window);
-}
-
-/* SYNTAX: SCROLLBACK GOTO <+|-linecount>|<linenum>|<timestamp> */
-static void cmd_scrollback_goto(gchar *data)
-{
- GList *pos;
- gchar *arg1, *arg2;
- void *free_arg;
- gint lines;
-
- if (!cmd_get_params(data, &free_arg, 2, &arg1, &arg2))
- return;
- if (*arg2 == '\0' && (*arg1 == '-' || *arg1 == '+'))
- {
- /* go forward/backward n lines */
- if (sscanf(arg1 + (*arg1 == '-' ? 0 : 1), "%d", &lines) == 1)
- gui_window_scroll(active_win, lines);
- }
- else if (*arg2 == '\0' && strchr(arg1, ':') == NULL && strchr(arg1, '.') == NULL &&
- sscanf(arg1, "%d", &lines) == 1)
- {
- /* go to n'th line. */
- pos = g_list_nth(WINDOW_GUI(active_win)->lines, lines);
- if (pos != NULL)
- scrollback_goto_pos(active_win, pos);
- }
- else
- {
- struct tm tm;
- time_t stamp;
- gint day, month;
-
- /* [dd.mm | -<days ago>] hh:mi[:ss] */
- stamp = time(NULL);
- if (*arg1 == '-')
- {
- /* -<days ago> */
- if (sscanf(arg1+1, "%d", &day) == 1)
- stamp -= day*3600*24;
- memcpy(&tm, localtime(&stamp), sizeof(struct tm));
- }
- else if (*arg2 != '\0')
- {
- /* dd.mm */
- if (sscanf(arg1, "%d.%d", &day, &month) == 2)
- {
- month--;
- memcpy(&tm, localtime(&stamp), sizeof(struct tm));
-
- if (tm.tm_mon < month)
- tm.tm_year--;
- tm.tm_mon = month;
- tm.tm_mday = day;
- stamp = mktime(&tm);
- }
- }
- else
- {
- /* move time argument to arg2 */
- arg2 = arg1;
- }
-
- /* hh:mi[:ss] */
- memcpy(&tm, localtime(&stamp), sizeof(struct tm));
- tm.tm_sec = 0;
- sscanf(arg2, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
- stamp = mktime(&tm);
-
- if (stamp > time(NULL) && arg1 == arg2) {
- /* we used /SB GOTO 23:59 or something, we want to jump to
- previous day's 23:59 time instead of into future. */
- stamp -= 3600*24;
- }
-
- if (stamp > time(NULL)) {
- /* we're still looking into future, don't bother checking */
- cmd_params_free(free_arg);
- return;
- }
-
- /* find the first line after timestamp */
- for (pos = WINDOW_GUI(active_win)->lines; pos != NULL; pos = pos->next)
- {
- LINE_REC *rec = pos->data;
-
- if (rec->time >= stamp)
- {
- scrollback_goto_pos(active_win, pos);
- break;
- }
- }
- }
- cmd_params_free(free_arg);
-}
-
-/* SYNTAX: SCROLLBACK HOME */
-static void cmd_scrollback_home(const char *data)
-{
- GUI_WINDOW_REC *gui;
-
- gui = WINDOW_GUI(active_win);
-
- if (gui->startline == gui->lines)
- return;
-
- gui->startline = gui->lines;
- gui->subline = 0;
- gui_window_update_ypos(gui);
- gui->bottom = is_window_bottom(gui);
-
- if (is_window_visible(active_win))
- gui_window_redraw(active_win);
- signal_emit("gui page scrolled", 1, active_win);
-}
-
-/* SYNTAX: SCROLLBACK END */
-static void cmd_scrollback_end(const char *data)
-{
- GUI_WINDOW_REC *gui;
-
- gui = WINDOW_GUI(active_win);
- if (gui->bottom)
- return;
-
- gui->startline = gui->bottom_startline;
- gui->subline = gui->bottom_subline;
- gui->ypos = gui->parent->lines-1;
- gui->bottom = TRUE;
-
- if (is_window_visible(active_win))
- gui_window_redraw(active_win);
- signal_emit("gui page scrolled", 1, active_win);
-}
-
-/* SYNTAX: SCROLLBACK REDRAW */
-static void cmd_scrollback_redraw(void)
-{
- GUI_WINDOW_REC *gui;
- GList *tmp, *next;
-
- gui = WINDOW_GUI(active_win);
-
- screen_refresh_freeze();
- for (tmp = gui->lines; tmp != NULL; tmp = next) {
- next = tmp->next;
- gui_window_reformat_line(active_win, tmp->data);
- }
-
- gui_window_redraw(active_win);
- screen_refresh_thaw();
-}
-
-static void cmd_scrollback_status(void)
-{
- GSList *tmp;
- int window_kb, total_lines, total_kb;
-
- total_lines = 0; total_kb = 0;
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *window = tmp->data;
- GUI_WINDOW_REC *gui = WINDOW_GUI(window);
-
- window_kb = g_slist_length(gui->text_chunks)*
- LINE_TEXT_CHUNK_SIZE/1024;
- total_lines += window->lines;
- total_kb += window_kb;
- printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
- "Window %d: %d lines, %dkB of data",
- window->refnum, window->lines, window_kb);
- }
-
- printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
- "Total: %d lines, %dkB of data",
- total_lines, total_kb);
-}
-
-static void sig_away_changed(SERVER_REC *server)
-{
- GSList *tmp;
-
- if (!server->usermode_away)
- return;
-
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
-
- WINDOW_GUI(rec)->lastlog_last_away =
- g_list_last(WINDOW_GUI(rec)->bottom_startline);
- }
-}
-
-void gui_textwidget_init(void)
-{
- command_bind("scrollback", NULL, (SIGNAL_FUNC) cmd_scrollback);
- command_bind("scrollback clear", NULL, (SIGNAL_FUNC) cmd_scrollback_clear);
- command_bind("scrollback goto", NULL, (SIGNAL_FUNC) cmd_scrollback_goto);
- command_bind("scrollback home", NULL, (SIGNAL_FUNC) cmd_scrollback_home);
- command_bind("scrollback end", NULL, (SIGNAL_FUNC) cmd_scrollback_end);
- command_bind("scrollback redraw", NULL, (SIGNAL_FUNC) cmd_scrollback_redraw);
- command_bind("scrollback status", NULL, (SIGNAL_FUNC) cmd_scrollback_status);
-
- signal_add("away mode changed", (SIGNAL_FUNC) sig_away_changed);
-}
-
-void gui_textwidget_deinit(void)
-{
- command_unbind("scrollback", (SIGNAL_FUNC) cmd_scrollback);
- command_unbind("scrollback clear", (SIGNAL_FUNC) cmd_scrollback_clear);
- command_unbind("scrollback goto", (SIGNAL_FUNC) cmd_scrollback_goto);
- command_unbind("scrollback home", (SIGNAL_FUNC) cmd_scrollback_home);
- command_unbind("scrollback end", (SIGNAL_FUNC) cmd_scrollback_end);
- command_unbind("scrollback redraw", (SIGNAL_FUNC) cmd_scrollback_redraw);
- command_unbind("scrollback status", (SIGNAL_FUNC) cmd_scrollback_status);
-
- signal_remove("away mode changed", (SIGNAL_FUNC) sig_away_changed);
-}
diff --git a/src/fe-text/gui-windows.c b/src/fe-text/gui-windows.c
index 835a77cd..57a2b488 100644
--- a/src/fe-text/gui-windows.c
+++ b/src/fe-text/gui-windows.c
@@ -1,7 +1,7 @@
/*
gui-windows.c : irssi
- Copyright (C) 1999 Timo Sirainen
+ 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
@@ -24,66 +24,34 @@
#include "settings.h"
#include "special-vars.h"
-#include "formats.h"
-
#include "screen.h"
#include "gui-entry.h"
#include "gui-windows.h"
#include "gui-printtext.h"
-#ifdef HAVE_REGEX_H
-# include <regex.h>
-#endif
-
-/* 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 int window_create_override;
-static int default_indent_pos;
static char *prompt, *prompt_window;
-static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window, MAIN_WINDOW_REC *parent)
+static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window,
+ MAIN_WINDOW_REC *parent)
{
GUI_WINDOW_REC *gui;
- window->width = COLS;
- window->height = parent->lines;
+ window->width = parent->width;
+ window->height = parent->height;
gui = g_new0(GUI_WINDOW_REC, 1);
gui->parent = parent;
-
- gui->bottom = TRUE;
- gui->line_cache = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
- gui->line_chunk = g_mem_chunk_new("line chunk", sizeof(LINE_REC),
- sizeof(LINE_REC)*100, G_ALLOC_AND_FREE);
- gui->empty_linecount = parent->lines-1;
-
+ gui->view = textbuffer_view_create(textbuffer_create(),
+ window->width, window->height,
+ settings_get_int("indent"));
return gui;
}
-int line_cache_destroy(void *key, LINE_CACHE_REC *cache)
-{
- g_free_not_null(cache->lines);
- g_free(cache);
-
- return TRUE;
-}
-
static void gui_window_deinit(GUI_WINDOW_REC *gui)
{
- g_hash_table_foreach(gui->line_cache, (GHFunc) line_cache_destroy, NULL);
- g_hash_table_destroy(gui->line_cache);
-
- g_slist_foreach(gui->text_chunks, (GFunc) g_free, NULL);
- g_slist_free(gui->text_chunks);
-
- g_mem_chunk_destroy(gui->line_chunk);
- g_list_free(gui->lines);
-
+ textbuffer_view_destroy(gui->view);
g_free(gui);
}
@@ -141,557 +109,32 @@ static void gui_window_destroyed(WINDOW_REC *window)
mainwindow_destroy(parent);
}
-void gui_window_clear(WINDOW_REC *window)
-{
- MAIN_WINDOW_REC *parent;
-
- g_return_if_fail(window != NULL);
-
- parent = WINDOW_GUI(window)->parent;
- gui_window_deinit(WINDOW_GUI(window));
- window->gui_data = gui_window_init(window, parent);
-
- window->lines = 0;
-
- if (is_window_visible(window))
- gui_window_redraw(window);
-}
-
-/* update bottom_startline and bottom_subline of window. */
-static int gui_window_update_bottom(GUI_WINDOW_REC *gui, int lines)
-{
- int linecount, last_linecount;
-
- if (gui->bottom_startline == NULL)
- return -1;
-
- for (; lines < 0; lines++) {
- if (gui->bottom_subline > 0) {
- gui->bottom_subline--;
- continue;
- }
-
- if (gui->bottom_startline->prev == NULL)
- return -1;
- gui->bottom_startline = gui->bottom_startline->prev;
-
- linecount = gui_window_get_linecount(gui, gui->bottom_startline->data);
- gui->bottom_subline = linecount-1;
- }
-
- last_linecount = -1;
- for (; lines > 0; lines--) {
- last_linecount = linecount =
- gui_window_get_linecount(gui, gui->bottom_startline->data);
-
- if (linecount > gui->bottom_subline+1)
- gui->bottom_subline++;
- else {
- gui->bottom_subline = 0;
- if (gui->bottom_startline->next == NULL)
- break;
- gui->bottom_startline = gui->bottom_startline->next;
- }
- }
-
- return last_linecount;
-}
-
-void gui_window_newline(GUI_WINDOW_REC *gui, int visible)
-{
- /* FIXME: I'm pretty sure this could be done cleaner :) */
- int lines;
-
- g_return_if_fail(gui != NULL);
- g_return_if_fail(gui->bottom_startline != NULL);
- g_return_if_fail(gui->startline != NULL);
-
- gui->xpos = 0;
-
- lines = gui_window_get_linecount(gui, gui->bottom_startline->data);
- if (gui->bottom_subline >= lines) {
- /* after screen gets full after /CLEAR we end up here.. */
- gui->bottom_startline = gui->bottom_startline->next;
- gui->bottom_subline = 0;
- }
-
- lines = gui_window_get_linecount(gui, gui->startline->data);
- if (gui->subline >= lines) {
- /* after screen gets full after /CLEAR we end up here.. */
- if (gui->startline->next == NULL)
- g_warning("gui_window_newline(): bug #1");
- else
- gui->startline = gui->startline->next;
- gui->subline = 0;
- }
-
- if (gui->empty_linecount > 0) {
- /* window buffer height isn't even the size of the screen yet */
- gui->empty_linecount--;
- if (!gui->bottom) {
- gui->ypos++;
- return;
- }
- }
-
- if (gui->ypos >= -1 && gui->ypos < gui->parent->lines-1) {
- gui->ypos++;
- return;
- }
-
- if (!gui->bottom || (gui->startline == gui->bottom_startline &&
- gui->subline >= gui->bottom_subline)) {
- lines = gui_window_update_bottom(gui, 1);
-
- if (!gui->bottom) {
- gui->ypos++;
- return;
- }
- } else {
- lines = gui_window_get_linecount(gui, gui->startline->data);
- }
-
- if (lines > 1+gui->subline)
- gui->subline++;
- else {
- if (gui->startline->next == NULL)
- g_warning("gui_window_newline(): bug #2");
- else
- gui->startline = gui->startline->next;
- gui->subline = 0;
- }
-
- if (visible) {
- WINDOW *cwin;
-
-#ifdef USE_CURSES_WINDOWS
- cwin = gui->parent->curses_win;
-#else
- cwin = stdscr;
- setscrreg(gui->parent->first_line, gui->parent->last_line);
-#endif
- scrollok(cwin, TRUE);
- wscrl(cwin, 1);
- scrollok(cwin, FALSE);
- }
-}
-
-static LINE_CACHE_REC *gui_window_line_cache(GUI_WINDOW_REC *gui,
- LINE_REC *line)
-{
- LINE_CACHE_REC *rec;
- LINE_CACHE_SUB_REC *sub;
- GSList *lines;
- unsigned char cmd;
- char *ptr, *last_space_ptr;
- int xpos, pos, indent_pos, last_space, last_color, color;
-
- g_return_val_if_fail(line->text != NULL, NULL);
-
- rec = g_new(LINE_CACHE_REC, 1);
- rec->last_access = time(NULL);
-
- xpos = 0; color = 0; indent_pos = default_indent_pos;
- last_space = last_color = 0; last_space_ptr = NULL; sub = NULL;
-
- rec->count = 1; lines = NULL;
- for (ptr = line->text;;) {
- if (*ptr == '\0') {
- /* command */
- ptr++;
- cmd = *ptr;
- ptr++;
-
- if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT)
- break;
-
- if (cmd == LINE_CMD_CONTINUE) {
- char *tmp;
-
- memcpy(&tmp, ptr, sizeof(char *));
- ptr = tmp;
- continue;
- }
-
- if ((cmd & 0x80) == 0) {
- /* set color */
- color = (color & ATTR_UNDERLINE) | cmd;
- } else switch (cmd) {
- case LINE_CMD_UNDERLINE:
- color ^= ATTR_UNDERLINE;
- break;
- case LINE_CMD_COLOR0:
- color = color & ATTR_UNDERLINE;
- break;
- case LINE_CMD_COLOR8:
- color &= 0xfff0;
- color |= 8|ATTR_COLOR8;
- break;
- case LINE_CMD_BLINK:
- color |= 0x80;
- break;
- case LINE_CMD_INDENT:
- /* set indentation position here - don't do
- it if we're too close to right border */
- if (xpos < COLS-5) indent_pos = xpos;
- break;
- }
- continue;
- }
-
- if (xpos == COLS && sub != NULL &&
- (last_space <= indent_pos || last_space <= 10)) {
- /* long word, remove the indentation from this line */
- xpos -= sub->indent;
- sub->indent = 0;
- }
-
- if (xpos == COLS) {
- xpos = indent_pos;
-
- sub = g_new(LINE_CACHE_SUB_REC, 1);
- if (last_space > indent_pos && last_space > 10) {
- /* go back to last space */
- color = last_color;
- ptr = last_space_ptr;
- while (*ptr == ' ') ptr++;
- } else {
- /* long word, no indentation in next line */
- xpos = 0;
- sub->continues = TRUE;
- }
-
- sub->start = ptr;
- sub->indent = xpos;
- sub->color = color;
-
- lines = g_slist_append(lines, sub);
- rec->count++;
-
- last_space = 0;
- continue;
- }
-
- xpos++;
- if (*ptr++ == ' ') {
- last_space = xpos-1;
- last_space_ptr = ptr;
- last_color = color;
- }
- }
-
- if (rec->count < 2)
- rec->lines = NULL;
- else {
- rec->lines = g_new(LINE_CACHE_SUB_REC, rec->count-1);
- for (pos = 0; lines != NULL; pos++) {
- memcpy(&rec->lines[pos], lines->data, sizeof(LINE_CACHE_SUB_REC));
-
- g_free(lines->data);
- lines = g_slist_remove(lines, lines->data);
- }
- }
-
- g_hash_table_insert(gui->line_cache, line, rec);
- return rec;
-}
-
-void gui_window_cache_remove(GUI_WINDOW_REC *gui, LINE_REC *line)
-{
- LINE_CACHE_REC *cache;
-
- g_return_if_fail(gui != NULL);
- g_return_if_fail(line != NULL);
-
- cache = g_hash_table_lookup(gui->line_cache, line);
- if (cache != NULL) {
- g_hash_table_remove(gui->line_cache, line);
- line_cache_destroy(NULL, cache);
- }
-}
-
-int gui_window_get_linecount(GUI_WINDOW_REC *gui, LINE_REC *line)
-{
- LINE_CACHE_REC *cache;
-
- g_return_val_if_fail(gui != NULL, -1);
- g_return_val_if_fail(line != NULL, -1);
-
- cache = g_hash_table_lookup(gui->line_cache, line);
- if (cache == NULL)
- cache = gui_window_line_cache(gui, line);
- else
- cache->last_access = time(NULL);
-
- return cache->count;
-}
-
-static void single_line_draw(GUI_WINDOW_REC *gui, int ypos,
- LINE_CACHE_SUB_REC *rec, const char *text,
- const char *text_end)
-{
- WINDOW *cwin;
- char *tmp;
- int xpos, color;
-
- if (rec == NULL) {
- xpos = 0; color = 0;
- } else {
- xpos = rec->indent;
- color = rec->color;
- }
-
-#ifdef USE_CURSES_WINDOWS
- cwin = gui->parent->curses_win;
-#else
- cwin = stdscr;
- ypos += gui->parent->first_line;
-#endif
- wmove(cwin, ypos, xpos);
- set_color(cwin, color);
-
- while (text != text_end) {
- if (*text == '\0') {
- /* command */
- text++;
- if ((*text & 0x80) == 0) {
- /* set color */
- color = (color & ATTR_UNDERLINE) | *text;
- } else if (*text == (char) LINE_CMD_CONTINUE) {
- /* jump to next block */
- memcpy(&tmp, text+1, sizeof(char *));
- text = tmp;
- continue;
- } else switch ((unsigned char) *text) {
- case LINE_CMD_EOL:
- case LINE_CMD_FORMAT:
- return;
- case LINE_CMD_UNDERLINE:
- color ^= ATTR_UNDERLINE;
- break;
- case LINE_CMD_COLOR0:
- color = color & ATTR_UNDERLINE;
- break;
- case LINE_CMD_COLOR8:
- color &= 0xfff0;
- color |= 8|ATTR_COLOR8;
- break;
- case LINE_CMD_BLINK:
- color |= 0x80;
- break;
- }
- set_color(cwin, color);
- text++;
- continue;
- }
-
- if (((unsigned char) *text & 127) >= 32)
- waddch(cwin, (unsigned char) *text);
- else {
- /* low-ascii */
- set_color(cwin, ATTR_REVERSE);
- waddch(cwin, (*text & 127)+'A'-1);
- set_color(cwin, color);
- }
- text++;
- }
-}
-
-int gui_window_line_draw(GUI_WINDOW_REC *gui, LINE_REC *line, int ypos, int skip, int max)
-{
- LINE_CACHE_REC *cache;
- LINE_CACHE_SUB_REC *sub;
- char *pos, *next_pos;
- int n;
-
- g_return_val_if_fail(gui != NULL, -1);
- g_return_val_if_fail(line != NULL, -1);
-
- cache = g_hash_table_lookup(gui->line_cache, line);
- if (cache == NULL)
- cache = gui_window_line_cache(gui, line);
- else
- cache->last_access = time(NULL);
-
- if (max < 0) max = cache->count;
-
- for (n = skip; n < cache->count && max > 0; n++, ypos++, max--) {
- sub = n == 0 ? NULL : &cache->lines[n-1];
- pos = sub == NULL ? line->text : sub->start;
- next_pos = (n+1 < cache->count) ?
- cache->lines[n].start : NULL;
-
- single_line_draw(gui, ypos, sub, pos, next_pos);
- }
-
-#ifdef USE_CURSES_WINDOWS
- screen_refresh(gui->parent->curses_win);
-#else
- screen_refresh(NULL);
-#endif
-
- return cache->count;
-}
-
-void gui_window_redraw(WINDOW_REC *window)
+void gui_window_resize(WINDOW_REC *window, int width, int height)
{
GUI_WINDOW_REC *gui;
- WINDOW *cwin;
- GList *line;
- int ypos, lines, skip, max;
-
- g_return_if_fail(window != NULL);
gui = WINDOW_GUI(window);
-#ifdef USE_CURSES_WINDOWS
- cwin = gui->parent->curses_win;
-#else
- cwin = stdscr;
-#endif
-
- /* clear the lines first */
- set_color(cwin, 0);
-#ifdef USE_CURSES_WINDOWS
- for (ypos = 0; ypos <= gui->parent->lines; ypos++) {
-#else
- for (ypos = gui->parent->first_line; ypos <= gui->parent->last_line; ypos++) {
-#endif
- wmove(cwin, ypos, 0);
- wclrtoeol(cwin);
- }
-
- skip = gui->subline;
- ypos = 0;
- for (line = gui->startline; line != NULL; line = line->next) {
- LINE_REC *rec = line->data;
-
- max = gui->parent->lines-1 - ypos+1;
- if (max < 0) break;
-
- lines = gui_window_line_draw(gui, rec, ypos, skip, max);
- ypos += lines-skip;
- skip = 0;
- }
-
- screen_refresh(cwin);
-}
-
-static void gui_window_scroll_up(GUI_WINDOW_REC *gui, int lines)
-{
- LINE_REC *line;
- int count, linecount;
-
- if (gui->startline == NULL)
- return;
-
- count = lines-gui->subline; gui->ypos += gui->subline;
- gui->subline = 0;
-
- while (gui->startline->prev != NULL && count > 0) {
- gui->startline = gui->startline->prev;
-
- line = gui->startline->data;
- linecount = gui_window_get_linecount(gui, line);
- count -= linecount;
- gui->ypos += linecount;
- }
- if (count < 0) {
- gui->subline = -count;
- gui->ypos -= -count;
- }
-
- gui->bottom = is_window_bottom(gui);
-}
-
-#define is_scrolled_bottom(gui) \
- ((gui)->startline == (gui)->bottom_startline && \
- (gui)->subline >= (gui)->bottom_subline)
-
-static void gui_window_scroll_down(GUI_WINDOW_REC *gui, int lines)
-{
- LINE_REC *line;
- int count, linecount;
-
- if (is_scrolled_bottom(gui))
- return;
-
- count = lines+gui->subline; gui->ypos += gui->subline;
- gui->subline = 0;
-
- while (count > 0) {
- line = gui->startline->data;
-
- linecount = gui_window_get_linecount(gui, line);
- count -= linecount;
- gui->ypos -= linecount;
-
- if (gui->startline == gui->bottom_startline &&
- linecount+count > gui->bottom_subline) {
- /* reached the last screenful of text */
- gui->subline = gui->bottom_subline;
- gui->ypos += linecount;
- gui->ypos -= gui->subline;
- break;
- }
-
- if (count == 0) {
- if (gui->startline->next == NULL) {
- gui->subline = linecount;
- break;
- }
- gui->startline = gui->startline->next;
- break;
- }
-
- if (count < 0) {
- gui->subline = linecount+count;
- gui->ypos += -count;
- break;
- }
-
- if (gui->startline->next == NULL) {
- g_warning("gui_window_scroll_down(): bug");
- break;
- }
- gui->startline = gui->startline->next;
- }
-
- gui->bottom = is_window_bottom(gui);
+ window->width = width;
+ window->height = height;
+ textbuffer_view_resize(gui->view, width, height);
}
void gui_window_scroll(WINDOW_REC *window, int lines)
{
- GUI_WINDOW_REC *gui;
-
g_return_if_fail(window != NULL);
- gui = WINDOW_GUI(window);
-
- if (lines < 0) {
- if (gui->startline == NULL || gui->startline->prev == NULL)
- return;
- gui_window_scroll_up(gui, -lines);
- } else {
- if (is_scrolled_bottom(gui))
- return;
- gui_window_scroll_down(gui, lines);
- }
-
- if (is_window_visible(window))
- gui_window_redraw(window);
+ textbuffer_view_scroll(WINDOW_GUI(window)->view, lines);
signal_emit("gui page scrolled", 1, window);
}
-void gui_window_update_ypos(GUI_WINDOW_REC *gui)
+void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line)
{
- GList *tmp;
-
- g_return_if_fail(gui != NULL);
+ g_return_if_fail(window != NULL);
+ g_return_if_fail(line != NULL);
- gui->ypos = -gui->subline-1;
- for (tmp = gui->startline; tmp != NULL; tmp = tmp->next)
- gui->ypos += gui_window_get_linecount(gui, tmp->data);
+ textbuffer_view_scroll_line(WINDOW_GUI(window)->view, line);
+ signal_emit("gui page scrolled", 1, window);
}
void window_update_prompt(void)
@@ -745,16 +188,15 @@ static void window_update_prompt_window_item(WI_ITEM_REC *item)
void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent)
{
MAIN_WINDOW_REC *oldparent;
- int ychange;
oldparent = WINDOW_GUI(window)->parent;
if (oldparent == parent)
return;
WINDOW_GUI(window)->parent = parent;
-
- ychange = parent->lines - oldparent->lines;
- if (ychange != 0) gui_window_resize(window, ychange, FALSE);
+ if (parent->height != oldparent->height ||
+ parent->width != oldparent->width)
+ gui_window_resize(window, parent->width, parent->height);
}
static MAIN_WINDOW_REC *mainwindow_find_unsticky(void)
@@ -772,7 +214,7 @@ static MAIN_WINDOW_REC *mainwindow_find_unsticky(void)
return active_mainwin;
}
-static void signal_window_changed(WINDOW_REC *window)
+static void signal_window_changed(WINDOW_REC *window, WINDOW_REC *old_window)
{
MAIN_WINDOW_REC *parent;
@@ -802,442 +244,13 @@ static void signal_window_changed(WINDOW_REC *window)
}
active_mainwin->active = window;
- screen_refresh_freeze();
- window_update_prompt();
- gui_window_redraw(window);
- screen_refresh_thaw();
-}
-
-void gui_window_line2text(LINE_REC *line, int coloring, GString *str)
-{
- unsigned char cmd;
- 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, *ptr);
- ptr++;
- continue;
- }
-
- ptr++;
- cmd = (unsigned char) *ptr;
- ptr++;
-
- if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) {
- /* end of line */
- break;
- }
-
- if (cmd == LINE_CMD_CONTINUE) {
- /* line continues in another address.. */
- memcpy(&tmp, ptr, sizeof(char *));
- ptr = tmp;
- continue;
- }
-
- if (!coloring) {
- /* no colors, skip coloring commands */
- continue;
- }
-
- if ((cmd & 0x80) == 0) {
- /* set color */
- g_string_sprintfa(str, "\004%c%c",
- (cmd & 0x0f)+'0',
- ((cmd & 0xf0) >> 4)+'0');
- } else switch (cmd) {
- case LINE_CMD_UNDERLINE:
- g_string_append_c(str, 31);
- break;
- case LINE_CMD_COLOR0:
- g_string_sprintfa(str, "\004%c%c",
- '0', FORMAT_COLOR_NOCHANGE);
- break;
- case LINE_CMD_COLOR8:
- g_string_sprintfa(str, "\004%c%c",
- '8', FORMAT_COLOR_NOCHANGE);
- break;
- case LINE_CMD_BLINK:
- g_string_sprintfa(str, "\004%c", FORMAT_STYLE_BLINK);
- break;
- case LINE_CMD_INDENT:
- break;
- }
- }
-}
-
-GList *gui_window_find_text(WINDOW_REC *window, GList *startline,
- int level, int nolevel, const char *text,
- int regexp, int fullword, int case_sensitive)
-{
-#ifdef HAVE_REGEX_H
- regex_t preg;
-#endif
- GList *tmp;
- GList *matches;
- GString *str;
-
- g_return_val_if_fail(window != NULL, NULL);
- g_return_val_if_fail(text != NULL, NULL);
-
- if (regexp) {
-#ifdef HAVE_REGEX_H
- int flags = REG_EXTENDED | REG_NOSUB |
- (case_sensitive ? 0 : REG_ICASE);
- if (regcomp(&preg, text, flags) != 0)
- return NULL;
-#else
- return NULL;
-#endif
- }
-
- matches = NULL;
- str = g_string_new(NULL);
-
- if (startline == NULL)
- startline = WINDOW_GUI(window)->lines;
-
- for (tmp = startline; tmp != NULL; tmp = tmp->next) {
- LINE_REC *rec = tmp->data;
-
- if ((rec->level & level) == 0 || (rec->level & nolevel) != 0)
- continue;
-
- if (*text == '\0') {
- /* no search word, everything matches */
- matches = g_list_append(matches, rec);
- continue;
- }
-
- gui_window_line2text(rec, FALSE, str);
-
- if (
-#ifdef HAVE_REGEX_H
- regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
-#endif
- fullword ? strstr_full_case(str->str, text,
- !case_sensitive) != NULL :
- case_sensitive ? strstr(str->str, text) != NULL :
- stristr(str->str, text) != NULL) {
- /* matched */
- matches = g_list_append(matches, rec);
- }
- }
-#ifdef HAVE_REGEX_H
- if (regexp) regfree(&preg);
-#endif
- g_string_free(str, TRUE);
- return matches;
-}
-
-static void gui_update_bottom_startline(GUI_WINDOW_REC *gui)
-{
- GList *tmp;
- int linecount, total;
-
- if (gui->empty_linecount == 0) {
- /* no empty lines in screen, don't try to keep the old
- bottom startline */
- gui->bottom_startline = NULL;
- }
-
- total = 0;
- for (tmp = g_list_last(gui->lines); tmp != NULL; tmp = tmp->prev) {
- LINE_REC *line = tmp->data;
-
- linecount = gui_window_get_linecount(gui, line);
- if (tmp == gui->bottom_startline) {
- /* keep the old one, make sure that subline is ok */
- if (gui->bottom_subline > linecount+1)
- gui->bottom_subline = linecount+1;
- gui->empty_linecount = gui->parent->lines-total-
- gui->bottom_subline;
- return;
- }
-
- total += linecount;
- if (total >= gui->parent->lines) {
- gui->bottom_startline = tmp;
- gui->bottom_subline = total-gui->parent->lines;
- gui->empty_linecount = 0;
- return;
- }
- }
-
- /* not enough lines so we must be at the beginning of the window */
- gui->bottom_startline = gui->lines;
- gui->bottom_subline = 0;
- gui->empty_linecount = gui->parent->lines-total;
-}
-
-static void gui_window_horiz_resize(WINDOW_REC *window)
-{
- GUI_WINDOW_REC *gui;
- int linecount, diff;
-
- gui = WINDOW_GUI(window);
- if (gui->lines == NULL) return;
-
- g_hash_table_foreach_remove(gui->line_cache, (GHRFunc) line_cache_destroy, NULL);
-
- linecount = gui_window_get_linecount(gui, gui->startline->data);
- if (gui->subline > linecount+1)
- gui->subline = linecount+1;
-
- gui_window_update_ypos(gui);
- gui_update_bottom_startline(gui);
-
- if (gui->bottom) {
- if (g_list_find(gui->startline,
- gui->bottom_startline->data) == NULL ||
- (gui->startline == gui->bottom_startline &&
- gui->subline > gui->bottom_subline)) {
- gui->startline = gui->bottom_startline;
- gui->subline = gui->bottom_subline;
- gui_window_update_ypos(gui);
- } else {
- diff = gui->ypos+1-gui->parent->lines;
- if (diff > 0) gui_window_scroll(window, diff);
- }
- }
-}
-
-void gui_window_resize(WINDOW_REC *window, int ychange, int xchange)
-{
- GUI_WINDOW_REC *gui;
-
- gui = WINDOW_GUI(window);
-
- window->width = COLS;
- window->height = gui->parent->lines;
-
- if (xchange) {
- /* window width changed, we'll need to recalculate a
- few things.. */
- gui_window_horiz_resize(window);
- return;
- }
-
- if (ychange < 0 && gui->empty_linecount > 0) {
- /* empty space at the bottom of the screen - just eat it. */
- gui->empty_linecount += ychange;
- if (gui->empty_linecount >= 0)
- ychange = 0;
- else {
- ychange = gui->empty_linecount;
- gui->empty_linecount = 0;
- }
- }
-
- if (ychange > 0 && gui->bottom && gui->empty_linecount > 0)
- gui->empty_linecount += ychange;
- else {
- gui_window_update_bottom(WINDOW_GUI(window), -ychange);
- gui_window_scroll(window, -ychange);
-
- if (ychange > 0 && gui->bottom &&
- gui->ypos+1 < gui->parent->lines) {
- gui->empty_linecount += gui->parent->lines-gui->ypos-1;
- }
- }
-}
-
-static int window_remove_linecache(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;
- time_t now;
-
- now = time(NULL);
- for (tmp = windows; tmp != NULL; tmp = tmp->next) {
- WINDOW_REC *rec = tmp->data;
-
- g_hash_table_foreach_remove(WINDOW_GUI(rec)->line_cache,
- (GHRFunc) window_remove_linecache,
- &now);
- }
- return 1;
-}
-
-static char *line_read_format(unsigned const char **text)
-{
- GString *str;
- char *ret;
-
- str = g_string_new(NULL);
- for (;;) {
- if (**text == '\0') {
- if ((*text)[1] == LINE_CMD_EOL) {
- /* leave text at \0<eof> */
- break;
- }
- if ((*text)[1] == LINE_CMD_FORMAT_CONT) {
- /* leave text at \0<format_cont> */
- break;
- }
- (*text)++;
-
- if (**text == LINE_CMD_FORMAT) {
- /* move text to start after \0<format> */
- (*text)++;
- break;
- }
-
- if (**text == LINE_CMD_CONTINUE) {
- unsigned char *tmp;
-
- memcpy(&tmp, (*text)+1, sizeof(char *));
- *text = tmp;
- continue;
- } else if (**text & 0x80)
- (*text)++;
- continue;
- }
-
- g_string_append_c(str, (char) **text);
- (*text)++;
- }
-
- ret = str->str;
- g_string_free(str, FALSE);
- return ret;
-}
-
-static char *gui_window_line_get_format(WINDOW_REC *window, LINE_REC *line,
- GString *raw)
-{
- const unsigned char *text;
- char *module, *format_name, *args[MAX_FORMAT_PARAMS], *ret;
- TEXT_DEST_REC dest;
- int formatnum, argcount;
-
- text = (const unsigned char *) line->text;
-
- /* skip the beginning of the line until we find the format */
- g_free(line_read_format(&text));
- if (text[1] == LINE_CMD_FORMAT_CONT) {
- g_string_append_c(raw, '\0');
- g_string_append_c(raw, (char)LINE_CMD_FORMAT_CONT);
- return NULL;
- }
-
- /* read format information */
- module = line_read_format(&text);
- format_name = line_read_format(&text);
+ if (old_window != NULL && !is_window_visible(old_window))
+ textbuffer_view_set_window(WINDOW_GUI(old_window)->view, NULL);
- if (raw != NULL) {
- g_string_append_c(raw, '\0');
- g_string_append_c(raw, (char)LINE_CMD_FORMAT);
+ textbuffer_view_set_window(WINDOW_GUI(window)->view,
+ parent->curses_win);
- g_string_append(raw, module);
-
- g_string_append_c(raw, '\0');
- g_string_append_c(raw, (char)LINE_CMD_FORMAT);
-
- g_string_append(raw, format_name);
- }
-
- formatnum = format_find_tag(module, format_name);
- if (formatnum == -1)
- ret = NULL;
- else {
- argcount = 0;
- memset(args, 0, sizeof(args));
- while (*text != '\0' || text[1] != LINE_CMD_EOL) {
- args[argcount] = line_read_format(&text);
- if (raw != NULL) {
- g_string_append_c(raw, '\0');
- g_string_append_c(raw,
- (char)LINE_CMD_FORMAT);
-
- g_string_append(raw, args[argcount]);
- }
- argcount++;
- }
-
- /* get the format text */
- format_create_dest(&dest, NULL, NULL, line->level, window);
- ret = format_get_text_theme_charargs(current_theme,
- module, &dest,
- formatnum, args);
- while (argcount > 0)
- g_free(args[--argcount]);
- }
-
- g_free(module);
- g_free(format_name);
-
- return ret;
-}
-
-void gui_window_reformat_line(WINDOW_REC *window, LINE_REC *line)
-{
- GUI_WINDOW_REC *gui;
- TEXT_DEST_REC dest;
- GString *raw;
- char *str, *tmp, *prestr, *linestart, *leveltag;
-
- gui = WINDOW_GUI(window);
-
- raw = g_string_new(NULL);
- str = gui_window_line_get_format(window, line, raw);
-
- if (str == NULL && raw->len == 2 &&
- raw->str[1] == (char)LINE_CMD_FORMAT_CONT) {
- /* multiline format, format explained in one the
- following lines. remove this line. */
- gui_window_line_remove(window, line, FALSE);
- } else if (str != NULL) {
- /* FIXME: ugly ugly .. and this can't handle
- non-formatted lines.. */
- g_string_append_c(raw, '\0');
- g_string_append_c(raw, (char)LINE_CMD_EOL);
-
- gui_window_line_text_free(gui, line);
-
- gui->temp_line = line;
- gui->temp_line->text = gui->cur_text->buffer+gui->cur_text->pos;
- gui->cur_text->lines++;
- gui->eol_marked = FALSE;
-
- format_create_dest(&dest, NULL, NULL, line->level, window);
-
- linestart = format_get_line_start(current_theme, &dest, line->time);
- leveltag = format_get_level_tag(current_theme, &dest);
-
- prestr = g_strconcat(linestart == NULL ? "" : linestart,
- leveltag, NULL);
- g_free_not_null(linestart);
- g_free_not_null(leveltag);
-
- tmp = format_add_linestart(str, prestr);
- g_free(str);
- g_free(prestr);
-
- format_send_to_gui(&dest, tmp);
- g_free(tmp);
-
- gui_window_line_append(gui, raw->str, raw->len);
-
- gui->eol_marked = TRUE;
- gui->temp_line = NULL;
- }
- g_string_free(raw, TRUE);
+ window_update_prompt();
}
static void sig_check_window_update(WINDOW_REC *window)
@@ -1248,6 +261,8 @@ static void sig_check_window_update(WINDOW_REC *window)
static void read_settings(void)
{
+ GSList *tmp;
+
SIGNAL_FUNC funcs[] = {
(SIGNAL_FUNC) window_update_prompt,
(SIGNAL_FUNC) window_update_prompt_server,
@@ -1255,8 +270,6 @@ static void read_settings(void)
(SIGNAL_FUNC) window_update_prompt_window_item
};
- default_indent_pos = settings_get_int("indent");
-
if (prompt != NULL) {
special_vars_remove_signals(prompt, 4, funcs);
special_vars_remove_signals(prompt_window, 4, funcs);
@@ -1266,6 +279,13 @@ static void read_settings(void)
prompt = g_strdup(settings_get_str("prompt"));
prompt_window = g_strdup(settings_get_str("prompt_window"));
+ for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *rec = tmp->data;
+
+ textbuffer_view_set_default_indent(WINDOW_GUI(rec)->view,
+ settings_get_int("indent"));
+ }
+
special_vars_add_signals(prompt, 4, funcs);
special_vars_add_signals(prompt_window, 4, funcs);
@@ -1281,7 +301,6 @@ void gui_windows_init(void)
prompt = NULL; prompt_window = NULL;
window_create_override = -1;
- linecache_tag = g_timeout_add(LINE_CACHE_CHECK_TIME, (GSourceFunc) sig_check_linecache, NULL);
read_settings();
signal_add("gui window create override", (SIGNAL_FUNC) sig_window_create_override);
@@ -1294,7 +313,6 @@ void gui_windows_init(void)
void gui_windows_deinit(void)
{
- g_source_remove(linecache_tag);
g_free_not_null(prompt);
g_free_not_null(prompt_window);
diff --git a/src/fe-text/gui-windows.h b/src/fe-text/gui-windows.h
index 9fef4521..fb94b46f 100644
--- a/src/fe-text/gui-windows.h
+++ b/src/fe-text/gui-windows.h
@@ -1,94 +1,20 @@
#ifndef __GUI_WINDOWS_H
#define __GUI_WINDOWS_H
-#include "servers.h"
#include "mainwindows.h"
+#include "textbuffer-view.h"
#define WINDOW_GUI(a) ((GUI_WINDOW_REC *) ((a)->gui_data))
#define is_window_visible(win) \
(WINDOW_GUI(win)->parent->active == (win))
-#define LINE_TEXT_CHUNK_SIZE 2048
-
-/* 7 first bits of LINE_REC's info byte. */
-enum {
- LINE_CMD_EOL=0x80, /* line ends here. */
- LINE_CMD_CONTINUE, /* line continues in next block */
- LINE_CMD_COLOR0, /* change to black, would be same as \0\0 but it breaks things.. */
- LINE_CMD_COLOR8, /* change to dark grey, normally 8 = bold black */
- LINE_CMD_UNDERLINE, /* enable/disable underlining */
- LINE_CMD_INDENT, /* if line is split, indent it at this position */
- LINE_CMD_BLINK, /* blinking background */
- LINE_CMD_FORMAT, /* end of line, but next will come the format that was used to create the
- text in format <module><format_name><arg><arg2...> - fields are separated
- with \0<format> and last argument ends with \0<eol>. \0<continue> is allowed
- anywhere */
- LINE_CMD_FORMAT_CONT /* multiline format, continues to next line */
-};
-
-typedef struct {
- char *start;
- int indent;
- int color;
- unsigned int continues:1; /* first word in line belong to the end of the last word in previous line */
-} LINE_CACHE_SUB_REC;
-
-typedef struct {
- time_t last_access;
-
- int count; /* number of real lines */
- LINE_CACHE_SUB_REC *lines;
-} LINE_CACHE_REC;
-
-typedef struct {
- /* text in the line. \0 means that the next char will be a
- color or command. <= 127 = color or if 8.bit is set, the
- first 7 bits are the command. See LINE_CMD_xxxx.
-
- DO NOT ADD BLACK WITH \0\0 - this will break things. Use
- LINE_CMD_COLOR0 instead. */
- char *text;
-
- int level;
- time_t time;
-} LINE_REC;
-
-typedef struct {
- char buffer[LINE_TEXT_CHUNK_SIZE];
- int pos;
- int lines;
-} TEXT_CHUNK_REC;
-
typedef struct {
MAIN_WINDOW_REC *parent;
+ TEXT_BUFFER_VIEW_REC *view;
- GMemChunk *line_chunk;
- GSList *text_chunks;
- GList *lines;
- GHashTable *line_cache;
-
- LINE_REC *cur_line, *temp_line;
- TEXT_CHUNK_REC *cur_text;
-
- int xpos, ypos; /* cursor position in screen */
- GList *startline; /* line at the top of the screen */
- int subline; /* number of "real lines" to skip from `startline' */
-
- GList *bottom_startline; /* marks the bottom of the text buffer */
- int bottom_subline;
- int empty_linecount; /* how many empty lines are in screen.
- a screenful when started or used /CLEAR */
- unsigned int bottom:1; /* window is at the bottom of the text buffer */
- unsigned int eol_marked:1; /* last line marked for eol */
-
- /* For /LAST -new and -away */
- GList *lastlog_last_check;
- GList *lastlog_last_away;
-
- /* for gui-printtext.c */
- int last_subline;
- int last_color, last_flags;
+ unsigned int use_insert_after:1;
+ LINE_REC *insert_after;
} GUI_WINDOW_REC;
void gui_windows_init(void);
@@ -96,30 +22,15 @@ void gui_windows_deinit(void);
WINDOW_REC *gui_window_create(MAIN_WINDOW_REC *parent);
-void gui_window_set_server(WINDOW_REC *window, SERVER_REC *server);
-
-void gui_window_line2text(LINE_REC *line, int coloring, GString *str);
-GList *gui_window_find_text(WINDOW_REC *window, GList *startline,
- int level, int nolevel, const char *text,
- int regexp, int fullword, int case_sensitive);
-
-/* get number of real lines that line record takes */
-int gui_window_get_linecount(GUI_WINDOW_REC *gui, LINE_REC *line);
-void gui_window_cache_remove(GUI_WINDOW_REC *gui, LINE_REC *line);
-int gui_window_line_draw(GUI_WINDOW_REC *gui, LINE_REC *line, int ypos, int skip, int max);
-
-void gui_window_clear(WINDOW_REC *window);
-void gui_window_redraw(WINDOW_REC *window);
-void gui_window_reformat_line(WINDOW_REC *window, LINE_REC *line);
-void gui_window_resize(WINDOW_REC *window, int ychange, int xchange);
+void gui_window_resize(WINDOW_REC *window, int width, int height);
void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent);
-#define is_window_bottom(gui) \
- ((gui)->ypos >= -1 && (gui)->ypos <= (gui)->parent->last_line-(gui)->parent->first_line)
+#define gui_window_redraw(window) \
+ textbuffer_view_redraw(WINDOW_GUI(window)->view)
-void window_update_prompt(void);
-void gui_window_newline(GUI_WINDOW_REC *gui, int visible);
void gui_window_scroll(WINDOW_REC *window, int lines);
-void gui_window_update_ypos(GUI_WINDOW_REC *gui);
+void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line);
+
+void window_update_prompt(void);
#endif
diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c
index 80fbed21..dfae17c5 100644
--- a/src/fe-text/irssi.c
+++ b/src/fe-text/irssi.c
@@ -55,8 +55,8 @@ void irc_deinit(void);
void gui_expandos_init(void);
void gui_expandos_deinit(void);
-void gui_textwidget_init(void);
-void gui_textwidget_deinit(void);
+void textbuffer_commands_init(void);
+void textbuffer_commands_deinit(void);
void lastlog_init(void);
void lastlog_deinit(void);
@@ -124,11 +124,13 @@ static void textui_finish_init(void)
quitting = FALSE;
screen_refresh_freeze();
+ textbuffer_init();
+ textbuffer_view_init();
+ textbuffer_commands_init();
gui_entry_init();
gui_expandos_init();
gui_printtext_init();
gui_readline_init();
- gui_textwidget_init();
lastlog_init();
mainwindows_init();
mainwindow_activity_init();
@@ -165,7 +167,6 @@ static void textui_deinit(void)
signal_remove("gui exit", (SIGNAL_FUNC) sig_exit);
- gui_textwidget_deinit();
lastlog_deinit();
statusbar_deinit();
gui_printtext_deinit();
@@ -176,6 +177,9 @@ static void textui_deinit(void)
mainwindows_deinit();
gui_expandos_deinit();
gui_entry_deinit();
+ textbuffer_commands_deinit();
+ textbuffer_view_deinit();
+ textbuffer_deinit();
screen_refresh_thaw();
deinit_screen();
diff --git a/src/fe-text/lastlog.c b/src/fe-text/lastlog.c
index 11addf29..4178764c 100644
--- a/src/fe-text/lastlog.c
+++ b/src/fe-text/lastlog.c
@@ -35,16 +35,20 @@
static void window_lastlog_clear(WINDOW_REC *window)
{
+ TEXT_BUFFER_VIEW_REC *view;
GList *tmp, *next;
- for (tmp = WINDOW_GUI(window)->lines; tmp != NULL; tmp = next) {
+ screen_refresh_freeze();
+ view = WINDOW_GUI(window)->view;
+ for (tmp = textbuffer_view_get_lines(view); tmp != NULL; tmp = next) {
LINE_REC *line = tmp->data;
next = tmp->next;
- if (line->level & MSGLEVEL_LASTLOG)
- gui_window_line_remove(window, line, FALSE);
+ if (line->info.level & MSGLEVEL_LASTLOG)
+ textbuffer_view_remove_line(view, line);
}
- gui_window_redraw(window);
+ screen_refresh_thaw();
+ //gui_window_redraw(window);
}
/* Only unknown keys in `optlist' should be levels.
@@ -89,8 +93,9 @@ int cmd_options_get_level(const char *cmd, GHashTable *optlist)
static void show_lastlog(const char *searchtext, GHashTable *optlist,
int start, int count, int fhandle)
{
- WINDOW_REC *window;
- GList *startline, *list, *tmp;
+ WINDOW_REC *window;
+ LINE_REC *startline;
+ GList *list, *tmp;
GString *line;
char *str;
int level, len;
@@ -120,14 +125,18 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
}
if (g_hash_table_lookup(optlist, "new") != NULL)
- startline = WINDOW_GUI(window)->lastlog_last_check;
+ startline = textbuffer_view_get_bookmark(WINDOW_GUI(window)->view, "lastlog_last_check");
else if (g_hash_table_lookup(optlist, "away") != NULL)
- startline = WINDOW_GUI(window)->lastlog_last_away;
+ startline = textbuffer_view_get_bookmark(WINDOW_GUI(window)->view, "lastlog_last_away");
else
startline = NULL;
- if (startline == NULL) startline = WINDOW_GUI(window)->lines;
- list = gui_window_find_text(window, startline,
+ if (startline == NULL) {
+ list = textbuffer_view_get_lines(WINDOW_GUI(window)->view);
+ startline = list == NULL ? NULL : list->data;
+ }
+
+ list = textbuffer_find_text(WINDOW_GUI(window)->view->buffer, startline,
level, MSGLEVEL_LASTLOG,
searchtext,
g_hash_table_lookup(optlist, "regexp") != NULL,
@@ -163,9 +172,9 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
LINE_REC *rec = tmp->data;
/* get the line text */
- gui_window_line2text(rec, fhandle == -1, line);
+ textbuffer_line2text(rec, fhandle == -1, line);
if (!settings_get_bool("timestamps")) {
- struct tm *tm = localtime(&rec->time);
+ struct tm *tm = localtime(&rec->info.time);
char timestamp[10];
g_snprintf(timestamp, sizeof(timestamp),
@@ -191,9 +200,10 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
if (fhandle == -1 && g_hash_table_lookup(optlist, "-") == NULL)
printformat(NULL, NULL, MSGLEVEL_LASTLOG, TXT_LASTLOG_END);
- WINDOW_GUI(window)->lastlog_last_check =
- g_list_last(WINDOW_GUI(window)->bottom_startline);
+ textbuffer_view_set_bookmark_bottom(WINDOW_GUI(window)->view,
+ "lastlog_last_check");
+ textbuffer_line_unref_list(WINDOW_GUI(window)->view->buffer, list);
g_list_free(list);
}
diff --git a/src/fe-text/mainwindows-save.c b/src/fe-text/mainwindows-save.c
index 51d4ba34..113037b1 100644
--- a/src/fe-text/mainwindows-save.c
+++ b/src/fe-text/mainwindows-save.c
@@ -37,7 +37,7 @@ static void main_window_save(MAIN_WINDOW_REC *window, CONFIG_NODE *node)
node = config_node_section(node, num, NODE_TYPE_BLOCK);
iconfig_node_set_int(node, "first_line", window->first_line);
- iconfig_node_set_int(node, "lines", window->lines);
+ iconfig_node_set_int(node, "lines", window->height);
str = g_string_new(NULL);
for (tmp = window->sticky_windows; tmp != NULL; tmp = tmp->next) {
@@ -141,7 +141,7 @@ static void sig_windows_restored(void)
active_mainwin = NULL;
window_set_active(window);
- if (lowerwin->lines > WINDOW_MIN_SIZE)
+ if (lowerwin->height > WINDOW_MIN_SIZE)
mainwindow_set_size(lowerwin, WINDOW_MIN_SIZE);
count--;
diff --git a/src/fe-text/mainwindows.c b/src/fe-text/mainwindows.c
index 479df26b..751d9d9e 100644
--- a/src/fe-text/mainwindows.c
+++ b/src/fe-text/mainwindows.c
@@ -36,6 +36,7 @@ GSList *mainwindows;
MAIN_WINDOW_REC *active_mainwin;
static int reserved_up, reserved_down;
+static int screen_width, screen_height;
static MAIN_WINDOW_REC *find_window_with_room(void)
{
@@ -47,7 +48,7 @@ static MAIN_WINDOW_REC *find_window_with_room(void)
for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
MAIN_WINDOW_REC *rec = tmp->data;
- space = rec->lines;
+ space = rec->height;
if (space >= WINDOW_MIN_SIZE+NEW_WINDOW_SIZE && space > biggest) {
biggest = space;
biggest_rec = rec;
@@ -60,21 +61,24 @@ static MAIN_WINDOW_REC *find_window_with_room(void)
#ifdef USE_CURSES_WINDOWS
static void create_curses_window(MAIN_WINDOW_REC *window)
{
- window->curses_win = newwin(window->lines, COLS, window->first_line, 0);
+ window->curses_win = newwin(window->height, window->width,
+ window->first_line, 0);
idlok(window->curses_win, 1);
}
#endif
-static void mainwindow_resize(MAIN_WINDOW_REC *window, int ychange, int xchange)
+static void mainwindow_resize(MAIN_WINDOW_REC *window, int xdiff, int ydiff)
{
GSList *tmp;
- if (ychange == 0 && !xchange) return;
+ if (xdiff == 0 && ydiff == 0)
+ return;
- window->lines = window->last_line-window->first_line+1;
+ window->width += xdiff;
+ window->height = window->last_line-window->first_line+1;
#ifdef USE_CURSES_WINDOWS
#ifdef HAVE_CURSES_WRESIZE
- wresize(window->curses_win, window->lines, COLS);
+ wresize(window->curses_win, window->height, window->width);
mvwin(window->curses_win, window->first_line, 0);
#else
delwin(window->curses_win);
@@ -87,10 +91,11 @@ static void mainwindow_resize(MAIN_WINDOW_REC *window, int ychange, int xchange)
if (rec->gui_data != NULL &&
WINDOW_GUI(rec)->parent == window)
- gui_window_resize(rec, ychange, xchange);
+ gui_window_resize(rec, window->width, window->height);
}
- gui_window_redraw(window->active);
+ textbuffer_view_set_window(WINDOW_GUI(window->active)->view,
+ window->curses_win);
signal_emit("mainwindow resized", 1, window);
}
@@ -104,7 +109,8 @@ void mainwindows_recreate(void)
#ifdef USE_CURSES_WINDOWS
create_curses_window(rec);
#endif
- gui_window_redraw(rec->active);
+ textbuffer_view_set_window(WINDOW_GUI(rec->active)->view,
+ rec->curses_win);
}
}
@@ -120,27 +126,29 @@ MAIN_WINDOW_REC *mainwindow_create(void)
active_mainwin = rec;
rec->first_line = reserved_up;
- rec->last_line = LINES-1-reserved_down-rec->statusbar_lines;
- rec->lines = rec->last_line-rec->first_line+1;
+ rec->last_line = screen_height-1 -
+ reserved_down-rec->statusbar_lines;
+ rec->width = screen_width;
+ rec->height = rec->last_line-rec->first_line+1;
} else {
parent = WINDOW_GUI(active_win)->parent;
- if (parent->lines < WINDOW_MIN_SIZE+NEW_WINDOW_SIZE)
+ if (parent->height < WINDOW_MIN_SIZE+NEW_WINDOW_SIZE)
parent = find_window_with_room();
if (parent == NULL)
return NULL; /* not enough space */
- space = (parent->lines-parent->statusbar_lines)/2;
+ space = (parent->height-parent->statusbar_lines)/2;
rec->first_line = parent->first_line;
rec->last_line = rec->first_line + space-rec->statusbar_lines;
- rec->lines = rec->last_line-rec->first_line+1;
+ rec->height = rec->last_line-rec->first_line+1;
parent->first_line = rec->last_line+1+rec->statusbar_lines;
- parent->lines = parent->last_line-parent->first_line+1;
+ parent->height = parent->last_line-parent->first_line+1;
- mainwindow_resize(parent, -space-1, FALSE);
+ mainwindow_resize(parent, 0, -space-1);
}
#ifdef USE_CURSES_WINDOWS
- rec->curses_win = newwin(rec->lines, COLS, rec->first_line, 0);
+ rec->curses_win = newwin(rec->height, rec->width, rec->first_line, 0);
refresh();
#endif
@@ -196,14 +204,14 @@ static void mainwindows_add_space(int first_line, int last_line)
rec = mainwindows_find_lower(last_line);
if (rec != NULL) {
rec->first_line = first_line;
- mainwindow_resize(rec, size, FALSE);
+ mainwindow_resize(rec, 0, size);
return;
}
rec = mainwindows_find_upper(first_line);
if (rec != NULL) {
rec->last_line = last_line-rec->statusbar_lines;
- mainwindow_resize(rec, size, FALSE);
+ mainwindow_resize(rec, 0, size);
}
}
@@ -280,7 +288,7 @@ GSList *mainwindows_get_sorted(int reverse)
return list;
}
-static void mainwindows_resize_too_small(int ychange, int xchange)
+static void mainwindows_resize_too_small(int xdiff, int ydiff)
{
GSList *sorted, *tmp;
int space, moved;
@@ -291,8 +299,8 @@ static void mainwindows_resize_too_small(int ychange, int xchange)
for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
MAIN_WINDOW_REC *rec = tmp->data;
- space = rec->lines;
- if (ychange == 0 || space <= 0) {
+ space = rec->height;
+ if (ydiff == 0 || space <= 0) {
if (moved > 0) {
rec->first_line -= moved;
rec->last_line -= moved;
@@ -301,17 +309,17 @@ static void mainwindows_resize_too_small(int ychange, int xchange)
continue;
}
- if (space > -ychange) space = -ychange;
- ychange += space;
+ if (space > -ydiff) space = -ydiff;
+ ydiff += space;
rec->first_line -= moved;
moved += space;
rec->last_line -= space;
- mainwindow_resize(rec, -space, xchange);
+ mainwindow_resize(rec, xdiff, -space);
}
g_slist_free(sorted);
}
-static void mainwindows_resize_smaller(int ychange, int xchange)
+static void mainwindows_resize_smaller(int xdiff, int ydiff)
{
GSList *sorted, *tmp;
int space;
@@ -320,40 +328,40 @@ static void mainwindows_resize_smaller(int ychange, int xchange)
for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
MAIN_WINDOW_REC *rec = tmp->data;
- space += rec->lines-WINDOW_MIN_SIZE;
+ space += rec->height-WINDOW_MIN_SIZE;
}
- if (space < -ychange) {
+ if (space < -ydiff) {
/* not enough space, use different algorithm */
- mainwindows_resize_too_small(ychange, xchange);
+ mainwindows_resize_too_small(xdiff, ydiff);
return;
}
/* resize windows that have space */
sorted = mainwindows_get_sorted(TRUE);
- for (tmp = sorted; tmp != NULL && ychange < 0; tmp = tmp->next) {
+ for (tmp = sorted; tmp != NULL && ydiff < 0; tmp = tmp->next) {
MAIN_WINDOW_REC *rec = tmp->data;
- space = rec->lines-WINDOW_MIN_SIZE;
+ space = rec->height-WINDOW_MIN_SIZE;
if (space <= 0) {
- rec->first_line += ychange;
- rec->last_line += ychange;
+ rec->first_line += ydiff;
+ rec->last_line += ydiff;
signal_emit("mainwindow moved", 1, rec);
continue;
}
if (space <= 0) space = 1;
- if (space > -ychange) space = -ychange;
- rec->last_line += ychange;
- ychange += space;
- rec->first_line += ychange;
+ if (space > -ydiff) space = -ydiff;
+ rec->last_line += ydiff;
+ ydiff += space;
+ rec->first_line += ydiff;
- mainwindow_resize(rec, -space, xchange);
+ mainwindow_resize(rec, xdiff, -space);
}
g_slist_free(sorted);
}
-static void mainwindows_resize_bigger(int ychange, int xchange)
+static void mainwindows_resize_bigger(int xdiff, int ydiff)
{
GSList *sorted, *tmp;
int moved, space;
@@ -363,8 +371,8 @@ static void mainwindows_resize_bigger(int ychange, int xchange)
for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
MAIN_WINDOW_REC *rec = tmp->data;
- space = rec->lines-WINDOW_MIN_SIZE;
- if (ychange == 0 || (space >= 0 && tmp->next != NULL)) {
+ space = rec->height-WINDOW_MIN_SIZE;
+ if (ydiff == 0 || (space >= 0 && tmp->next != NULL)) {
if (moved > 0) {
rec->first_line += moved;
rec->last_line += moved;
@@ -376,41 +384,48 @@ static void mainwindows_resize_bigger(int ychange, int xchange)
if (space < 0 && tmp->next != NULL) {
/* space below minimum */
space = -space;
- if (space > ychange) space = ychange;
+ if (space > ydiff) space = ydiff;
} else {
/* lowest window - give all the extra space for it */
- space = ychange;
+ space = ydiff;
}
- ychange -= space;
+ ydiff -= space;
rec->first_line += moved;
moved += space;
rec->last_line += moved;
- mainwindow_resize(rec, space, xchange);
+ mainwindow_resize(rec, xdiff, space);
}
g_slist_free(sorted);
}
-void mainwindows_resize_horiz(int xchange)
+void mainwindows_resize_horiz(int xdiff)
{
GSList *tmp;
for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
MAIN_WINDOW_REC *rec = tmp->data;
- mainwindow_resize(rec, 0, xchange);
+ mainwindow_resize(rec, xdiff, 0);
}
}
-void mainwindows_resize(int ychange, int xchange)
+void mainwindows_resize(int width, int height)
{
+ int xdiff, ydiff;
+
+ xdiff = width-screen_width;
+ ydiff = height-screen_height;
+ screen_width = width;
+ screen_height = height;
+
screen_refresh_freeze();
- if (ychange < 0)
- mainwindows_resize_smaller(ychange, xchange);
- else if (ychange > 0)
- mainwindows_resize_bigger(ychange, xchange);
- else if (xchange != 0)
- mainwindows_resize_horiz(xchange);
+ if (ydiff < 0)
+ mainwindows_resize_smaller(xdiff, ydiff);
+ else if (ydiff > 0)
+ mainwindows_resize_bigger(xdiff, ydiff);
+ else if (xdiff != 0)
+ mainwindows_resize_horiz(xdiff);
irssi_redraw();
screen_refresh_thaw();
@@ -435,7 +450,7 @@ int mainwindows_reserve_lines(int count, int up)
ret = reserved_down;
reserved_down += count;
- window = mainwindows_find_upper(LINES);
+ window = mainwindows_find_upper(screen_height);
if (window != NULL) window->last_line -= count;
}
@@ -462,14 +477,14 @@ static int mainwindow_grow(MAIN_WINDOW_REC *window, int count)
/* shrink lower window */
shrink_win = mainwindows_find_lower(window->last_line);
- if (shrink_win != NULL && shrink_win->lines-count >= WINDOW_MIN_SIZE) {
+ if (shrink_win != NULL && shrink_win->height-count >= WINDOW_MIN_SIZE) {
window->last_line += count;
shrink_win->first_line += count;
} else {
/* shrink upper window */
shrink_win = mainwindows_find_upper(window->first_line);
if (shrink_win == NULL ||
- shrink_win->lines-count < WINDOW_MIN_SIZE)
+ shrink_win->height-count < WINDOW_MIN_SIZE)
return FALSE;
window->first_line -= count;
@@ -484,7 +499,7 @@ static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count)
{
MAIN_WINDOW_REC *grow_win;
- if (window->lines-count < WINDOW_MIN_SIZE)
+ if (window->height-count < WINDOW_MIN_SIZE)
return FALSE;
grow_win = mainwindows_find_lower(window->last_line);
@@ -505,7 +520,7 @@ static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count)
void mainwindow_set_size(MAIN_WINDOW_REC *window, int size)
{
- size -= window->lines;
+ size -= window->height;
if (size < 0)
mainwindow_shrink(window, size);
else
@@ -551,7 +566,7 @@ static void cmd_window_size(const char *data)
if (!is_numeric(data, 0)) return;
size = atoi(data);
- size -= WINDOW_GUI(active_win)->parent->lines;
+ size -= WINDOW_GUI(active_win)->parent->height;
if (size == 0) return;
ltoa(sizestr, size < 0 ? -size : size);
@@ -571,7 +586,7 @@ static void cmd_window_balance(void)
windows = g_slist_length(mainwindows);
if (windows == 1) return;
- avail_size = LINES-reserved_up-reserved_down;
+ avail_size = screen_height - reserved_up-reserved_down;
unit_size = avail_size/windows;
bigger_units = avail_size%windows;
@@ -580,11 +595,11 @@ static void cmd_window_balance(void)
for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
MAIN_WINDOW_REC *rec = tmp->data;
- old_size = rec->lines;
+ old_size = rec->height;
rec->first_line = last_line+1;
rec->last_line = rec->first_line-1 + unit_size -
rec->statusbar_lines;
- rec->lines = rec->last_line-rec->first_line+1;
+ rec->height = rec->last_line-rec->first_line+1;
if (bigger_units > 0) {
rec->last_line++;
@@ -592,7 +607,7 @@ static void cmd_window_balance(void)
}
last_line = rec->last_line + rec->statusbar_lines;
- mainwindow_resize(rec, rec->lines-old_size, FALSE);
+ mainwindow_resize(rec, rec->height-old_size, FALSE);
}
g_slist_free(sorted);
@@ -847,6 +862,9 @@ static void cmd_window_stick(const char *data)
void mainwindows_init(void)
{
+ screen_width = COLS;
+ screen_height = LINES;
+
mainwindows = NULL;
active_mainwin = NULL;
reserved_up = reserved_down = 0;
diff --git a/src/fe-text/mainwindows.h b/src/fe-text/mainwindows.h
index 88ff97e0..491449c6 100644
--- a/src/fe-text/mainwindows.h
+++ b/src/fe-text/mainwindows.h
@@ -12,8 +12,10 @@ typedef struct {
#ifdef USE_CURSES_WINDOWS
WINDOW *curses_win;
+#else
+#error disable-curses-windows is currently broken /* FIXME */
#endif
- int first_line, last_line, lines;
+ int first_line, last_line, width, height;
int statusbar_lines;
void *statusbar;
void *statusbar_window_item;
@@ -32,7 +34,7 @@ void mainwindows_redraw(void);
void mainwindows_recreate(void);
void mainwindow_set_size(MAIN_WINDOW_REC *window, int size);
-void mainwindows_resize(int ychange, int xchange);
+void mainwindows_resize(int width, int height);
int mainwindows_reserve_lines(int count, int up);
GSList *mainwindows_get_sorted(int reverse);
diff --git a/src/fe-text/screen.c b/src/fe-text/screen.c
index 86681d78..ba1651e3 100644
--- a/src/fe-text/screen.c
+++ b/src/fe-text/screen.c
@@ -49,8 +49,6 @@ static void deinit_screen_int(void);
static void sig_winch(int p)
{
- int ychange, xchange;
-
#if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM)
struct winsize ws;
@@ -67,24 +65,14 @@ static void sig_winch(int p)
ws.ws_col = MIN_SCREEN_WIDTH;
/* Resize curses terminal */
- ychange = ws.ws_row-LINES;
- xchange = ws.ws_col-COLS;
resizeterm(ws.ws_row, ws.ws_col);
#else
- int old_lines, old_cols;
-
- old_lines = LINES;
- old_cols = COLS;
-
deinit_screen_int();
init_screen_int();
mainwindows_recreate();
-
- ychange = LINES-old_lines;
- xchange = COLS-old_cols;
#endif
- mainwindows_resize(ychange, xchange != 0);
+ mainwindows_resize(COLS, LINES);
}
#endif
diff --git a/src/fe-text/statusbar-items.c b/src/fe-text/statusbar-items.c
index a11b6bbb..3b21d11b 100644
--- a/src/fe-text/statusbar-items.c
+++ b/src/fe-text/statusbar-items.c
@@ -350,7 +350,7 @@ static void sig_statusbar_more_check_remove(WINDOW_REC *window)
if (!is_window_visible(window))
return;
- if (more_item != NULL && WINDOW_GUI(window)->bottom) {
+ if (more_item != NULL && WINDOW_GUI(window)->view->bottom) {
statusbar_item_remove(more_item);
more_item = NULL;
}
@@ -361,7 +361,7 @@ static void sig_statusbar_more_check(WINDOW_REC *window)
if (window == NULL || !is_window_visible(window))
return;
- if (!WINDOW_GUI(window)->bottom) {
+ if (!WINDOW_GUI(window)->view->bottom) {
if (more_item == NULL) {
more_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_more);
statusbar_redraw(mainbar);
@@ -593,7 +593,9 @@ static void sidebar_remove_items(MAIN_WINDOW_REC *window)
static void sig_mainwindow_created(MAIN_WINDOW_REC *window)
{
- window->statusbar = statusbar_create(STATUSBAR_POS_MIDDLE, window->first_line+window->lines);
+ window->statusbar =
+ statusbar_create(STATUSBAR_POS_MIDDLE,
+ window->first_line+window->height);
sidebar_add_items(window);
}
diff --git a/src/fe-text/statusbar.c b/src/fe-text/statusbar.c
index 936c8d68..2404c1a1 100644
--- a/src/fe-text/statusbar.c
+++ b/src/fe-text/statusbar.c
@@ -337,7 +337,7 @@ static void sig_mainwindow_resized(MAIN_WINDOW_REC *window)
STATUSBAR_REC *rec;
rec = window->statusbar;
- rec->ypos = window->first_line+window->lines;
+ rec->ypos = window->first_line+window->height;
}
void statusbar_init(void)
diff --git a/src/fe-text/textbuffer-commands.c b/src/fe-text/textbuffer-commands.c
new file mode 100644
index 00000000..92ec70bd
--- /dev/null
+++ b/src/fe-text/textbuffer-commands.c
@@ -0,0 +1,297 @@
+/*
+ textbuffer-commands.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "misc.h"
+#include "levels.h"
+
+#include "printtext.h"
+#include "gui-windows.h"
+
+/* SYNTAX: CLEAR */
+static void cmd_clear(const char *data)
+{
+ GHashTable *optlist;
+ void *free_arg;
+ GSList *tmp;
+
+ g_return_if_fail(data != NULL);
+
+ if (!cmd_get_params(data, &free_arg, PARAM_FLAG_OPTIONS,
+ "clear", &optlist)) return;
+
+ if (g_hash_table_lookup(optlist, "all") == NULL) {
+ /* clear active window */
+ textbuffer_view_clear(WINDOW_GUI(active_win)->view);
+ } else {
+ /* clear all windows */
+ for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *window = tmp->data;
+
+ textbuffer_view_clear(WINDOW_GUI(window)->view);
+ }
+ }
+
+ cmd_params_free(free_arg);
+}
+
+static void cmd_scrollback(const char *data, SERVER_REC *server,
+ WI_ITEM_REC *item)
+{
+ command_runsub("scrollback", data, server, item);
+}
+
+/* SYNTAX: SCROLLBACK CLEAR */
+static void cmd_scrollback_clear(void)
+{
+ textbuffer_view_remove_all_lines(WINDOW_GUI(active_win)->view);
+}
+
+static void scrollback_goto_line(int linenum)
+{
+ TEXT_BUFFER_VIEW_REC *view;
+
+ view = WINDOW_GUI(active_win)->view;
+ if (view->buffer->lines_count == 0)
+ return;
+
+ textbuffer_view_scroll_line(view, view->buffer->lines->data);
+ gui_window_scroll(active_win, linenum);
+}
+
+static void scrollback_goto_time(const char *datearg, const char *timearg)
+{
+ GList *tmp;
+ struct tm tm;
+ time_t now, stamp;
+ int day, month;
+
+ /* [dd[.mm] | -<days ago>] hh:mi[:ss] */
+ now = stamp = time(NULL);
+ if (*datearg == '-') {
+ /* -<days ago> */
+ stamp -= atoi(datearg+1) * 3600*24;
+ memcpy(&tm, localtime(&stamp), sizeof(struct tm));
+ } else if (*timearg != '\0') {
+ /* dd[.mm] */
+ memcpy(&tm, localtime(&stamp), sizeof(struct tm));
+
+ day = month = 0;
+ sscanf(datearg, "%d.%d", &day, &month);
+ if (day <= 0) return;
+
+ if (month <= 0) {
+ /* month not given */
+ if (day > tm.tm_mday) {
+ /* last month's day */
+ if (tm.tm_mon > 0)
+ tm.tm_mon--;
+ else {
+ /* last year's day.. */
+ tm.tm_year--;
+ tm.tm_mon = 11;
+ }
+ }
+ } else {
+ month--;
+ if (month > tm.tm_mon)
+ tm.tm_year--;
+ tm.tm_mon = month;
+ }
+
+ tm.tm_mday = day;
+ stamp = mktime(&tm);
+ }
+ else
+ {
+ /* only time given, move it to timearg */
+ timearg = datearg;
+ }
+
+ /* hh:mi[:ss] */
+ memcpy(&tm, localtime(&stamp), sizeof(struct tm));
+ tm.tm_sec = 0;
+ sscanf(timearg, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+ stamp = mktime(&tm);
+
+ if (stamp > now && timearg == datearg) {
+ /* we used /SB GOTO 23:59 or something, we want to jump to
+ previous day's 23:59 time instead of into future. */
+ stamp -= 3600*24;
+ }
+
+ if (stamp > now) {
+ /* we're still looking into future, don't bother checking */
+ return;
+ }
+
+ /* scroll to first line after timestamp */
+ tmp = textbuffer_view_get_lines(WINDOW_GUI(active_win)->view);
+ for (; tmp != NULL; tmp = tmp->next) {
+ LINE_REC *rec = tmp->data;
+
+ if (rec->info.time >= stamp) {
+ gui_window_scroll_line(active_win, rec);
+ break;
+ }
+ }
+}
+
+/* SYNTAX: SCROLLBACK GOTO <+|-linecount>|<linenum>|<timestamp> */
+static void cmd_scrollback_goto(const char *data)
+{
+ char *datearg, *timearg;
+ void *free_arg;
+ int lines;
+
+ if (!cmd_get_params(data, &free_arg, 2, &datearg, &timearg))
+ return;
+
+ if (*timearg == '\0' && (*datearg == '-' || *datearg == '+')) {
+ /* go forward/backward n lines */
+ lines = atoi(datearg + (*datearg == '-' ? 0 : 1));
+ gui_window_scroll(active_win, lines);
+ } else if (*timearg == '\0' && is_numeric(datearg, '\0')) {
+ /* go to n'th line. */
+ scrollback_goto_line(atoi(datearg));
+ } else {
+ /* should be timestamp */
+ scrollback_goto_time(datearg, timearg);
+ }
+
+ cmd_params_free(free_arg);
+}
+
+/* SYNTAX: SCROLLBACK HOME */
+static void cmd_scrollback_home(const char *data)
+{
+ TEXT_BUFFER_REC *buffer;
+
+ buffer = WINDOW_GUI(active_win)->view->buffer;
+ if (buffer->lines_count > 0)
+ gui_window_scroll_line(active_win, buffer->lines->data);
+}
+
+/* SYNTAX: SCROLLBACK END */
+static void cmd_scrollback_end(const char *data)
+{
+ TEXT_BUFFER_VIEW_REC *view;
+
+ view = WINDOW_GUI(active_win)->view;
+ if (view->bottom_startline == NULL)
+ return;
+
+ textbuffer_view_scroll_line(view, view->bottom_startline->data);
+ gui_window_scroll(active_win, view->bottom_subline);
+}
+
+/* SYNTAX: SCROLLBACK REDRAW */
+static void cmd_scrollback_redraw(void)
+{
+#if 0
+ GUI_WINDOW_REC *gui;
+ GList *tmp, *next;
+
+ gui = WINDOW_GUI(active_win);
+
+ screen_refresh_freeze();
+ for (tmp = gui->lines; tmp != NULL; tmp = next) {
+ next = tmp->next;
+ gui_window_reformat_line(active_win, tmp->data);
+ }
+
+ gui_window_redraw(active_win);
+ screen_refresh_thaw();
+#endif
+}
+
+static void cmd_scrollback_status(void)
+{
+ GSList *tmp;
+ int window_kb, total_lines, total_kb;
+
+ total_lines = 0; total_kb = 0;
+ for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *window = tmp->data;
+ TEXT_BUFFER_VIEW_REC *view;
+
+ view = WINDOW_GUI(window)->view;
+
+ window_kb = g_slist_length(view->buffer->text_chunks)*
+ LINE_TEXT_CHUNK_SIZE/1024;
+ total_lines += view->buffer->lines_count;
+ total_kb += window_kb;
+ printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+ "Window %d: %d lines, %dkB of data",
+ window->refnum, view->buffer->lines_count,
+ window_kb);
+ }
+
+ printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+ "Total: %d lines, %dkB of data",
+ total_lines, total_kb);
+}
+
+static void sig_away_changed(SERVER_REC *server)
+{
+ GSList *tmp;
+
+ if (!server->usermode_away)
+ return;
+
+ for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+ WINDOW_REC *rec = tmp->data;
+
+ textbuffer_view_set_bookmark_bottom(WINDOW_GUI(rec)->view,
+ "lastlog_last_away");
+ }
+}
+
+void textbuffer_commands_init(void)
+{
+ command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear);
+ command_bind("scrollback", NULL, (SIGNAL_FUNC) cmd_scrollback);
+ command_bind("scrollback clear", NULL, (SIGNAL_FUNC) cmd_scrollback_clear);
+ command_bind("scrollback goto", NULL, (SIGNAL_FUNC) cmd_scrollback_goto);
+ command_bind("scrollback home", NULL, (SIGNAL_FUNC) cmd_scrollback_home);
+ command_bind("scrollback end", NULL, (SIGNAL_FUNC) cmd_scrollback_end);
+ command_bind("scrollback redraw", NULL, (SIGNAL_FUNC) cmd_scrollback_redraw);
+ command_bind("scrollback status", NULL, (SIGNAL_FUNC) cmd_scrollback_status);
+
+ command_set_options("clear", "all");
+
+ signal_add("away mode changed", (SIGNAL_FUNC) sig_away_changed);
+}
+
+void textbuffer_commands_deinit(void)
+{
+ command_unbind("clear", (SIGNAL_FUNC) cmd_clear);
+ command_unbind("scrollback", (SIGNAL_FUNC) cmd_scrollback);
+ command_unbind("scrollback clear", (SIGNAL_FUNC) cmd_scrollback_clear);
+ command_unbind("scrollback goto", (SIGNAL_FUNC) cmd_scrollback_goto);
+ command_unbind("scrollback home", (SIGNAL_FUNC) cmd_scrollback_home);
+ command_unbind("scrollback end", (SIGNAL_FUNC) cmd_scrollback_end);
+ command_unbind("scrollback redraw", (SIGNAL_FUNC) cmd_scrollback_redraw);
+ command_unbind("scrollback status", (SIGNAL_FUNC) cmd_scrollback_status);
+
+ signal_remove("away mode changed", (SIGNAL_FUNC) sig_away_changed);
+}
diff --git a/src/fe-text/textbuffer-view.c b/src/fe-text/textbuffer-view.c
new file mode 100644
index 00000000..c1d83ac6
--- /dev/null
+++ b/src/fe-text/textbuffer-view.c
@@ -0,0 +1,1059 @@
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "textbuffer-view.h"
+#include "screen.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 >= 0 && (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);
+}
+
+static LINE_CACHE_REC *
+view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
+{
+ LINE_CACHE_REC *rec;
+ LINE_CACHE_SUB_REC *sub;
+ GSList *lines;
+ unsigned char cmd;
+ char *ptr, *last_space_ptr;
+ int xpos, pos, indent_pos, last_space, last_color, color, linecount;
+
+ g_return_val_if_fail(line->text != NULL, NULL);
+
+ xpos = 0; color = 0; indent_pos = view->default_indent;
+ last_space = last_color = 0; last_space_ptr = NULL; sub = NULL;
+
+ linecount = 1;
+ lines = NULL;
+ for (ptr = line->text;;) {
+ if (*ptr == '\0') {
+ /* command */
+ ptr++;
+ cmd = *ptr;
+ ptr++;
+
+ if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT)
+ break;
+
+ if (cmd == LINE_CMD_CONTINUE) {
+ char *tmp;
+
+ memcpy(&tmp, ptr, sizeof(char *));
+ ptr = tmp;
+ continue;
+ }
+
+ if ((cmd & 0x80) == 0) {
+ /* set color */
+ color = (color & ATTR_UNDERLINE) | cmd;
+ } else switch (cmd) {
+ case LINE_CMD_UNDERLINE:
+ color ^= ATTR_UNDERLINE;
+ break;
+ case LINE_CMD_COLOR0:
+ color = color & ATTR_UNDERLINE;
+ break;
+ case LINE_CMD_COLOR8:
+ color &= 0xfff0;
+ color |= 8|ATTR_COLOR8;
+ break;
+ case LINE_CMD_BLINK:
+ color |= 0x80;
+ break;
+ case 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;
+ break;
+ }
+ continue;
+ }
+
+ if (xpos == view->width && sub != NULL &&
+ (last_space <= indent_pos || last_space <= 10)) {
+ /* long word, remove the indentation from this line */
+ xpos -= sub->indent;
+ sub->indent = 0;
+ }
+
+ if (xpos == view->width) {
+ xpos = indent_pos;
+
+ sub = g_new0(LINE_CACHE_SUB_REC, 1);
+ if (last_space > indent_pos && last_space > 10) {
+ /* go back to last space */
+ color = last_color;
+ ptr = last_space_ptr;
+ while (*ptr == ' ') ptr++;
+ } else {
+ /* long word, no indentation in next line */
+ xpos = 0;
+ sub->continues = TRUE;
+ }
+
+ sub->start = ptr;
+ sub->indent = xpos;
+ sub->color = color;
+
+ lines = g_slist_append(lines, sub);
+ linecount++;
+
+ last_space = 0;
+ continue;
+ }
+
+ xpos++;
+ if (*ptr++ == ' ') {
+ last_space = xpos-1;
+ last_space_ptr = ptr;
+ last_color = color;
+ }
+ }
+
+ 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++) {
+ memcpy(&rec->lines[pos], lines->data,
+ sizeof(LINE_CACHE_SUB_REC));
+
+ g_free(lines->data);
+ lines = g_slist_remove(lines, lines->data);
+ }
+ }
+
+ g_hash_table_insert(view->cache->line_cache, line, rec);
+ return rec;
+}
+
+static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
+ int subline, int ypos, int max)
+{
+ LINE_CACHE_REC *cache;
+ const unsigned char *text, *text_newline;
+ char *tmp;
+ int xpos, color, drawcount, first;
+
+ cache = textbuffer_view_get_line_cache(view, line);
+ if (subline >= cache->count)
+ return 0;
+
+ xpos = color = drawcount = 0; first = TRUE;
+ text_newline = text =
+ subline == 0 ? line->text : cache->lines[subline-1].start;
+ for (;;) {
+ if (text == text_newline) {
+ if (first)
+ first = FALSE;
+ else {
+ ypos++;
+ if (--max == 0)
+ break;
+ }
+
+ if (subline > 0) {
+ xpos = cache->lines[subline-1].indent;
+ color = cache->lines[subline-1].color;
+ }
+
+ set_color(view->window, 0);
+ wmove(view->window, ypos, 0);
+ wclrtoeol(view->window);
+
+ wmove(view->window, ypos, xpos);
+ set_color(view->window, color);
+
+ /* get the beginning of the next subline */
+ text_newline = subline == cache->count-1 ? NULL :
+ cache->lines[subline].start;
+
+ drawcount++;
+ subline++;
+ }
+
+ if (*text == '\0') {
+ /* command */
+ text++;
+ if (*text == LINE_CMD_EOL || *text == LINE_CMD_FORMAT)
+ break;
+
+ if ((*text & 0x80) == 0) {
+ /* set color */
+ color = (color & ATTR_UNDERLINE) | *text;
+ } else if (*text == LINE_CMD_CONTINUE) {
+ /* jump to next block */
+ memcpy(&tmp, text+1, sizeof(unsigned char *));
+ text = tmp;
+ continue;
+ } else switch (*text) {
+ case LINE_CMD_UNDERLINE:
+ color ^= ATTR_UNDERLINE;
+ break;
+ case LINE_CMD_COLOR0:
+ color = color & ATTR_UNDERLINE;
+ break;
+ case LINE_CMD_COLOR8:
+ color &= 0xfff0;
+ color |= 8|ATTR_COLOR8;
+ break;
+ case LINE_CMD_BLINK:
+ color |= 0x80;
+ break;
+ }
+ set_color(view->window, color);
+ text++;
+ continue;
+ }
+
+ if ((*text & 127) >= 32)
+ waddch(view->window, *text);
+ else {
+ /* low-ascii */
+ set_color(view->window, ATTR_REVERSE);
+ waddch(view->window, (*text & 127)+'A'-1);
+ set_color(view->window, color);
+ }
+ text++;
+ }
+
+ 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)
+{
+ GList *tmp;
+ 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;
+ tmp = g_list_last(view->buffer->lines);
+ for (; tmp != NULL; tmp = tmp->prev) {
+ LINE_REC *line = tmp->data;
+
+ linecount = view_get_linecount(view, line);
+ if (tmp == 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 = tmp;
+ 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->lines;
+ view->bottom_subline = 0;
+ view->empty_linecount = view->height - total;
+}
+
+static void textbuffer_view_init_ypos(TEXT_BUFFER_VIEW_REC *view)
+{
+ GList *tmp;
+
+ g_return_if_fail(view != NULL);
+
+ view->ypos = -view->subline-1;
+ for (tmp = view->startline; tmp != NULL; tmp = tmp->next)
+ view->ypos += view_get_linecount(view, tmp->data);
+}
+
+/* Create new view. */
+TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
+ int width, int height,
+ int default_indent)
+{
+ TEXT_BUFFER_VIEW_REC *view;
+
+ view = g_new0(TEXT_BUFFER_VIEW_REC, 1);
+ view->buffer = buffer;
+ view->siblings = textbuffer_get_views(buffer);
+
+ view->width = width;
+ view->height = height;
+ view->default_indent = default_indent;
+
+ 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;
+
+ 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)
+{
+ view->default_indent = default_indent;
+}
+
+static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, GList *lines)
+{
+ int linecount;
+
+ linecount = 0;
+ while (lines != NULL) {
+ linecount += view_get_linecount(view, lines->data);
+ lines = lines->next;
+ }
+
+ return linecount;
+}
+
+static void view_draw(TEXT_BUFFER_VIEW_REC *view, GList *line,
+ int subline, int ypos, int lines)
+{
+ int linecount;
+
+ while (line != NULL && lines > 0) {
+ LINE_REC *rec = line->data;
+
+ linecount = view_line_draw(view, rec, subline, ypos, lines);
+ ypos += linecount; lines -= linecount;
+
+ subline = 0;
+ line = line->next;
+ }
+
+ /* clear the rest of the view */
+ while (ypos < lines) {
+ wmove(view->window, ypos, 0);
+ wclrtoeol(view->window);
+ ypos++;
+ }
+}
+
+#define view_draw_top(view, lines) \
+ view_draw(view, (view)->startline, (view)->subline, 0, lines)
+
+static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines)
+{
+ GList *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->data);
+ ypos += linecount;
+ if (ypos > maxline) {
+ subline = maxline-(ypos-linecount);
+ break;
+ }
+ line = line->next;
+ }
+
+ view_draw(view, line, subline, maxline, lines);
+}
+
+/* Returns number of lines actually scrolled */
+static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
+ int scrollcount, int draw_nonclean)
+{
+ int linecount, realcount, scroll_visible;
+
+ /* scroll down */
+ scroll_visible = lines == &view->startline;
+
+ realcount = -*subline;
+ scrollcount += *subline;
+ *subline = 0;
+ while (scrollcount > 0) {
+ linecount = view_get_linecount(view, (*lines)->data);
+
+ 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;
+ }
+
+ *lines = (*lines)->next;
+ }
+
+ /* scroll up */
+ while (scrollcount < 0 && (*lines)->prev != NULL) {
+ *lines = (*lines)->prev;
+ linecount = view_get_linecount(view, (*lines)->data);
+
+ realcount -= linecount;
+ scrollcount += linecount;
+ if (scrollcount > 0) {
+ realcount += scrollcount;
+ *subline = scrollcount;
+ break;
+ }
+ }
+
+ if (scroll_visible && realcount != 0) {
+ if (realcount <= -view->height || realcount >= view->height) {
+ /* scrolled more than screenful, redraw the
+ whole view */
+ textbuffer_view_redraw(view);
+ } else {
+ scrollok(view->window, TRUE);
+ wscrl(view->window, realcount);
+ scrollok(view->window, FALSE);
+
+ if (draw_nonclean) {
+ if (realcount < 0)
+ view_draw_top(view, -realcount);
+ else
+ view_draw_bottom(view, realcount);
+ }
+
+ screen_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;
+
+ if (view->buffer->lines == NULL)
+ return;
+
+ 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;
+ view->height = height;
+
+ textbuffer_view_init_bottom(view);
+
+ /* check that we didn't scroll lower than bottom startline.. */
+ if (g_list_find(view->bottom_startline->next,
+ view->startline->data) != NULL) {
+ 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->data);
+ 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);
+ }
+ }
+
+ 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;
+ }
+
+ textbuffer_view_redraw(view);
+}
+
+/* Clear the view, don't actually remove any lines from buffer. */
+void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view)
+{
+ view->ypos = -1;
+ view->bottom_startline = view->startline =
+ g_list_last(view->buffer->lines);
+ 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;
+
+ textbuffer_view_redraw(view);
+}
+
+/* Scroll the view up/down */
+void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines)
+{
+ int count;
+
+ count = view_scroll(view, &view->startline, &view->subline,
+ lines, TRUE);
+ view->ypos += lines < 0 ? count : -count;
+ view->bottom = view_is_bottom(view);
+
+ screen_refresh(view->window);
+}
+
+/* Scroll to specified line */
+void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
+{
+ GList *tmp;
+
+ if (g_list_find(view->bottom_startline->next, line) != NULL) {
+ view->startline = view->bottom_startline;
+ view->subline = view->bottom_subline;
+ } else {
+ for (tmp = view->buffer->lines; tmp != NULL; tmp = tmp->next) {
+ LINE_REC *rec = tmp->data;
+
+ if (rec == line) {
+ view->startline = tmp;
+ view->subline = 0;
+ break;
+ }
+ }
+ }
+
+ textbuffer_view_init_ypos(view);
+ view->bottom = view_is_bottom(view);
+
+ 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;
+
+ 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_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_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
+{
+ int linecount, ypos, subline;
+
+ if (view->bottom_startline == NULL) {
+ view->startline = view->bottom_startline =
+ view->buffer->lines;
+ }
+
+ if (view->buffer->cur_line != line &&
+ g_list_find(view->bottom_startline, line) == NULL)
+ return;
+
+ linecount = view->cache->last_linecount;
+ 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->ypos >= view->height) {
+ linecount = view->ypos-view->height+1;
+ view_scroll(view, &view->startline,
+ &view->subline, linecount, FALSE);
+ view->ypos -= linecount;
+ }
+
+ ypos = view->ypos+1 - view->cache->last_linecount;
+ if (ypos >= 0)
+ subline = 0;
+ else {
+ subline = -ypos;
+ ypos = 0;
+ }
+ view_line_draw(view, line, subline, ypos, view->height-ypos);
+ }
+ screen_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;
+
+ 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;
+ LINE_REC *newline;
+ 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) {
+ GList *pos = g_list_find(view->buffer->lines, line);
+ newline = pos->prev == NULL ? NULL : pos->next->data;
+ for (tmp = rec.remove_list; tmp != NULL; tmp = tmp->next) {
+ g_hash_table_remove(view->bookmarks, tmp->data);
+ if (newline != NULL) {
+ g_hash_table_insert(view->bookmarks,
+ tmp->data, newline);
+ }
+ }
+ 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,
+ GList *lines, int subline,
+ LINE_REC *skip_line)
+{
+ int height, linecount;
+
+ height = -subline;
+ while (lines != NULL && height < view->height) {
+ LINE_REC *line = lines->data;
+
+ if (line != skip_line) {
+ linecount = view_get_linecount(view, line);
+ height += linecount;
+ }
+ lines = lines->next;
+ }
+
+ return height < view->height ? height : view->height;
+}
+
+static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
+ int linecount)
+{
+ int realcount, scroll;
+
+ view_bookmarks_check(view, line);
+
+ if (view->buffer->cur_line == line) {
+ /* the last line is being removed */
+ LINE_REC *prevline;
+
+ prevline = view->buffer->lines->data == line ? NULL :
+ g_list_last(view->bottom_startline)->data;
+ view->cache->last_linecount = prevline == NULL ? 0 :
+ view_get_linecount(view, prevline);
+ }
+
+ if (line == view->buffer->lines->data) {
+ /* first line in the buffer - this is the most commonly
+ removed line.. */
+ if (line == view->bottom_startline->data) {
+ /* very small scrollback.. */
+ view->bottom_startline = view->bottom_startline->next;
+ view->bottom_subline = 0;
+
+ if (view->startline->data == line) {
+ view->startline = view->startline->next;
+ view->subline = 0;
+ view->empty_linecount += linecount;
+ view->ypos -= linecount;
+ }
+ }
+ } else if (g_list_find(view->bottom_startline, line) != NULL) {
+ realcount = view_scroll(view, &view->bottom_startline,
+ &view->bottom_subline,
+ -linecount, FALSE);
+ if (view->bottom) {
+ /* we're at the bottom, remove the same amount as
+ from bottom_startline */
+ view_scroll(view, &view->startline,
+ &view->subline, -linecount, TRUE);
+ view->ypos -= realcount;
+ } else {
+ if (view->startline->data == line) {
+ view->startline =
+ view->startline->next != NULL ?
+ view->startline->next :
+ view->startline->prev;
+ view->subline = 0;
+ }
+ scroll = view->height -
+ view_get_lines_height(view, view->startline,
+ view->subline, line);
+ if (scroll > 0) {
+ view_scroll(view, &view->startline,
+ &view->subline, -scroll, TRUE);
+ view->ypos -= scroll;
+ }
+ }
+ view->empty_linecount += linecount-realcount;
+ }
+
+ view->bottom = view_is_bottom(view);
+ screen_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;
+
+ 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);
+}
+
+/* Remove all lines from buffer. */
+void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view)
+{
+ GSList *tmp;
+
+ textbuffer_remove_all_lines(view->buffer);
+
+ /* 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);
+
+ /* recreate caches, clear screens */
+ view->cache = textbuffer_cache_get(view->siblings, view->width);
+ textbuffer_view_clear(view);
+
+ 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);
+ textbuffer_view_clear(rec);
+ }
+}
+
+/* Set a bookmark in view */
+void textbuffer_view_set_bookmark(TEXT_BUFFER_VIEW_REC *view,
+ const char *name, LINE_REC *line)
+{
+ gpointer key, value;
+
+ 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;
+
+ if (view->bottom_startline != NULL) {
+ line = g_list_last(view->bottom_startline)->data;
+ 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)
+{
+ 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, WINDOW *window)
+{
+ if (view->window != window) {
+ view->window = window;
+ if (window != NULL)
+ textbuffer_view_redraw(view);
+ }
+}
+
+/* Redraw a view to window */
+void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view)
+{
+ if (view->window == NULL)
+ return;
+
+ view_draw_top(view, view->height);
+ screen_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);
+ }
+ 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);
+}
diff --git a/src/fe-text/textbuffer-view.h b/src/fe-text/textbuffer-view.h
new file mode 100644
index 00000000..cdb55b7f
--- /dev/null
+++ b/src/fe-text/textbuffer-view.h
@@ -0,0 +1,131 @@
+#ifndef __TEXTBUFFER_VIEW_H
+#define __TEXTBUFFER_VIEW_H
+
+#include "textbuffer.h"
+#include "screen.h"
+
+typedef struct {
+ unsigned char *start;
+ int indent;
+ int color;
+
+ /* first word in line belong to the end of the last word in
+ previous line */
+ unsigned int continues:1;
+} LINE_CACHE_SUB_REC;
+
+typedef struct {
+ time_t last_access;
+
+ int count; /* number of real lines */
+
+ /* variable sized array, actually. starts from the second line,
+ so size of it is count-1 */
+ LINE_CACHE_SUB_REC lines[1];
+} LINE_CACHE_REC;
+
+typedef struct {
+ int refcount;
+ int width;
+
+ GHashTable *line_cache;
+
+ /* should contain the same value for each cache that uses the
+ same buffer */
+ unsigned char update_counter;
+ /* number of real lines used by the last line in buffer */
+ int last_linecount;
+} TEXT_BUFFER_CACHE_REC;
+
+typedef struct {
+ TEXT_BUFFER_REC *buffer;
+ GSList *siblings; /* other views that use the same buffer */
+
+ WINDOW *window;
+ int width, height;
+ int default_indent;
+
+ TEXT_BUFFER_CACHE_REC *cache;
+ GList *startline; /* line at the top of the screen */
+ int subline; /* number of "real lines" to skip from `startline' */
+
+ /* marks the bottom of the text buffer */
+ GList *bottom_startline;
+ int bottom_subline;
+
+ /* how many empty lines are in screen. a screenful when started
+ or used /CLEAR */
+ int empty_linecount;
+ /* window is at the bottom of the text buffer */
+ unsigned int bottom:1;
+
+ /* info how to efficiently refresh window buffer */
+ //unsigned int redraw:1;
+ int ypos; /* cursor position - visible area is 0..height-1 */
+ /*GList *drawn_startline;
+ int drawn_subline;*/
+
+ /* Bookmarks to the lines in the buffer - removed automatically
+ when the line gets removed from buffer */
+ GHashTable *bookmarks;
+} TEXT_BUFFER_VIEW_REC;
+
+/* Create new view. */
+TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
+ int width, int height,
+ int default_indent);
+/* Destroy the view. */
+void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view);
+/* Change the default indent position */
+void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
+ int default_indent);
+
+/* Resize the view. */
+void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height);
+/* Clear the view, don't actually remove any lines from buffer. */
+void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view);
+
+#define textbuffer_view_get_lines(view) \
+ ((view)->buffer->lines)
+
+/* Scroll the view up/down */
+void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines);
+/* Scroll to specified line */
+void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line);
+/* Return line cache */
+LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view,
+ LINE_REC *line);
+
+/*
+ Functions for manipulating the text buffer, using these commands update
+ all views that use the buffer.
+*/
+
+/* 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);
+/* Remove one line from buffer. */
+void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line);
+/* Remove all lines from buffer. */
+void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view);
+
+/* Set a bookmark in view */
+void textbuffer_view_set_bookmark(TEXT_BUFFER_VIEW_REC *view,
+ const char *name, LINE_REC *line);
+/* Set a bookmark in view to the bottom line */
+void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view,
+ const char *name);
+/* Return the line for bookmark */
+LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
+ const char *name);
+
+/* Specify window where the changes in view should be drawn,
+ NULL disables it. */
+void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, WINDOW *window);
+/* Redraw the view */
+void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view);
+
+void textbuffer_view_init(void);
+void textbuffer_view_deinit(void);
+
+#endif
diff --git a/src/fe-text/textbuffer.c b/src/fe-text/textbuffer.c
new file mode 100644
index 00000000..b0ef2e70
--- /dev/null
+++ b/src/fe-text/textbuffer.c
@@ -0,0 +1,601 @@
+/*
+ textbuffer.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "misc.h"
+#include "formats.h"
+
+#include "textbuffer.h"
+
+#ifdef HAVE_REGEX_H
+# include <regex.h>
+#endif
+
+#define TEXT_CHUNK_USABLE_SIZE (LINE_TEXT_CHUNK_SIZE-2-(int)sizeof(char*))
+
+static GMemChunk *buffer_chunk, *line_chunk, *text_chunk;
+
+TEXT_BUFFER_REC *textbuffer_create(void)
+{
+ TEXT_BUFFER_REC *buffer;
+
+ buffer = g_mem_chunk_alloc0(buffer_chunk);
+ buffer->last_eol = TRUE;
+ return buffer;
+}
+
+void textbuffer_destroy(TEXT_BUFFER_REC *buffer)
+{
+ textbuffer_remove_all_lines(buffer);
+ g_mem_chunk_free(buffer_chunk, 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;
+ }
+
+ 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;
+ char *buf, *ptr, **pptr;
+
+ g_return_val_if_fail(buffer != NULL, NULL);
+
+ rec = g_mem_chunk_alloc(text_chunk);
+ 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(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)
+{
+ g_return_if_fail(buffer != NULL);
+ g_return_if_fail(chunk != NULL);
+
+ buffer->text_chunks = g_slist_remove(buffer->text_chunks, chunk);
+ g_mem_chunk_free(text_chunk, chunk);
+}
+
+static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line)
+{
+ TEXT_CHUNK_REC *chunk;
+ const unsigned char *text;
+ unsigned char *tmp = NULL;
+
+ for (text = line->text;; text++) {
+ if (*text != '\0')
+ continue;
+
+ text++;
+ if (*text == LINE_CMD_CONTINUE || *text == LINE_CMD_EOL) {
+ if (*text == 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 (*text == LINE_CMD_EOL)
+ break;
+
+ text = tmp-1;
+ }
+ }
+}
+
+static void text_chunk_append(TEXT_BUFFER_REC *buffer,
+ const char *data, int len)
+{
+ TEXT_CHUNK_REC *chunk;
+ int left;
+
+ if (len == 0)
+ return;
+
+ chunk = buffer->cur_text;
+ while (chunk->pos + len >= TEXT_CHUNK_USABLE_SIZE) {
+ left = TEXT_CHUNK_USABLE_SIZE - chunk->pos;
+ if (data[left-1] == 0) left--; /* don't split the commands */
+
+ 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);
+}
+
+static LINE_REC *textbuffer_line_create(TEXT_BUFFER_REC *buffer)
+{
+ LINE_REC *rec;
+
+ if (buffer->cur_text == NULL)
+ text_chunk_create(buffer);
+
+ rec = g_mem_chunk_alloc(line_chunk);
+ rec->refcount = 1;
+ rec->text = buffer->cur_text->buffer + buffer->cur_text->pos;
+
+ buffer->cur_text->refcount++;
+ return rec;
+}
+
+static LINE_REC *textbuffer_line_insert(TEXT_BUFFER_REC *buffer,
+ LINE_REC *prev)
+{
+ LINE_REC *line;
+
+ line = textbuffer_line_create(buffer);
+ if (prev == buffer->cur_line) {
+ buffer->cur_line = line;
+ buffer->lines = g_list_append(buffer->lines, buffer->cur_line);
+ } else {
+ buffer->lines = g_list_insert(buffer->lines, line,
+ g_list_index(buffer->lines, prev)+1);
+ }
+ buffer->lines_count++;
+
+ return line;
+}
+
+void textbuffer_line_ref(LINE_REC *line)
+{
+ if (++line->refcount == 255)
+ g_error("line reference counter wrapped - shouldn't happen");
+}
+
+void textbuffer_line_unref(TEXT_BUFFER_REC *buffer, LINE_REC *line)
+{
+ if (--line->refcount == 0) {
+ text_chunk_line_free(buffer, line);
+ g_mem_chunk_free(line_chunk, line);
+ }
+}
+
+void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list)
+{
+ while (list != NULL) {
+ textbuffer_line_unref(buffer, list->data);
+ list = list->next;
+ }
+}
+
+LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
+ const unsigned char *data, int len,
+ LINE_INFO_REC *info)
+{
+ return textbuffer_insert(buffer, buffer->cur_line, data, len, info);
+}
+
+LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after,
+ const unsigned char *data, int len,
+ LINE_INFO_REC *info)
+{
+ LINE_REC *line;
+
+ line = !buffer->last_eol ? insert_after :
+ textbuffer_line_insert(buffer, insert_after);
+
+ if (info != NULL)
+ memcpy(&line->info, info, sizeof(line->info));
+
+ text_chunk_append(buffer, data, len);
+
+ buffer->last_eol = len >= 2 &&
+ data[len-2] == 0 && data[len-1] == LINE_CMD_EOL;
+
+ return line;
+}
+
+void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line)
+{
+ buffer->lines = g_list_remove(buffer->lines, line);
+
+ if (buffer->cur_line == line) {
+ buffer->cur_line = buffer->lines == NULL ? NULL :
+ g_list_last(buffer->lines)->data;
+ }
+
+ buffer->lines_count--;
+ textbuffer_line_unref(buffer, line);
+}
+
+/* Removes all lines from buffer, ignoring reference counters */
+void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer)
+{
+ GSList *tmp;
+
+ for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next)
+ g_mem_chunk_free(text_chunk, tmp->data);
+ g_slist_free(buffer->text_chunks);
+ buffer->text_chunks = NULL;
+
+ g_list_free(buffer->lines);
+ buffer->lines = NULL;
+
+ buffer->cur_line = NULL;
+ buffer->lines_count = 0;
+}
+
+void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
+{
+ unsigned char cmd;
+ 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, *ptr);
+ ptr++;
+ continue;
+ }
+
+ ptr++;
+ cmd = (unsigned char) *ptr;
+ ptr++;
+
+ if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) {
+ /* end of line */
+ break;
+ }
+
+ if (cmd == LINE_CMD_CONTINUE) {
+ /* line continues in another address.. */
+ memcpy(&tmp, ptr, sizeof(char *));
+ ptr = tmp;
+ continue;
+ }
+
+ if (!coloring) {
+ /* no colors, skip coloring commands */
+ continue;
+ }
+
+ if ((cmd & 0x80) == 0) {
+ /* set color */
+ g_string_sprintfa(str, "\004%c%c",
+ (cmd & 0x0f)+'0',
+ ((cmd & 0xf0) >> 4)+'0');
+ } else switch (cmd) {
+ case LINE_CMD_UNDERLINE:
+ g_string_append_c(str, 31);
+ break;
+ case LINE_CMD_COLOR0:
+ g_string_sprintfa(str, "\004%c%c",
+ '0', FORMAT_COLOR_NOCHANGE);
+ break;
+ case LINE_CMD_COLOR8:
+ g_string_sprintfa(str, "\004%c%c",
+ '8', FORMAT_COLOR_NOCHANGE);
+ break;
+ case LINE_CMD_BLINK:
+ g_string_sprintfa(str, "\004%c", FORMAT_STYLE_BLINK);
+ break;
+ case LINE_CMD_INDENT:
+ break;
+ }
+ }
+}
+
+GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
+ int level, int nolevel, const char *text,
+ int regexp, int fullword, int case_sensitive)
+{
+#ifdef HAVE_REGEX_H
+ regex_t preg;
+#endif
+ GList *line, *tmp;
+ GList *matches;
+ GString *str;
+
+ g_return_val_if_fail(buffer != NULL, NULL);
+ g_return_val_if_fail(text != NULL, NULL);
+
+ if (regexp) {
+#ifdef HAVE_REGEX_H
+ int flags = REG_EXTENDED | REG_NOSUB |
+ (case_sensitive ? 0 : REG_ICASE);
+ if (regcomp(&preg, text, flags) != 0)
+ return NULL;
+#else
+ return NULL;
+#endif
+ }
+
+ matches = NULL;
+ str = g_string_new(NULL);
+
+ line = g_list_find(buffer->lines, startline);
+ if (line == NULL)
+ line = buffer->lines;
+
+ for (tmp = line; tmp != NULL; tmp = tmp->next) {
+ LINE_REC *rec = tmp->data;
+
+ if ((rec->info.level & level) == 0 ||
+ (rec->info.level & nolevel) != 0)
+ continue;
+
+ if (*text == '\0') {
+ /* no search word, everything matches */
+ textbuffer_line_ref(rec);
+ matches = g_list_append(matches, rec);
+ continue;
+ }
+
+ textbuffer_line2text(rec, FALSE, str);
+
+ if (
+#ifdef HAVE_REGEX_H
+ regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
+#endif
+ fullword ? strstr_full_case(str->str, text,
+ !case_sensitive) != NULL :
+ case_sensitive ? strstr(str->str, text) != NULL :
+ stristr(str->str, text) != NULL) {
+ /* matched */
+ textbuffer_line_ref(rec);
+ matches = g_list_append(matches, rec);
+ }
+ }
+#ifdef HAVE_REGEX_H
+ if (regexp) regfree(&preg);
+#endif
+ g_string_free(str, TRUE);
+ return matches;
+}
+
+#if 0 /* FIXME: saving formats is broken */
+static char *line_read_format(unsigned const char **text)
+{
+ GString *str;
+ char *ret;
+
+ str = g_string_new(NULL);
+ for (;;) {
+ if (**text == '\0') {
+ if ((*text)[1] == LINE_CMD_EOL) {
+ /* leave text at \0<eof> */
+ break;
+ }
+ if ((*text)[1] == LINE_CMD_FORMAT_CONT) {
+ /* leave text at \0<format_cont> */
+ break;
+ }
+ (*text)++;
+
+ if (**text == LINE_CMD_FORMAT) {
+ /* move text to start after \0<format> */
+ (*text)++;
+ break;
+ }
+
+ if (**text == LINE_CMD_CONTINUE) {
+ unsigned char *tmp;
+
+ memcpy(&tmp, (*text)+1, sizeof(char *));
+ *text = tmp;
+ continue;
+ } else if (**text & 0x80)
+ (*text)++;
+ continue;
+ }
+
+ g_string_append_c(str, (char) **text);
+ (*text)++;
+ }
+
+ ret = str->str;
+ g_string_free(str, FALSE);
+ return ret;
+}
+
+static char *textbuffer_line_get_format(WINDOW_REC *window, LINE_REC *line,
+ GString *raw)
+{
+ const unsigned char *text;
+ char *module, *format_name, *args[MAX_FORMAT_PARAMS], *ret;
+ TEXT_DEST_REC dest;
+ int formatnum, argcount;
+
+ text = (const unsigned char *) line->text;
+
+ /* skip the beginning of the line until we find the format */
+ g_free(line_read_format(&text));
+ if (text[1] == LINE_CMD_FORMAT_CONT) {
+ g_string_append_c(raw, '\0');
+ g_string_append_c(raw, (char)LINE_CMD_FORMAT_CONT);
+ return NULL;
+ }
+
+ /* read format information */
+ module = line_read_format(&text);
+ format_name = line_read_format(&text);
+
+ if (raw != NULL) {
+ g_string_append_c(raw, '\0');
+ g_string_append_c(raw, (char)LINE_CMD_FORMAT);
+
+ g_string_append(raw, module);
+
+ g_string_append_c(raw, '\0');
+ g_string_append_c(raw, (char)LINE_CMD_FORMAT);
+
+ g_string_append(raw, format_name);
+ }
+
+ formatnum = format_find_tag(module, format_name);
+ if (formatnum == -1)
+ ret = NULL;
+ else {
+ argcount = 0;
+ memset(args, 0, sizeof(args));
+ while (*text != '\0' || text[1] != LINE_CMD_EOL) {
+ args[argcount] = line_read_format(&text);
+ if (raw != NULL) {
+ g_string_append_c(raw, '\0');
+ g_string_append_c(raw,
+ (char)LINE_CMD_FORMAT);
+
+ g_string_append(raw, args[argcount]);
+ }
+ argcount++;
+ }
+
+ /* get the format text */
+ format_create_dest(&dest, NULL, NULL, line->level, window);
+ ret = format_get_text_theme_charargs(current_theme,
+ module, &dest,
+ formatnum, args);
+ while (argcount > 0)
+ g_free(args[--argcount]);
+ }
+
+ g_free(module);
+ g_free(format_name);
+
+ return ret;
+}
+
+void textbuffer_reformat_line(WINDOW_REC *window, LINE_REC *line)
+{
+ GUI_WINDOW_REC *gui;
+ TEXT_DEST_REC dest;
+ GString *raw;
+ char *str, *tmp, *prestr, *linestart, *leveltag;
+
+ gui = WINDOW_GUI(window);
+
+ raw = g_string_new(NULL);
+ str = textbuffer_line_get_format(window, line, raw);
+
+ if (str == NULL && raw->len == 2 &&
+ raw->str[1] == (char)LINE_CMD_FORMAT_CONT) {
+ /* multiline format, format explained in one the
+ following lines. remove this line. */
+ textbuffer_line_remove(window, line, FALSE);
+ } else if (str != NULL) {
+ /* FIXME: ugly ugly .. and this can't handle
+ non-formatted lines.. */
+ g_string_append_c(raw, '\0');
+ g_string_append_c(raw, (char)LINE_CMD_EOL);
+
+ textbuffer_line_text_free(gui, line);
+
+ gui->temp_line = line;
+ gui->temp_line->text = gui->cur_text->buffer+gui->cur_text->pos;
+ gui->cur_text->lines++;
+ gui->eol_marked = FALSE;
+
+ format_create_dest(&dest, NULL, NULL, line->level, window);
+
+ linestart = format_get_line_start(current_theme, &dest, line->time);
+ leveltag = format_get_level_tag(current_theme, &dest);
+
+ prestr = g_strconcat(linestart == NULL ? "" : linestart,
+ leveltag, NULL);
+ g_free_not_null(linestart);
+ g_free_not_null(leveltag);
+
+ tmp = format_add_linestart(str, prestr);
+ g_free(str);
+ g_free(prestr);
+
+ format_send_to_gui(&dest, tmp);
+ g_free(tmp);
+
+ textbuffer_line_append(gui, raw->str, raw->len);
+
+ gui->eol_marked = TRUE;
+ gui->temp_line = NULL;
+ }
+ g_string_free(raw, TRUE);
+}
+#endif
+
+void textbuffer_init(void)
+{
+ buffer_chunk = g_mem_chunk_new("text buffer chunk",
+ sizeof(TEXT_BUFFER_REC),
+ sizeof(TEXT_BUFFER_REC)*32, G_ALLOC_AND_FREE);
+ line_chunk = g_mem_chunk_new("line chunk", sizeof(LINE_REC),
+ sizeof(LINE_REC)*1024, G_ALLOC_AND_FREE);
+ text_chunk = g_mem_chunk_new("text chunk", sizeof(TEXT_CHUNK_REC),
+ sizeof(TEXT_CHUNK_REC)*32, G_ALLOC_AND_FREE);
+}
+
+void textbuffer_deinit(void)
+{
+ g_mem_chunk_destroy(buffer_chunk);
+ g_mem_chunk_destroy(line_chunk);
+ g_mem_chunk_destroy(text_chunk);
+}
diff --git a/src/fe-text/textbuffer.h b/src/fe-text/textbuffer.h
new file mode 100644
index 00000000..21f70e26
--- /dev/null
+++ b/src/fe-text/textbuffer.h
@@ -0,0 +1,90 @@
+#ifndef __TEXTBUFFER_H
+#define __TEXTBUFFER_H
+
+/* FIXME: Textbuffer code gets a lot faster in some points when I get rid of
+ GList and make prev/next pointers directly in LINE_REC. However, this
+ can still wait for a while until I get rid of GList entirely everywhere. */
+
+#define LINE_TEXT_CHUNK_SIZE 16384
+
+enum {
+ LINE_CMD_EOL=0x80, /* line ends here */
+ LINE_CMD_CONTINUE, /* line continues in next block */
+ LINE_CMD_COLOR0, /* change to black, would be same as \0\0 but it breaks things.. */
+ LINE_CMD_COLOR8, /* change to dark grey, normally 8 = bold black */
+ LINE_CMD_UNDERLINE, /* enable/disable underlining */
+ LINE_CMD_INDENT, /* if line is split, indent it at this position */
+ LINE_CMD_BLINK, /* blinking background */
+ LINE_CMD_FORMAT, /* end of line, but next will come the format that was used to create the
+ text in format <module><format_name><arg><arg2...> - fields are separated
+ with \0<format> and last argument ends with \0<eol>. \0<continue> is allowed
+ anywhere */
+ LINE_CMD_FORMAT_CONT /* multiline format, continues to next line */
+};
+
+typedef struct {
+ int level;
+ time_t time;
+} LINE_INFO_REC;
+
+typedef struct {
+ /* text in the line. \0 means that the next char will be a
+ color or command. <= 127 = color or if 8. bit is set, the
+ first 7 bits are the command. See LINE_CMD_xxxx.
+
+ DO NOT ADD BLACK WITH \0\0 - this will break things. Use
+ LINE_CMD_COLOR0 instead. */
+ unsigned char *text;
+ unsigned char refcount;
+ LINE_INFO_REC info;
+} LINE_REC;
+
+typedef struct {
+ unsigned char buffer[LINE_TEXT_CHUNK_SIZE];
+ int pos;
+ int refcount;
+} TEXT_CHUNK_REC;
+
+typedef struct {
+ GSList *text_chunks;
+ GList *lines;
+ int lines_count;
+
+ LINE_REC *cur_line;
+ TEXT_CHUNK_REC *cur_text;
+
+ unsigned int last_eol:1;
+} TEXT_BUFFER_REC;
+
+/* Create new buffer */
+TEXT_BUFFER_REC *textbuffer_create(void);
+/* Destroy the buffer */
+void textbuffer_destroy(TEXT_BUFFER_REC *buffer);
+
+void textbuffer_line_ref(LINE_REC *line);
+void textbuffer_line_unref(TEXT_BUFFER_REC *buffer, LINE_REC *line);
+void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list);
+
+/* Append text to buffer. When \0<EOL> is found at the END OF DATA, a new
+ line is created. You must send the EOL command before you can do anything
+ else with the buffer. */
+LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
+ const unsigned char *data, int len,
+ LINE_INFO_REC *info);
+LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after,
+ const unsigned char *data, int len,
+ LINE_INFO_REC *info);
+
+void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line);
+/* Removes all lines from buffer, ignoring reference counters */
+void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer);
+
+void textbuffer_line2text(LINE_REC *line, int coloring, GString *str);
+GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
+ int level, int nolevel, const char *text,
+ int regexp, int fullword, int case_sensitive);
+
+void textbuffer_init(void);
+void textbuffer_deinit(void);
+
+#endif