diff options
Diffstat (limited to 'src/fe-text/gui-windows.c')
-rw-r--r-- | src/fe-text/gui-windows.c | 544 |
1 files changed, 288 insertions, 256 deletions
diff --git a/src/fe-text/gui-windows.c b/src/fe-text/gui-windows.c index 60956cce..64d06e98 100644 --- a/src/fe-text/gui-windows.c +++ b/src/fe-text/gui-windows.c @@ -46,6 +46,7 @@ static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window, MAIN_WINDOW_REC *pare 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->last_line-parent->first_line; @@ -53,8 +54,19 @@ static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window, MAIN_WINDOW_REC *pare 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); @@ -106,7 +118,7 @@ static void gui_window_destroyed(WINDOW_REC *window) gui_window_deinit(gui); window->gui_data = NULL; - if (mainwindows->next != NULL && parent->active == window) + if (parent->active == window && mainwindows->next != NULL) mainwindow_destroy(parent); } @@ -126,322 +138,337 @@ void gui_window_clear(WINDOW_REC *window) gui_window_redraw(window); } -int gui_window_update_bottom(GUI_WINDOW_REC *gui, int lines) +/* 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; + int linecount, last_linecount; - while (lines < 0) - { - if (gui->bottom_subline > 0) - gui->bottom_subline--; - else - { - if (gui->bottom_startline->prev == NULL) + if (gui->bottom_startline == 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; - } - lines++; - } + for (; lines < 0; lines++) { + if (gui->bottom_subline > 0) { + gui->bottom_subline--; + continue; + } - last_linecount = linecount = -1; - while (lines > 0) - { - if (linecount == -1) - last_linecount = linecount = gui_window_get_linecount(gui, gui->bottom_startline->data); + if (gui->bottom_startline->prev == NULL) + return -1; + gui->bottom_startline = gui->bottom_startline->prev; - if (linecount > gui->bottom_subline+1) - gui->bottom_subline++; - else - { - gui->bottom_subline = 0; - linecount = -1; + 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 (gui->bottom_startline->next == NULL) - break; - gui->bottom_startline = gui->bottom_startline->next; + 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; + } + lines--; } - lines--; - } - return last_linecount; + return last_linecount; } -void gui_window_newline(GUI_WINDOW_REC *gui, gboolean visible) +void gui_window_newline(GUI_WINDOW_REC *gui, int visible) { - gboolean last_line; - gint linecount; + int lines; - g_return_if_fail(gui != NULL); + g_return_if_fail(gui != NULL); - gui->xpos = 0; - last_line = gui->ypos >= gui->parent->last_line-gui->parent->first_line; + gui->xpos = 0; - if (gui->empty_linecount > 0) - { - /* window buffer height isn't even the size of the screen yet */ - gui->empty_linecount--; - linecount = gui_window_get_linecount(gui, gui->startline->data); - } - else - { - linecount = gui_window_update_bottom(gui, 1); - } + if (gui->empty_linecount > 0) { + /* window buffer height isn't even the size of the screen yet */ + gui->empty_linecount--; + gui->ypos++; + return; + } - if (!last_line || !gui->bottom) - { - gui->ypos++; - } - else if (gui->bottom) - { - if (gui->subline >= linecount) - { - /* after screen gets full after /CLEAR we end up here.. */ - gui->startline = gui->startline->next; - gui->subline = 0; + lines = gui_window_update_bottom(gui, 1); - linecount = gui_window_update_bottom(gui, 1); + if (!gui->bottom) { + gui->ypos++; + return; } - if (linecount > 1+gui->subline) - gui->subline++; - else - { - gui->startline = gui->startline->next; - gui->subline = 0; + if (gui->subline >= lines) { + /* after screen gets full after /CLEAR we end up here.. */ + gui->startline = gui->startline->next; + gui->subline = 0; + + lines = gui_window_update_bottom(gui, 1); } - if (visible) - { - scroll_up(gui->parent->first_line, gui->parent->last_line); - move(gui->parent->last_line, 0); clrtoeol(); + if (lines > 1+gui->subline) + gui->subline++; + else { + gui->startline = gui->startline->next; + gui->subline = 0; + } + + if (visible) { + scroll_up(gui->parent->first_line, gui->parent->last_line); + move(gui->parent->last_line, 0); clrtoeol(); } - } } -/* get number of real lines that line record takes - this really should share - at least some code with gui_window_line_draw().. */ -gint gui_window_get_linecount(GUI_WINDOW_REC *gui, LINE_REC *line) +static LINE_CACHE_REC *gui_window_line_cache(GUI_WINDOW_REC *gui, LINE_REC *line) { - gchar *ptr, *last_space_ptr, *tmp; - gint lines, xpos, indent_pos, last_space; + LINE_CACHE_REC *rec; + LINE_CACHE_SUB_REC *sub; + GSList *lines; + unsigned char *ptr, *last_space_ptr; + int xpos, pos, indent_pos, last_space, color; + + g_return_val_if_fail(line->text != NULL, NULL); + + rec = g_new(LINE_CACHE_REC, 1); + + xpos = 0; color = 0; indent_pos = DEFAULT_INDENT_POS; + last_space = 0; last_space_ptr = NULL; + + rec->count = 1; lines = NULL; + for (ptr = (unsigned char *) line->text;;) { + if (*ptr == '\0') { + /* command */ + ptr++; + if (*ptr == LINE_CMD_EOL) + break; + + if (*ptr == LINE_CMD_CONTINUE) { + char *tmp; + + memcpy(&tmp, ptr+1, sizeof(char *)); + ptr = tmp; + continue; + } + + if ((*ptr & 0x80) == 0) { + /* set color */ + color = (color & ATTR_UNDERLINE) | *ptr; + } else switch (*ptr) { + case LINE_CMD_OVERFLOW: + g_error("buffer overflow!"); + case LINE_CMD_UNDERLINE: + color ^= ATTR_UNDERLINE; + break; + case LINE_CMD_COLOR8: + color &= 0xfff0; + color |= 8|ATTR_COLOR8; + 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; + } + + ptr++; + continue; + } - g_return_val_if_fail(gui != NULL, -1); - g_return_val_if_fail(line != NULL, -1); + if (xpos == COLS) { + xpos = indent_pos; - if (line->text == NULL) - return 0; + if (last_space > indent_pos && last_space > 10) { + /* go back to last space */ + ptr = last_space_ptr; + while (*ptr == ' ') ptr++; + } - xpos = 0; lines = 1; indent_pos = DEFAULT_INDENT_POS; - last_space = 0; last_space_ptr = NULL; - for (ptr = line->text;; ptr++) - { - if (*ptr == '\0') - { - /* command */ - ptr++; - switch ((guchar) *ptr) - { - case LINE_CMD_OVERFLOW: - g_error("buffer overflow!"); - case LINE_CMD_EOL: - return lines; - case LINE_CMD_CONTINUE: - memcpy(&tmp, ptr+1, sizeof(gchar *)); - ptr = tmp-1; - break; - case LINE_CMD_INDENT: - indent_pos = xpos; - break; - } - continue; - } + sub = g_new(LINE_CACHE_SUB_REC, 1); + sub->start = ptr; + sub->indent = indent_pos; + sub->color = color; - if (xpos == COLS) - { - xpos = indent_pos >= COLS-5 ? DEFAULT_INDENT_POS : indent_pos; + lines = g_slist_append(lines, sub); + rec->count++; - if (last_space > indent_pos && last_space > 10) - { - ptr = last_space_ptr; - while (*ptr == ' ') ptr++; - } + last_space = 0; + continue; + } - last_space = 0; - lines++; - ptr--; - continue; + xpos++; + if (*ptr++ == ' ') { + last_space = xpos-1; + last_space_ptr = ptr; + } } - xpos++; - if (*ptr == ' ') - { - last_space = xpos-1; - last_space_ptr = ptr+1; + 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; } -/* draw line - ugly code.. */ -gint gui_window_line_draw(GUI_WINDOW_REC *gui, LINE_REC *line, gint ypos, gint skip, gint max) +void gui_window_cache_remove(GUI_WINDOW_REC *gui, LINE_REC *line) { - gchar *ptr, *last_space_ptr, *tmp; - gint lines, xpos, color, indent_pos, last_space, last_space_color; + LINE_CACHE_REC *cache; - g_return_val_if_fail(gui != NULL, -1); - g_return_val_if_fail(line != NULL, -1); + g_return_if_fail(gui != NULL); + g_return_if_fail(line != NULL); - if (line->text == NULL) - return 0; - - move(ypos, 0); - xpos = 0; color = 0; lines = -1; indent_pos = DEFAULT_INDENT_POS; - last_space = last_space_color = 0; last_space_ptr = NULL; - for (ptr = line->text;; ptr++) - { - if (*ptr == '\0') - { - /* command */ - ptr++; - if ((*ptr & 0x80) == 0) - { - /* set color */ - color = (color & ATTR_UNDERLINE) | *ptr; - } - else switch ((guchar) *ptr) - { - case LINE_CMD_OVERFLOW: - g_error("buffer overflow!"); - case LINE_CMD_EOL: - return lines; - case LINE_CMD_CONTINUE: - memcpy(&tmp, ptr+1, sizeof(gchar *)); - ptr = tmp-1; - break; - case LINE_CMD_UNDERLINE: - color ^= ATTR_UNDERLINE; - break; - case LINE_CMD_COLOR8: - color &= 0xfff0; - color |= 8|ATTR_COLOR8; - break; - case LINE_CMD_BEEP: - beep(); - break; - case LINE_CMD_INDENT: - indent_pos = xpos; - break; - } - set_color(color); - continue; + cache = g_hash_table_lookup(gui->line_cache, line); + if (cache != NULL) { + g_hash_table_remove(gui->line_cache, line); + g_free_not_null(cache->lines); + g_free(cache); } +} - if (xpos == COLS) - { - xpos = indent_pos >= COLS-5 ? DEFAULT_INDENT_POS : indent_pos; +int gui_window_get_linecount(GUI_WINDOW_REC *gui, LINE_REC *line) +{ + LINE_CACHE_REC *cache; - if (last_space > indent_pos && last_space > 10) - { - /* remove the last word */ - if (!skip) - { - move(ypos, last_space); - set_color(0); - clrtoeol(); - } + g_return_val_if_fail(gui != NULL, -1); + g_return_val_if_fail(line != NULL, -1); - /* skip backwards to draw the line again. */ - ptr = last_space_ptr; - color = last_space_color; - if (!skip) set_color(color); - while (*ptr == ' ') ptr++; - } - last_space = 0; + cache = g_hash_table_lookup(gui->line_cache, line); + if (cache == NULL) + cache = gui_window_line_cache(gui, line); - if (skip > 0) - { - if (--skip == 0) set_color(color); - } - else - { - if (lines == max) - return lines; - if (max != -1) - ypos++; - else - { - gui_window_newline(gui, TRUE); - ypos = gui->parent->first_line+gui->ypos; - } - lines++; - } - move(ypos, indent_pos); + return cache->count; +} - /* we could have \0.. */ - ptr--; - continue; - } +static void single_line_draw(GUI_WINDOW_REC *gui, int ypos, LINE_CACHE_SUB_REC *rec, const char *text, const char *text_end) +{ + char *tmp; + int xpos, color; - xpos++; - if (*ptr == ' ') - { - last_space = xpos-1; - last_space_color = color; - last_space_ptr = ptr+1; + if (rec == NULL) { + xpos = 0; color = 0; + } else { + xpos = rec->indent; + color = rec->color; } - if (skip) continue; - if (lines == -1) lines = 0; + move(ypos, xpos); + 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_OVERFLOW: + g_error("buffer overflow!"); + case LINE_CMD_EOL: + return; + case LINE_CMD_UNDERLINE: + color ^= ATTR_UNDERLINE; + break; + case LINE_CMD_COLOR8: + color &= 0xfff0; + color |= 8|ATTR_COLOR8; + break; + } + set_color(color); + text++; + continue; + } - if ((guchar) *ptr >= 32) - addch((guchar) *ptr); - else - { - /* low-ascii */ - set_color(ATTR_REVERSE); - addch(*ptr+'A'-1); - set_color(color); + if (xpos == COLS) { + /* there should be only spaces left */ + text++; + continue; + } + + if ((unsigned char) *text >= 32) + addch((unsigned char) *text); + else { + /* low-ascii */ + set_color(ATTR_REVERSE); + addch(*text+'A'-1); + set_color(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); + + 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); + } + + return cache->count; } void gui_window_redraw(WINDOW_REC *window) { - GUI_WINDOW_REC *gui; - GList *line; - gint ypos, lines, skip, max; + GUI_WINDOW_REC *gui; + GList *line; + int ypos, lines, skip, max; - g_return_if_fail(window != NULL); + g_return_if_fail(window != NULL); - gui = WINDOW_GUI(window); + gui = WINDOW_GUI(window); - for (ypos = gui->parent->first_line; ypos <= gui->parent->last_line; ypos++) - { + /* clear the lines first */ set_color(0); - move(ypos, 0); - clrtoeol(); - } + for (ypos = gui->parent->first_line; ypos <= gui->parent->last_line; ypos++) { + move(ypos, 0); + clrtoeol(); + } - skip = gui->subline; - ypos = gui->parent->first_line; - for (line = gui->startline; line != NULL; line = line->next) - { - LINE_REC *rec = line->data; + skip = gui->subline; + ypos = gui->parent->first_line; + for (line = gui->startline; line != NULL; line = line->next) { + LINE_REC *rec = line->data; - max = gui->parent->last_line - ypos; - if (max < 0) break; + max = gui->parent->last_line - ypos+1; + if (max < 0) break; - lines = gui_window_line_draw(gui, rec, ypos, skip, max); - skip = 0; + lines = gui_window_line_draw(gui, rec, ypos, skip, max); + ypos += lines-skip; + skip = 0; + } - ypos += lines+1; - } - screen_refresh(); + screen_refresh(); } static void gui_window_scroll_up(GUI_WINDOW_REC *gui, gint lines) @@ -579,6 +606,8 @@ static void signal_window_changed(WINDOW_REC *window) { g_return_if_fail(window != NULL); + if (quitting) return; + if (is_window_visible(window)) { /* already visible, great! */ active_mainwin = WINDOW_GUI(window)->parent; @@ -674,6 +703,8 @@ static void gui_window_horiz_resize(WINDOW_REC *window) 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, g_list_last(gui->lines)->data); gui->last_subline = linecount-1; @@ -703,7 +734,8 @@ void gui_window_resize(WINDOW_REC *window, int ychange, int xchange) gui = WINDOW_GUI(window); if (xchange) { - /* window width changed, we'll need to recalculate a few things.. */ + /* window width changed, we'll need to recalculate a + few things.. */ gui_window_horiz_resize(window); return; } |