diff options
Diffstat (limited to 'src/fe-text')
-rw-r--r-- | src/fe-text/Makefile.am | 10 | ||||
-rw-r--r-- | src/fe-text/gui-printtext.c | 478 | ||||
-rw-r--r-- | src/fe-text/gui-printtext.h | 4 | ||||
-rw-r--r-- | src/fe-text/gui-readline.c | 2 | ||||
-rw-r--r-- | src/fe-text/gui-textwidget.c | 282 | ||||
-rw-r--r-- | src/fe-text/gui-windows.c | 1056 | ||||
-rw-r--r-- | src/fe-text/gui-windows.h | 109 | ||||
-rw-r--r-- | src/fe-text/irssi.c | 12 | ||||
-rw-r--r-- | src/fe-text/lastlog.c | 38 | ||||
-rw-r--r-- | src/fe-text/mainwindows-save.c | 4 | ||||
-rw-r--r-- | src/fe-text/mainwindows.c | 146 | ||||
-rw-r--r-- | src/fe-text/mainwindows.h | 6 | ||||
-rw-r--r-- | src/fe-text/screen.c | 14 | ||||
-rw-r--r-- | src/fe-text/statusbar-items.c | 8 | ||||
-rw-r--r-- | src/fe-text/statusbar.c | 2 | ||||
-rw-r--r-- | src/fe-text/textbuffer-commands.c | 297 | ||||
-rw-r--r-- | src/fe-text/textbuffer-view.c | 1059 | ||||
-rw-r--r-- | src/fe-text/textbuffer-view.h | 131 | ||||
-rw-r--r-- | src/fe-text/textbuffer.c | 601 | ||||
-rw-r--r-- | src/fe-text/textbuffer.h | 90 |
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 |