2 textbuffer-view.c : Text buffer handling
4 Copyright (C) 1999-2001 Timo Sirainen
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "textbuffer-view.h"
30 /* how often to scan line cache for lines not accessed for a while (ms) */
31 #define LINE_CACHE_CHECK_TIME (5*60*1000)
32 /* how long to keep line cache in memory (seconds) */
33 #define LINE_CACHE_KEEP_TIME (10*60)
35 static int linecache_tag;
38 #define view_is_bottom(view) \
39 ((view)->ypos >= -1 && (view)->ypos < (view)->height)
41 #define view_get_linecount(view, line) \
42 textbuffer_view_get_line_cache(view, line)->count
44 static GSList *textbuffer_get_views(TEXT_BUFFER_REC *buffer)
48 for (tmp = views; tmp != NULL; tmp = tmp->next) {
49 TEXT_BUFFER_VIEW_REC *view = tmp->data;
51 if (view->buffer == buffer) {
52 list = g_slist_copy(view->siblings);
53 return g_slist_prepend(list, view);
60 static TEXT_BUFFER_CACHE_REC *
61 textbuffer_cache_get(GSList *views, int width)
63 TEXT_BUFFER_CACHE_REC *cache;
65 /* check if there's existing cache with correct width */
66 while (views != NULL) {
67 TEXT_BUFFER_VIEW_REC *view = views->data;
69 if (view->width == width) {
70 view->cache->refcount++;
76 /* create new cache */
77 cache = g_new0(TEXT_BUFFER_CACHE_REC, 1);
80 cache->line_cache = g_hash_table_new((GHashFunc) g_direct_hash,
81 (GCompareFunc) g_direct_equal);
85 static int line_cache_destroy(void *key, LINE_CACHE_REC *cache)
91 static void textbuffer_cache_destroy(TEXT_BUFFER_CACHE_REC *cache)
93 g_hash_table_foreach(cache->line_cache,
94 (GHFunc) line_cache_destroy, NULL);
95 g_hash_table_destroy(cache->line_cache);
99 static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *cache)
101 if (--cache->refcount == 0)
102 textbuffer_cache_destroy(cache);
105 static LINE_CACHE_REC *
106 view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
109 LINE_CACHE_SUB_REC *sub;
112 char *ptr, *last_space_ptr;
113 int xpos, pos, indent_pos, last_space, last_color, color, linecount;
115 g_return_val_if_fail(line->text != NULL, NULL);
117 xpos = 0; color = 0; indent_pos = view->default_indent;
118 last_space = last_color = 0; last_space_ptr = NULL; sub = NULL;
122 for (ptr = line->text;;) {
129 if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT)
132 if (cmd == LINE_CMD_CONTINUE) {
135 memcpy(&tmp, ptr, sizeof(char *));
140 if ((cmd & 0x80) == 0) {
142 color = (color & ATTR_UNDERLINE) | cmd;
143 } else switch (cmd) {
144 case LINE_CMD_UNDERLINE:
145 color ^= ATTR_UNDERLINE;
147 case LINE_CMD_COLOR0:
148 color = color & ATTR_UNDERLINE;
150 case LINE_CMD_COLOR8:
152 color |= 8|ATTR_COLOR8;
157 case LINE_CMD_INDENT:
158 /* set indentation position here - don't do
159 it if we're too close to right border */
160 if (xpos < view->width-5) indent_pos = xpos;
166 if (xpos == view->width && sub != NULL &&
167 (last_space <= indent_pos || last_space <= 10) &&
168 !view->longword_noindent) {
169 /* long word, remove the indentation from this line */
174 if (xpos == view->width) {
177 sub = g_new0(LINE_CACHE_SUB_REC, 1);
178 if (last_space > indent_pos && last_space > 10) {
179 /* go back to last space */
181 ptr = last_space_ptr;
182 while (*ptr == ' ') ptr++;
183 } else if (!view->longword_noindent) {
184 /* long word, no indentation in next line */
186 sub->continues = TRUE;
193 lines = g_slist_append(lines, sub);
203 last_space_ptr = ptr;
208 rec = g_malloc(sizeof(LINE_CACHE_REC)-sizeof(LINE_CACHE_SUB_REC) +
209 sizeof(LINE_CACHE_SUB_REC) * (linecount-1));
210 rec->last_access = time(NULL);
211 rec->count = linecount;
213 if (rec->count > 1) {
214 for (pos = 0; lines != NULL; pos++) {
215 memcpy(&rec->lines[pos], lines->data,
216 sizeof(LINE_CACHE_SUB_REC));
219 lines = g_slist_remove(lines, lines->data);
223 g_hash_table_insert(view->cache->line_cache, line, rec);
227 static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
228 int subline, int ypos, int max)
230 LINE_CACHE_REC *cache;
231 const unsigned char *text, *text_newline;
233 int xpos, color, drawcount, first;
235 cache = textbuffer_view_get_line_cache(view, line);
236 if (subline >= cache->count)
239 xpos = color = drawcount = 0; first = TRUE;
240 text_newline = text =
241 subline == 0 ? line->text : cache->lines[subline-1].start;
243 if (text == text_newline) {
253 xpos = cache->lines[subline-1].indent;
254 color = cache->lines[subline-1].color;
257 set_color(view->window, 0);
258 wmove(view->window, ypos, 0);
259 wclrtoeol(view->window);
261 wmove(view->window, ypos, xpos);
262 set_color(view->window, color);
264 /* get the beginning of the next subline */
265 text_newline = subline == cache->count-1 ? NULL :
266 cache->lines[subline].start;
275 if (*text == LINE_CMD_EOL || *text == LINE_CMD_FORMAT)
278 if ((*text & 0x80) == 0) {
280 color = (color & ATTR_UNDERLINE) | *text;
281 } else if (*text == LINE_CMD_CONTINUE) {
282 /* jump to next block */
283 memcpy(&tmp, text+1, sizeof(unsigned char *));
286 } else switch (*text) {
287 case LINE_CMD_UNDERLINE:
288 color ^= ATTR_UNDERLINE;
290 case LINE_CMD_COLOR0:
291 color = color & ATTR_UNDERLINE;
293 case LINE_CMD_COLOR8:
295 color |= 8|ATTR_COLOR8;
301 set_color(view->window, color);
306 if ((*text & 127) >= 32)
307 waddch(view->window, *text);
310 set_color(view->window, ATTR_REVERSE);
311 waddch(view->window, (*text & 127)+'A'-1);
312 set_color(view->window, color);
320 /* Recalculate view's bottom line information - try to keep the
321 original if possible */
322 static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
325 int linecount, total;
327 if (view->empty_linecount == 0) {
328 /* no empty lines in screen, no need to try to keep
329 the old bottom startline */
330 view->bottom_startline = NULL;
334 tmp = g_list_last(view->buffer->lines);
335 for (; tmp != NULL; tmp = tmp->prev) {
336 LINE_REC *line = tmp->data;
338 linecount = view_get_linecount(view, line);
339 if (tmp == view->bottom_startline) {
340 /* keep the old one, make sure that subline is ok */
341 if (view->bottom_subline > linecount)
342 view->bottom_subline = linecount;
343 view->empty_linecount = view->height - total -
344 (linecount-view->bottom_subline);
349 if (total >= view->height) {
350 view->bottom_startline = tmp;
351 view->bottom_subline = total - view->height;
352 view->empty_linecount = 0;
357 /* not enough lines so we must be at the beginning of the buffer */
358 view->bottom_startline = view->buffer->lines;
359 view->bottom_subline = 0;
360 view->empty_linecount = view->height - total;
363 static void textbuffer_view_init_ypos(TEXT_BUFFER_VIEW_REC *view)
367 g_return_if_fail(view != NULL);
369 view->ypos = -view->subline-1;
370 for (tmp = view->startline; tmp != NULL; tmp = tmp->next)
371 view->ypos += view_get_linecount(view, tmp->data);
374 /* Create new view. */
375 TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
376 int width, int height,
378 int longword_noindent)
380 TEXT_BUFFER_VIEW_REC *view;
382 g_return_val_if_fail(buffer != NULL, NULL);
383 g_return_val_if_fail(width > 0, NULL);
385 view = g_new0(TEXT_BUFFER_VIEW_REC, 1);
386 view->buffer = buffer;
387 view->siblings = textbuffer_get_views(buffer);
390 view->height = height;
391 view->default_indent = default_indent;
392 view->longword_noindent = longword_noindent;
394 view->cache = textbuffer_cache_get(view->siblings, width);
395 textbuffer_view_init_bottom(view);
397 view->startline = view->bottom_startline;
398 view->subline = view->bottom_subline;
401 textbuffer_view_init_ypos(view);
403 view->bookmarks = g_hash_table_new((GHashFunc) g_str_hash,
404 (GCompareFunc) g_str_equal);
406 views = g_slist_append(views, view);
410 /* Destroy the view. */
411 void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view)
415 g_return_if_fail(view != NULL);
417 views = g_slist_remove(views, view);
419 if (view->siblings == NULL) {
420 /* last view for textbuffer, destroy */
421 textbuffer_destroy(view->buffer);
423 /* remove ourself from siblings lists */
424 for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
425 TEXT_BUFFER_VIEW_REC *rec = tmp->data;
427 rec->siblings = g_slist_remove(rec->siblings, view);
429 g_slist_free(view->siblings);
432 g_hash_table_foreach(view->bookmarks, (GHFunc) g_free, NULL);
433 g_hash_table_destroy(view->bookmarks);
435 textbuffer_cache_unref(view->cache);
439 /* Change the default indent position */
440 void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
442 int longword_noindent)
444 view->default_indent = default_indent;
445 view->longword_noindent = longword_noindent;
448 static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, GList *lines)
453 while (lines != NULL) {
454 linecount += view_get_linecount(view, lines->data);
461 static void view_draw(TEXT_BUFFER_VIEW_REC *view, GList *line,
462 int subline, int ypos, int lines)
466 while (line != NULL && lines > 0) {
467 LINE_REC *rec = line->data;
469 linecount = view_line_draw(view, rec, subline, ypos, lines);
470 ypos += linecount; lines -= linecount;
476 /* clear the rest of the view */
478 wmove(view->window, ypos, 0);
479 wclrtoeol(view->window);
484 #define view_draw_top(view, lines) \
485 view_draw(view, (view)->startline, (view)->subline, 0, lines)
487 static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines)
490 int ypos, maxline, subline, linecount;
492 maxline = view->height-lines;
493 line = view->startline; ypos = -view->subline; subline = 0;
494 while (line != NULL && ypos < maxline) {
495 linecount = view_get_linecount(view, line->data);
497 if (ypos > maxline) {
498 subline = maxline-(ypos-linecount);
504 view_draw(view, line, subline, maxline, lines);
507 /* Returns number of lines actually scrolled */
508 static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
509 int scrollcount, int draw_nonclean)
511 int linecount, realcount, scroll_visible;
517 scroll_visible = lines == &view->startline;
519 realcount = -*subline;
520 scrollcount += *subline;
522 while (scrollcount > 0) {
523 linecount = view_get_linecount(view, (*lines)->data);
525 if ((scroll_visible && *lines == view->bottom_startline) &&
526 (scrollcount >= view->bottom_subline)) {
527 *subline = view->bottom_subline;
528 realcount += view->bottom_subline;
533 realcount += linecount;
534 scrollcount -= linecount;
535 if (scrollcount < 0) {
536 realcount += scrollcount;
537 *subline = linecount+scrollcount;
542 *lines = (*lines)->next;
546 while (scrollcount < 0 && (*lines)->prev != NULL) {
547 *lines = (*lines)->prev;
548 linecount = view_get_linecount(view, (*lines)->data);
550 realcount -= linecount;
551 scrollcount += linecount;
552 if (scrollcount > 0) {
553 realcount += scrollcount;
554 *subline = scrollcount;
559 if (scroll_visible && realcount != 0 && view->window != NULL) {
560 if (realcount <= -view->height || realcount >= view->height) {
561 /* scrolled more than screenful, redraw the
563 textbuffer_view_redraw(view);
565 scrollok(view->window, TRUE);
566 wscrl(view->window, realcount);
567 scrollok(view->window, FALSE);
571 view_draw_top(view, -realcount);
573 view_draw_bottom(view, realcount);
576 screen_refresh(view->window);
580 return realcount >= 0 ? realcount : -realcount;
583 /* Resize the view. */
584 void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height)
588 g_return_if_fail(view != NULL);
589 g_return_if_fail(width > 0);
591 if (view->width != width) {
592 /* line cache needs to be recreated */
593 textbuffer_cache_unref(view->cache);
594 view->cache = textbuffer_cache_get(view->siblings, width);
598 view->height = height;
601 if (view->buffer->lines == NULL) {
602 view->empty_linecount = height;
606 textbuffer_view_init_bottom(view);
608 /* check that we didn't scroll lower than bottom startline.. */
609 if (g_list_find(view->bottom_startline->next,
610 view->startline->data) != NULL) {
611 view->startline = view->bottom_startline;
612 view->subline = view->bottom_subline;
613 } else if (view->startline == view->bottom_startline &&
614 view->subline > view->bottom_subline) {
615 view->subline = view->bottom_subline;
617 /* make sure the subline is still in allowed range */
618 linecount = view_get_linecount(view, view->startline->data);
619 if (view->subline > linecount)
620 view->subline = linecount;
623 textbuffer_view_init_ypos(view);
624 if (view->bottom && !view_is_bottom(view)) {
625 /* we scrolled to far up, need to get down. go right over
626 the empty lines if there's any */
627 view->startline = view->bottom_startline;
628 view->subline = view->bottom_subline;
629 if (view->empty_linecount > 0) {
630 view_scroll(view, &view->startline, &view->subline,
631 -view->empty_linecount, FALSE);
633 textbuffer_view_init_ypos(view);
636 view->bottom = view_is_bottom(view);
638 /* check if we left empty space at the bottom.. */
639 linecount = view_get_linecount_all(view, view->startline) -
641 if (view->empty_linecount < view->height-linecount)
642 view->empty_linecount = view->height-linecount;
645 textbuffer_view_redraw(view);
648 /* Clear the view, don't actually remove any lines from buffer. */
649 void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view)
651 g_return_if_fail(view != NULL);
654 view->bottom_startline = view->startline =
655 g_list_last(view->buffer->lines);
656 view->bottom_subline = view->subline =
657 view->buffer->cur_line == NULL ? 0 :
658 view_get_linecount(view, view->buffer->cur_line);
659 view->empty_linecount = view->height;
662 textbuffer_view_redraw(view);
665 /* Scroll the view up/down */
666 void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines)
670 g_return_if_fail(view != NULL);
672 count = view_scroll(view, &view->startline, &view->subline,
674 view->ypos += lines < 0 ? count : -count;
675 view->bottom = view_is_bottom(view);
677 if (view->window != NULL)
678 screen_refresh(view->window);
681 /* Scroll to specified line */
682 void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
686 g_return_if_fail(view != NULL);
688 if (g_list_find(view->bottom_startline->next, line) != NULL) {
689 view->startline = view->bottom_startline;
690 view->subline = view->bottom_subline;
692 for (tmp = view->buffer->lines; tmp != NULL; tmp = tmp->next) {
693 LINE_REC *rec = tmp->data;
696 view->startline = tmp;
703 textbuffer_view_init_ypos(view);
704 view->bottom = view_is_bottom(view);
706 textbuffer_view_redraw(view);
709 /* Return line cache */
710 LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view,
713 LINE_CACHE_REC *cache;
715 g_return_val_if_fail(view != NULL, NULL);
716 g_return_val_if_fail(line != NULL, NULL);
718 cache = g_hash_table_lookup(view->cache->line_cache, line);
720 cache = view_update_line_cache(view, line);
722 cache->last_access = time(NULL);
727 static void view_remove_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
728 unsigned char update_counter)
730 LINE_CACHE_REC *cache;
732 if (view->cache->update_counter == update_counter)
734 view->cache->update_counter = update_counter;
736 cache = g_hash_table_lookup(view->cache->line_cache, line);
739 g_hash_table_remove(view->cache->line_cache, line);
743 static void view_update_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
744 unsigned char update_counter)
746 view_remove_cache(view, line, update_counter);
748 if (view->buffer->cur_line == line)
749 view->cache->last_linecount = view_get_linecount(view, line);
752 static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
754 int linecount, ypos, subline;
756 if (view->bottom_startline == NULL) {
757 view->startline = view->bottom_startline =
761 if (view->buffer->cur_line != line &&
762 g_list_find(view->bottom_startline, line) == NULL)
765 linecount = view->cache->last_linecount;
766 view->ypos += linecount;
767 if (view->empty_linecount > 0) {
768 view->empty_linecount -= linecount;
769 if (view->empty_linecount >= 0)
772 linecount = -view->empty_linecount;
773 view->empty_linecount = 0;
778 view_scroll(view, &view->bottom_startline,
779 &view->bottom_subline, linecount, FALSE);
783 if (view->ypos >= view->height) {
784 linecount = view->ypos-view->height+1;
785 view_scroll(view, &view->startline,
786 &view->subline, linecount, FALSE);
787 view->ypos -= linecount;
790 if (view->window != NULL) {
791 ypos = view->ypos+1 - view->cache->last_linecount;
798 view_line_draw(view, line, subline, ypos,
799 view->height - ypos);
803 if (view->window != NULL)
804 screen_refresh(view->window);
807 /* Update some line in the buffer which has been modified using
808 textbuffer_append() or textbuffer_insert(). */
809 void textbuffer_view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
812 unsigned char update_counter;
814 g_return_if_fail(view != NULL);
815 g_return_if_fail(line != NULL);
817 if (!view->buffer->last_eol)
820 update_counter = view->cache->update_counter+1;
821 view_update_cache(view, line, update_counter);
822 view_insert_line(view, line);
824 for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
825 TEXT_BUFFER_VIEW_REC *rec = tmp->data;
827 view_update_cache(rec, line, update_counter);
828 view_insert_line(rec, line);
833 LINE_REC *remove_line;
837 static void bookmark_check_remove(char *key, LINE_REC *line,
838 BOOKMARK_FIND_REC *rec)
840 if (line == rec->remove_line)
841 rec->remove_list = g_slist_append(rec->remove_list, key);
844 static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
846 BOOKMARK_FIND_REC rec;
850 rec.remove_line = line;
851 rec.remove_list = NULL;
852 g_hash_table_foreach(view->bookmarks,
853 (GHFunc) bookmark_check_remove, &rec);
855 if (rec.remove_list != NULL) {
856 GList *pos = g_list_find(view->buffer->lines, line);
858 new_line = pos == NULL || pos->prev == NULL ? NULL :
859 (pos->next == NULL ? pos->prev->data :
861 for (tmp = rec.remove_list; tmp != NULL; tmp = tmp->next) {
862 g_hash_table_remove(view->bookmarks, tmp->data);
863 if (new_line != NULL) {
864 g_hash_table_insert(view->bookmarks,
865 tmp->data, new_line);
868 g_slist_free(rec.remove_list);
872 /* Return number of real lines `lines' list takes -
873 stops counting when the height reaches the view height */
874 static int view_get_lines_height(TEXT_BUFFER_VIEW_REC *view,
875 GList *lines, int subline,
878 int height, linecount;
881 while (lines != NULL && height < view->height) {
882 LINE_REC *line = lines->data;
884 if (line != skip_line) {
885 linecount = view_get_linecount(view, line);
891 return height < view->height ? height : view->height;
894 static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
897 int realcount, scroll;
899 view_bookmarks_check(view, line);
901 if (view->buffer->cur_line == line) {
902 /* the last line is being removed */
905 prevline = view->buffer->lines->data == line ? NULL :
906 g_list_last(view->bottom_startline)->data;
907 view->cache->last_linecount = prevline == NULL ? 0 :
908 view_get_linecount(view, prevline);
911 if (line == view->buffer->lines->data) {
912 /* first line in the buffer - this is the most commonly
914 if (view->bottom_startline->data == line) {
915 /* very small scrollback.. */
916 view->bottom_startline = view->bottom_startline->next;
917 view->bottom_subline = 0;
920 if (view->startline->data == line) {
921 /* removing the first line in screen */
922 realcount = view_scroll(view, &view->startline,
925 view->ypos -= realcount;
926 view->empty_linecount += linecount-realcount;
928 } else if (g_list_find(view->bottom_startline, line) != NULL) {
929 realcount = view_scroll(view, &view->bottom_startline,
930 &view->bottom_subline,
933 /* we're at the bottom, remove the same amount as
934 from bottom_startline */
935 view_scroll(view, &view->startline,
936 &view->subline, -linecount, TRUE);
937 view->ypos -= linecount-realcount;
939 if (view->startline->data == line) {
941 view->startline->next != NULL ?
942 view->startline->next :
943 view->startline->prev;
946 scroll = view->height -
947 view_get_lines_height(view, view->startline,
948 view->subline, line);
950 view_scroll(view, &view->startline,
951 &view->subline, -scroll, TRUE);
952 view->ypos -= scroll;
955 view->empty_linecount += linecount-realcount;
958 view->bottom = view_is_bottom(view);
959 if (view->window != NULL)
960 screen_refresh(view->window);
963 /* Remove one line from buffer. */
964 void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
967 unsigned char update_counter;
970 g_return_if_fail(view != NULL);
971 g_return_if_fail(line != NULL);
973 linecount = view_get_linecount(view, line);
974 update_counter = view->cache->update_counter+1;
976 view_remove_line(view, line, linecount);
977 view_remove_cache(view, line, update_counter);
979 for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
980 TEXT_BUFFER_VIEW_REC *rec = tmp->data;
982 view_remove_line(rec, line, linecount);
983 view_remove_cache(rec, line, update_counter);
986 textbuffer_remove(view->buffer, line);
989 /* Remove all lines from buffer. */
990 void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view)
994 g_return_if_fail(view != NULL);
996 textbuffer_remove_all_lines(view->buffer);
998 /* destroy line caches - note that you can't do simultaneously
999 unrefs + cache_get()s or it will keep using the old caches */
1000 textbuffer_cache_unref(view->cache);
1001 g_slist_foreach(view->siblings, (GFunc) textbuffer_cache_unref, NULL);
1003 /* recreate caches, clear screens */
1004 view->cache = textbuffer_cache_get(view->siblings, view->width);
1005 textbuffer_view_clear(view);
1007 for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
1008 TEXT_BUFFER_VIEW_REC *rec = tmp->data;
1010 rec->cache = textbuffer_cache_get(rec->siblings, rec->width);
1011 textbuffer_view_clear(rec);
1015 /* Set a bookmark in view */
1016 void textbuffer_view_set_bookmark(TEXT_BUFFER_VIEW_REC *view,
1017 const char *name, LINE_REC *line)
1019 gpointer key, value;
1021 g_return_if_fail(view != NULL);
1022 g_return_if_fail(name != NULL);
1024 if (g_hash_table_lookup_extended(view->bookmarks, name,
1026 g_hash_table_remove(view->bookmarks, key);
1030 g_hash_table_insert(view->bookmarks, g_strdup(name), line);
1033 /* Set a bookmark in view to the bottom line */
1034 void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view,
1039 g_return_if_fail(view != NULL);
1040 g_return_if_fail(name != NULL);
1042 if (view->bottom_startline != NULL) {
1043 line = g_list_last(view->bottom_startline)->data;
1044 textbuffer_view_set_bookmark(view, name, line);
1048 /* Return the line for bookmark */
1049 LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
1052 g_return_val_if_fail(view != NULL, NULL);
1053 g_return_val_if_fail(name != NULL, NULL);
1055 return g_hash_table_lookup(view->bookmarks, name);
1058 /* Specify window where the changes in view should be drawn,
1059 NULL disables it. */
1060 void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, WINDOW *window)
1062 g_return_if_fail(view != NULL);
1064 if (view->window != window) {
1065 view->window = window;
1067 textbuffer_view_redraw(view);
1071 /* Redraw a view to window */
1072 void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view)
1074 g_return_if_fail(view != NULL);
1076 if (view->window != NULL) {
1077 werase(view->window);
1078 view_draw_top(view, view->height);
1079 screen_refresh(view->window);
1083 static int line_cache_check_remove(void *key, LINE_CACHE_REC *cache,
1086 if (cache->last_access+LINE_CACHE_KEEP_TIME > *now)
1089 line_cache_destroy(NULL, cache);
1093 static int sig_check_linecache(void)
1095 GSList *tmp, *caches;
1098 now = time(NULL); caches = NULL;
1099 for (tmp = views; tmp != NULL; tmp = tmp->next) {
1100 TEXT_BUFFER_VIEW_REC *rec = tmp->data;
1102 if (g_slist_find(caches, rec->cache) != NULL)
1105 caches = g_slist_append(caches, rec->cache);
1106 g_hash_table_foreach_remove(rec->cache->line_cache,
1107 (GHRFunc) line_cache_check_remove,
1113 void textbuffer_view_init(void)
1115 linecache_tag = g_timeout_add(LINE_CACHE_CHECK_TIME, (GSourceFunc) sig_check_linecache, NULL);
1118 void textbuffer_view_deinit(void)
1120 g_source_remove(linecache_tag);