File: | fe-text/textbuffer-view.c |
Location: | line 414, column 7 |
Description: | Dereference of null pointer. |
1 | /* |
2 | textbuffer-view.c : Text buffer handling |
3 | |
4 | Copyright (C) 1999-2001 Timo Sirainen |
5 | |
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. |
10 | |
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. |
15 | |
16 | You should have received a copy of the GNU General Public License along |
17 | with this program; if not, write to the Free Software Foundation, Inc., |
18 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ |
20 | |
21 | #define G_LOG_DOMAIN"TextBufferView" "TextBufferView" |
22 | |
23 | #include "module.h" |
24 | #include "textbuffer-view.h" |
25 | #include "utf8.h" |
26 | |
27 | typedef struct { |
28 | char *name; |
29 | LINE_REC *line; |
30 | } BOOKMARK_REC; |
31 | |
32 | /* how often to scan line cache for lines not accessed for a while (ms) */ |
33 | #define LINE_CACHE_CHECK_TIME(5*60*1000) (5*60*1000) |
34 | /* how long to keep line cache in memory (seconds) */ |
35 | #define LINE_CACHE_KEEP_TIME(10*60) (10*60) |
36 | |
37 | static int linecache_tag; |
38 | static GSList *views; |
39 | |
40 | #define view_is_bottom(view)((view)->ypos >= -1 && (view)->ypos < (view )->height) \ |
41 | ((view)->ypos >= -1 && (view)->ypos < (view)->height) |
42 | |
43 | #define view_get_linecount(view, line)textbuffer_view_get_line_cache(view, line)->count \ |
44 | textbuffer_view_get_line_cache(view, line)->count |
45 | |
46 | static GSList *textbuffer_get_views(TEXT_BUFFER_REC *buffer) |
47 | { |
48 | GSList *tmp, *list; |
49 | |
50 | for (tmp = views; tmp != NULL((void *)0); tmp = tmp->next) { |
51 | TEXT_BUFFER_VIEW_REC *view = tmp->data; |
52 | |
53 | if (view->buffer == buffer) { |
54 | list = g_slist_copy(view->siblings); |
55 | return g_slist_prepend(list, view); |
56 | } |
57 | } |
58 | |
59 | return NULL((void *)0); |
60 | } |
61 | |
62 | static TEXT_BUFFER_CACHE_REC * |
63 | textbuffer_cache_get(GSList *views, int width) |
64 | { |
65 | TEXT_BUFFER_CACHE_REC *cache; |
66 | |
67 | /* check if there's existing cache with correct width */ |
68 | while (views != NULL((void *)0)) { |
69 | TEXT_BUFFER_VIEW_REC *view = views->data; |
70 | |
71 | if (view->width == width) { |
72 | view->cache->refcount++; |
73 | return view->cache; |
74 | } |
75 | views = views->next; |
76 | } |
77 | |
78 | /* create new cache */ |
79 | cache = g_new0(TEXT_BUFFER_CACHE_REC, 1)((TEXT_BUFFER_CACHE_REC *) g_malloc0 (((gsize) sizeof (TEXT_BUFFER_CACHE_REC )) * ((gsize) (1)))); |
80 | cache->refcount = 1; |
81 | cache->width = width; |
82 | cache->line_cache = g_hash_table_new((GHashFunc) g_direct_hash, |
83 | (GCompareFunc) g_direct_equal); |
84 | return cache; |
85 | } |
86 | |
87 | static int line_cache_destroy(void *key, LINE_CACHE_REC *cache) |
88 | { |
89 | g_free(cache); |
90 | return TRUE(!(0)); |
91 | } |
92 | |
93 | static void textbuffer_cache_destroy(TEXT_BUFFER_CACHE_REC *cache) |
94 | { |
95 | g_hash_table_foreach(cache->line_cache, |
96 | (GHFunc) line_cache_destroy, NULL((void *)0)); |
97 | g_hash_table_destroy(cache->line_cache); |
98 | g_free(cache); |
99 | } |
100 | |
101 | static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *cache) |
102 | { |
103 | if (--cache->refcount == 0) |
104 | textbuffer_cache_destroy(cache); |
105 | } |
106 | |
107 | #define FGATTR((0x1000|0x2000|0x0800|0x0400) | 0x0100 | 0x0f) (ATTR_NOCOLORS(0x1000|0x2000|0x0800|0x0400) | ATTR_RESETFG0x0100 | 0x0f) |
108 | #define BGATTR((0x1000|0x2000|0x0800|0x0400) | 0x0200 | 0xf0) (ATTR_NOCOLORS(0x1000|0x2000|0x0800|0x0400) | ATTR_RESETBG0x0200 | 0xf0) |
109 | |
110 | static void update_cmd_color(unsigned char cmd, int *color) |
111 | { |
112 | if ((cmd & 0x80) == 0) { |
113 | if (cmd & LINE_COLOR_BG0x20) { |
114 | /* set background color */ |
115 | *color &= FGATTR((0x1000|0x2000|0x0800|0x0400) | 0x0100 | 0x0f); |
116 | if ((cmd & LINE_COLOR_DEFAULT0x10) == 0) |
117 | *color |= (cmd & 0x0f) << 4; |
118 | else { |
119 | *color = (*color & FGATTR((0x1000|0x2000|0x0800|0x0400) | 0x0100 | 0x0f)) | ATTR_RESETBG0x0200; |
120 | } |
121 | } else { |
122 | /* set foreground color */ |
123 | *color &= BGATTR((0x1000|0x2000|0x0800|0x0400) | 0x0200 | 0xf0); |
124 | if ((cmd & LINE_COLOR_DEFAULT0x10) == 0) |
125 | *color |= cmd & 0x0f; |
126 | else { |
127 | *color = (*color & BGATTR((0x1000|0x2000|0x0800|0x0400) | 0x0200 | 0xf0)) | ATTR_RESETFG0x0100; |
128 | } |
129 | } |
130 | } else switch (cmd) { |
131 | case LINE_CMD_UNDERLINE: |
132 | *color ^= ATTR_UNDERLINE0x1000; |
133 | break; |
134 | case LINE_CMD_REVERSE: |
135 | *color ^= ATTR_REVERSE0x2000; |
136 | break; |
137 | case LINE_CMD_BLINK: |
138 | *color ^= ATTR_BLINK0x0800; |
139 | break; |
140 | case LINE_CMD_BOLD: |
141 | *color ^= ATTR_BOLD0x0400; |
142 | break; |
143 | case LINE_CMD_COLOR0: |
144 | *color &= BGATTR((0x1000|0x2000|0x0800|0x0400) | 0x0200 | 0xf0); |
145 | break; |
146 | } |
147 | } |
148 | |
149 | static inline unichar read_unichar(const unsigned char *data, const unsigned char **next, int *width) |
150 | { |
151 | unichar chr = g_utf8_get_char_validated(data, -1); |
152 | |
153 | if (chr & 0x80000000) { |
154 | chr = 0xfffd; |
155 | *next = data + 1; |
156 | *width = 1; |
157 | } else { |
158 | *next = g_utf8_next_char(data)(char *)((data) + g_utf8_skip[*(const guchar *)(data)]); |
159 | *width = unichar_isprint(chr)(((chr) & ~0x80) >= 32) ? mk_wcwidth(chr) : 1; |
160 | } |
161 | return chr; |
162 | } |
163 | |
164 | static LINE_CACHE_REC * |
165 | view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) |
166 | { |
167 | INDENT_FUNC indent_func; |
168 | LINE_CACHE_REC *rec; |
169 | LINE_CACHE_SUB_REC *sub; |
170 | GSList *lines; |
171 | unsigned char cmd; |
172 | const unsigned char *ptr, *next_ptr, *last_space_ptr; |
173 | int xpos, pos, indent_pos, last_space, last_color, color, linecount; |
174 | int char_width; |
175 | |
176 | g_return_val_if_fail(line->text != NULL, NULL)do{ if (line->text != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "line->text != NULL" ); return (((void *)0)); }; }while (0); |
177 | |
178 | color = ATTR_RESETFG0x0100 | ATTR_RESETBG0x0200; |
179 | xpos = 0; indent_pos = view->default_indent; |
180 | last_space = last_color = 0; last_space_ptr = NULL((void *)0); sub = NULL((void *)0); |
181 | |
182 | indent_func = view->default_indent_func; |
183 | linecount = 1; |
184 | lines = NULL((void *)0); |
185 | for (ptr = line->text;;) { |
186 | if (*ptr == '\0') { |
187 | /* command */ |
188 | ptr++; |
189 | cmd = *ptr; |
190 | ptr++; |
191 | |
192 | if (cmd == LINE_CMD_EOL) |
193 | break; |
194 | |
195 | if (cmd == LINE_CMD_CONTINUE) { |
196 | unsigned char *tmp; |
197 | |
198 | memcpy(&tmp, ptr, sizeof(char *)); |
199 | ptr = tmp; |
200 | continue; |
201 | } |
202 | |
203 | if (cmd == LINE_CMD_INDENT) { |
204 | /* set indentation position here - don't do |
205 | it if we're too close to right border */ |
206 | if (xpos < view->width-5) indent_pos = xpos; |
207 | } else |
208 | update_cmd_color(cmd, &color); |
209 | continue; |
210 | } |
211 | |
212 | if (!view->utf8) { |
213 | /* MH */ |
214 | if (term_type != TERM_TYPE_BIG52 || |
215 | ptr[1] == '\0' || !is_big5(ptr[0], ptr[1])((0x81 <= (ptr[0]) && (ptr[0]) <= 0xFE) && (((0x40 <= (ptr[1]) && (ptr[1]) <= 0x7E) || (0x80 <= (ptr[1]) && (ptr[1]) <= 0xFE))))) |
216 | char_width = 1; |
217 | else |
218 | char_width = 2; |
219 | next_ptr = ptr+char_width; |
220 | } else { |
221 | read_unichar(ptr, &next_ptr, &char_width); |
222 | } |
223 | |
224 | if (xpos + char_width > view->width && sub != NULL((void *)0) && |
225 | (last_space <= indent_pos || last_space <= 10) && |
226 | view->longword_noindent) { |
227 | /* long word, remove the indentation from this line */ |
228 | xpos -= sub->indent; |
229 | sub->indent = 0; |
230 | } |
231 | |
232 | if (xpos + char_width > view->width) { |
233 | xpos = indent_func == NULL((void *)0) ? indent_pos : |
234 | indent_func(view, line, -1); |
235 | |
236 | sub = g_new0(LINE_CACHE_SUB_REC, 1)((LINE_CACHE_SUB_REC *) g_malloc0 (((gsize) sizeof (LINE_CACHE_SUB_REC )) * ((gsize) (1)))); |
237 | if (last_space > indent_pos && last_space > 10) { |
238 | /* go back to last space */ |
239 | color = last_color; |
240 | ptr = last_space_ptr; |
241 | while (*ptr == ' ') ptr++; |
242 | } else if (view->longword_noindent) { |
243 | /* long word, no indentation in next line */ |
244 | xpos = 0; |
245 | sub->continues = TRUE(!(0)); |
246 | } |
247 | |
248 | sub->start = ptr; |
249 | sub->indent = xpos; |
250 | sub->indent_func = indent_func; |
251 | sub->color = color; |
252 | |
253 | lines = g_slist_append(lines, sub); |
254 | linecount++; |
255 | |
256 | last_space = 0; |
257 | continue; |
258 | } |
259 | |
260 | if (!view->utf8 && char_width > 1) { |
261 | last_space = xpos; |
262 | last_space_ptr = next_ptr; |
263 | last_color = color; |
264 | } else if (*ptr == ' ') { |
265 | last_space = xpos; |
266 | last_space_ptr = ptr; |
267 | last_color = color; |
268 | } |
269 | |
270 | xpos += char_width; |
271 | ptr = next_ptr; |
272 | } |
273 | |
274 | rec = g_malloc(sizeof(LINE_CACHE_REC)-sizeof(LINE_CACHE_SUB_REC) + |
275 | sizeof(LINE_CACHE_SUB_REC) * (linecount-1)); |
276 | rec->last_access = time(NULL((void *)0)); |
277 | rec->count = linecount; |
278 | |
279 | if (rec->count > 1) { |
280 | for (pos = 0; lines != NULL((void *)0); pos++) { |
281 | void *data = lines->data; |
282 | |
283 | memcpy(&rec->lines[pos], data, |
284 | sizeof(LINE_CACHE_SUB_REC)); |
285 | |
286 | lines = g_slist_remove(lines, data); |
287 | g_free(data); |
288 | } |
289 | } |
290 | |
291 | g_hash_table_insert(view->cache->line_cache, line, rec); |
292 | return rec; |
293 | } |
294 | |
295 | static void view_remove_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, |
296 | unsigned char update_counter) |
297 | { |
298 | LINE_CACHE_REC *cache; |
299 | |
300 | if (view->cache->update_counter == update_counter) |
301 | return; |
302 | view->cache->update_counter = update_counter; |
303 | |
304 | cache = g_hash_table_lookup(view->cache->line_cache, line); |
305 | if (cache != NULL((void *)0)) { |
306 | g_free(cache); |
307 | g_hash_table_remove(view->cache->line_cache, line); |
308 | } |
309 | } |
310 | |
311 | static void view_update_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, |
312 | unsigned char update_counter) |
313 | { |
314 | view_remove_cache(view, line, update_counter); |
315 | |
316 | if (view->buffer->cur_line == line) |
317 | view->cache->last_linecount = view_get_linecount(view, line)textbuffer_view_get_line_cache(view, line)->count; |
318 | } |
319 | |
320 | static void view_reset_cache(TEXT_BUFFER_VIEW_REC *view) |
321 | { |
322 | GSList *tmp; |
323 | |
324 | /* destroy line caches - note that you can't do simultaneously |
325 | unrefs + cache_get()s or it will keep using the old caches */ |
326 | textbuffer_cache_unref(view->cache); |
327 | g_slist_foreach(view->siblings, (GFunc) textbuffer_cache_unref, NULL((void *)0)); |
328 | |
329 | view->cache = textbuffer_cache_get(view->siblings, view->width); |
330 | for (tmp = view->siblings; tmp != NULL((void *)0); tmp = tmp->next) { |
331 | TEXT_BUFFER_VIEW_REC *rec = tmp->data; |
332 | |
333 | rec->cache = textbuffer_cache_get(rec->siblings, rec->width); |
334 | } |
335 | } |
336 | |
337 | static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, |
338 | int subline, int ypos, int max) |
339 | { |
340 | INDENT_FUNC indent_func; |
341 | LINE_CACHE_REC *cache; |
342 | const unsigned char *text, *end, *text_newline; |
343 | unsigned char *tmp; |
344 | unichar chr; |
345 | int xpos, color, drawcount, first, need_move, need_clrtoeol, char_width; |
346 | |
347 | if (view->dirty) /* don't bother drawing anything - redraw is coming */ |
[1] Taking false branch | |
348 | return 0; |
349 | |
350 | cache = textbuffer_view_get_line_cache(view, line); |
351 | if (subline >= cache->count) |
[2] Taking false branch | |
352 | return 0; |
353 | |
354 | color = ATTR_RESET(0x0100|0x0200); |
355 | need_move = TRUE(!(0)); need_clrtoeol = FALSE(0); |
356 | xpos = drawcount = 0; first = TRUE(!(0)); |
357 | text_newline = text = |
358 | subline == 0 ? line->text : cache->lines[subline-1].start; |
[3] '?' condition evaluates to false | |
359 | for (;;) { |
[4] Loop condition is true. Entering loop body | |
[18] Loop condition is true. Entering loop body | |
360 | if (text == text_newline) { |
[5] Taking true branch | |
[19] Taking true branch | |
361 | if (need_clrtoeol && xpos < term_width) { |
[6] Taking false branch | |
[20] Taking false branch | |
362 | term_set_color(view->window, ATTR_RESET(0x0100|0x0200)); |
363 | term_clrtoeol(view->window); |
364 | } |
365 | |
366 | if (first) |
[7] Taking true branch | |
[21] Taking false branch | |
367 | first = FALSE(0); |
368 | else { |
369 | ypos++; |
370 | if (--max == 0) |
[22] Taking false branch | |
371 | break; |
372 | } |
373 | |
374 | if (subline > 0) { |
[8] Taking true branch | |
[23] Taking false branch | |
375 | /* continuing previous line - indent it */ |
376 | indent_func = cache->lines[subline-1].indent_func; |
377 | if (indent_func == NULL((void *)0)) |
[9] Taking true branch | |
378 | xpos = cache->lines[subline-1].indent; |
379 | color = cache->lines[subline-1].color; |
380 | } else { |
381 | indent_func = NULL((void *)0); |
382 | } |
383 | |
384 | if (xpos == 0 && indent_func == NULL((void *)0)) |
[10] Taking false branch | |
[24] Taking false branch | |
385 | need_clrtoeol = TRUE(!(0)); |
386 | else { |
387 | /* line was indented - need to clear the |
388 | indented area first */ |
389 | term_set_color(view->window, ATTR_RESET(0x0100|0x0200)); |
390 | term_move(view->window, 0, ypos); |
391 | term_clrtoeol(view->window); |
392 | |
393 | if (indent_func != NULL((void *)0)) |
[11] Taking false branch | |
[25] Taking false branch | |
394 | xpos = indent_func(view, line, ypos); |
395 | } |
396 | |
397 | if (need_move || xpos > 0) |
[12] Taking true branch | |
[26] Taking false branch | |
398 | term_move(view->window, xpos, ypos); |
399 | |
400 | term_set_color(view->window, color); |
401 | |
402 | if (subline == cache->count-1) { |
[13] Taking true branch | |
[27] Taking false branch | |
403 | text_newline = NULL((void *)0); |
404 | need_move = FALSE(0); |
405 | } else { |
406 | /* get the beginning of the next subline */ |
407 | text_newline = cache->lines[subline].start; |
408 | need_move = !cache->lines[subline].continues; |
409 | } |
410 | drawcount++; |
411 | subline++; |
412 | } |
413 | |
414 | if (*text == '\0') { |
[14] Taking true branch | |
[28] Dereference of null pointer | |
415 | /* command */ |
416 | text++; |
417 | if (*text == LINE_CMD_EOL) |
[15] Taking false branch | |
418 | break; |
419 | |
420 | if (*text == LINE_CMD_CONTINUE) { |
[16] Taking true branch | |
421 | /* jump to next block */ |
422 | memcpy(&tmp, text+1, sizeof(unsigned char *)); |
423 | text = tmp; |
424 | continue; |
[17] Execution continues on line 359 | |
425 | } else { |
426 | update_cmd_color(*text, &color); |
427 | term_set_color(view->window, color); |
428 | } |
429 | text++; |
430 | continue; |
431 | } |
432 | |
433 | if (view->utf8) { |
434 | chr = read_unichar(text, &end, &char_width); |
435 | } else { |
436 | chr = *text; |
437 | end = text; |
438 | if (term_type == TERM_TYPE_BIG52 && |
439 | is_big5(end[0], end[1])((0x81 <= (end[0]) && (end[0]) <= 0xFE) && (((0x40 <= (end[1]) && (end[1]) <= 0x7E) || (0x80 <= (end[1]) && (end[1]) <= 0xFE))))) |
440 | char_width = 2; |
441 | else |
442 | char_width = 1; |
443 | end += char_width; |
444 | } |
445 | |
446 | xpos += char_width; |
447 | if (xpos <= term_width) { |
448 | if (unichar_isprint(chr)(((chr) & ~0x80) >= 32)) { |
449 | if (view->utf8) |
450 | term_add_unichar(view->window, chr); |
451 | else |
452 | for (; text < end; text++) |
453 | term_addch(view->window, *text); |
454 | } else { |
455 | /* low-ascii */ |
456 | term_set_color(view->window, ATTR_RESET(0x0100|0x0200)|ATTR_REVERSE0x2000); |
457 | term_addch(view->window, (chr & 127)+'A'-1); |
458 | term_set_color(view->window, color); |
459 | } |
460 | } |
461 | text = end; |
462 | } |
463 | |
464 | if (need_clrtoeol && xpos < term_width) { |
465 | term_set_color(view->window, ATTR_RESET(0x0100|0x0200)); |
466 | term_clrtoeol(view->window); |
467 | } |
468 | |
469 | return drawcount; |
470 | } |
471 | |
472 | /* Recalculate view's bottom line information - try to keep the |
473 | original if possible */ |
474 | static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view) |
475 | { |
476 | LINE_REC *line; |
477 | int linecount, total; |
478 | |
479 | if (view->empty_linecount == 0) { |
480 | /* no empty lines in screen, no need to try to keep |
481 | the old bottom startline */ |
482 | view->bottom_startline = NULL((void *)0); |
483 | } |
484 | |
485 | total = 0; |
486 | line = textbuffer_line_last(view->buffer); |
487 | for (; line != NULL((void *)0); line = line->prev) { |
488 | linecount = view_get_linecount(view, line)textbuffer_view_get_line_cache(view, line)->count; |
489 | if (line == view->bottom_startline) { |
490 | /* keep the old one, make sure that subline is ok */ |
491 | if (view->bottom_subline > linecount) |
492 | view->bottom_subline = linecount; |
493 | view->empty_linecount = view->height - total - |
494 | (linecount-view->bottom_subline); |
495 | return; |
496 | } |
497 | |
498 | total += linecount; |
499 | if (total >= view->height) { |
500 | view->bottom_startline = line; |
501 | view->bottom_subline = total - view->height; |
502 | view->empty_linecount = 0; |
503 | return; |
504 | } |
505 | } |
506 | |
507 | /* not enough lines so we must be at the beginning of the buffer */ |
508 | view->bottom_startline = view->buffer->first_line; |
509 | view->bottom_subline = 0; |
510 | view->empty_linecount = view->height - total; |
511 | } |
512 | |
513 | static void textbuffer_view_init_ypos(TEXT_BUFFER_VIEW_REC *view) |
514 | { |
515 | LINE_REC *line; |
516 | |
517 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
518 | |
519 | view->ypos = -view->subline-1; |
520 | for (line = view->startline; line != NULL((void *)0); line = line->next) |
521 | view->ypos += view_get_linecount(view, line)textbuffer_view_get_line_cache(view, line)->count; |
522 | } |
523 | |
524 | /* Create new view. */ |
525 | TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer, |
526 | int width, int height, |
527 | int scroll, int utf8) |
528 | { |
529 | TEXT_BUFFER_VIEW_REC *view; |
530 | |
531 | g_return_val_if_fail(buffer != NULL, NULL)do{ if (buffer != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "buffer != NULL"); return (((void *)0)); }; }while (0); |
532 | g_return_val_if_fail(width > 0, NULL)do{ if (width > 0) { } else { g_return_if_fail_warning ("TextBufferView" , __PRETTY_FUNCTION__, "width > 0"); return (((void *)0)); }; }while (0); |
533 | |
534 | view = g_new0(TEXT_BUFFER_VIEW_REC, 1)((TEXT_BUFFER_VIEW_REC *) g_malloc0 (((gsize) sizeof (TEXT_BUFFER_VIEW_REC )) * ((gsize) (1)))); |
535 | view->buffer = buffer; |
536 | view->siblings = textbuffer_get_views(buffer); |
537 | |
538 | view->width = width; |
539 | view->height = height; |
540 | view->scroll = scroll; |
541 | view->utf8 = utf8; |
542 | |
543 | view->cache = textbuffer_cache_get(view->siblings, width); |
544 | textbuffer_view_init_bottom(view); |
545 | |
546 | view->startline = view->bottom_startline; |
547 | view->subline = view->bottom_subline; |
548 | view->bottom = TRUE(!(0)); |
549 | |
550 | textbuffer_view_init_ypos(view); |
551 | |
552 | view->bookmarks = g_hash_table_new((GHashFunc) g_str_hash, |
553 | (GCompareFunc) g_str_equal); |
554 | |
555 | views = g_slist_append(views, view); |
556 | return view; |
557 | } |
558 | |
559 | /* Destroy the view. */ |
560 | void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view) |
561 | { |
562 | GSList *tmp; |
563 | |
564 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
565 | |
566 | views = g_slist_remove(views, view); |
567 | |
568 | if (view->siblings == NULL((void *)0)) { |
569 | /* last view for textbuffer, destroy */ |
570 | textbuffer_destroy(view->buffer); |
571 | } else { |
572 | /* remove ourself from siblings lists */ |
573 | for (tmp = view->siblings; tmp != NULL((void *)0); tmp = tmp->next) { |
574 | TEXT_BUFFER_VIEW_REC *rec = tmp->data; |
575 | |
576 | rec->siblings = g_slist_remove(rec->siblings, view); |
577 | } |
578 | g_slist_free(view->siblings); |
579 | } |
580 | |
581 | g_hash_table_foreach(view->bookmarks, (GHFunc) g_free, NULL((void *)0)); |
582 | g_hash_table_destroy(view->bookmarks); |
583 | |
584 | textbuffer_cache_unref(view->cache); |
585 | g_free(view); |
586 | } |
587 | |
588 | /* Change the default indent position */ |
589 | void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view, |
590 | int default_indent, |
591 | int longword_noindent, |
592 | INDENT_FUNC indent_func) |
593 | { |
594 | if (default_indent != -1) |
595 | view->default_indent = default_indent; |
596 | if (longword_noindent != -1) |
597 | view->longword_noindent = longword_noindent; |
598 | |
599 | view->default_indent_func = indent_func; |
600 | } |
601 | |
602 | static void view_unregister_indent_func(TEXT_BUFFER_VIEW_REC *view, |
603 | INDENT_FUNC indent_func) |
604 | { |
605 | if (view->default_indent_func == indent_func) |
606 | view->default_indent_func = NULL((void *)0); |
607 | |
608 | /* recreate cache so it won't contain references |
609 | to the indent function */ |
610 | view_reset_cache(view); |
611 | view->cache = textbuffer_cache_get(view->siblings, view->width); |
612 | } |
613 | |
614 | void textbuffer_views_unregister_indent_func(INDENT_FUNC indent_func) |
615 | { |
616 | g_slist_foreach(views, (GFunc) view_unregister_indent_func, |
617 | (void *) indent_func); |
618 | } |
619 | |
620 | void textbuffer_view_set_scroll(TEXT_BUFFER_VIEW_REC *view, int scroll) |
621 | { |
622 | view->scroll = scroll; |
623 | } |
624 | |
625 | void textbuffer_view_set_utf8(TEXT_BUFFER_VIEW_REC *view, int utf8) |
626 | { |
627 | view->utf8 = utf8; |
628 | } |
629 | |
630 | static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) |
631 | { |
632 | int linecount; |
633 | |
634 | linecount = 0; |
635 | while (line != NULL((void *)0)) { |
636 | linecount += view_get_linecount(view, line)textbuffer_view_get_line_cache(view, line)->count; |
637 | line = line->next; |
638 | } |
639 | |
640 | return linecount; |
641 | } |
642 | |
643 | static void view_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, |
644 | int subline, int ypos, int lines, int fill_bottom) |
645 | { |
646 | int linecount; |
647 | |
648 | if (view->dirty) /* don't bother drawing anything - redraw is coming */ |
649 | return; |
650 | |
651 | while (line != NULL((void *)0) && lines > 0) { |
652 | linecount = view_line_draw(view, line, subline, ypos, lines); |
653 | ypos += linecount; lines -= linecount; |
654 | |
655 | subline = 0; |
656 | line = line->next; |
657 | } |
658 | |
659 | if (fill_bottom) { |
660 | /* clear the rest of the view */ |
661 | term_set_color(view->window, ATTR_RESET(0x0100|0x0200)); |
662 | while (lines > 0) { |
663 | term_move(view->window, 0, ypos); |
664 | term_clrtoeol(view->window); |
665 | ypos++; lines--; |
666 | } |
667 | } |
668 | } |
669 | |
670 | #define view_draw_top(view, lines, fill_bottom)view_draw(view, (view)->startline, (view)->subline, 0, lines , fill_bottom) \ |
671 | view_draw(view, (view)->startline, (view)->subline, \ |
672 | 0, lines, fill_bottom) |
673 | |
674 | static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines) |
675 | { |
676 | LINE_REC *line; |
677 | int ypos, maxline, subline, linecount; |
678 | |
679 | maxline = view->height-lines; |
680 | line = view->startline; ypos = -view->subline; subline = 0; |
681 | while (line != NULL((void *)0) && ypos < maxline) { |
682 | linecount = view_get_linecount(view, line)textbuffer_view_get_line_cache(view, line)->count; |
683 | ypos += linecount; |
684 | if (ypos > maxline) { |
685 | subline = maxline-(ypos-linecount); |
686 | break; |
687 | } |
688 | line = line->next; |
689 | } |
690 | |
691 | view_draw(view, line, subline, maxline, lines, TRUE(!(0))); |
692 | } |
693 | |
694 | /* Returns number of lines actually scrolled */ |
695 | static int view_scroll(TEXT_BUFFER_VIEW_REC *view, LINE_REC **lines, |
696 | int *subline, int scrollcount, int draw_nonclean) |
697 | { |
698 | int linecount, realcount, scroll_visible; |
699 | |
700 | if (*lines == NULL((void *)0)) |
701 | return 0; |
702 | |
703 | /* scroll down */ |
704 | scroll_visible = lines == &view->startline; |
705 | |
706 | realcount = -*subline; |
707 | scrollcount += *subline; |
708 | *subline = 0; |
709 | while (scrollcount > 0) { |
710 | linecount = view_get_linecount(view, *lines)textbuffer_view_get_line_cache(view, *lines)->count; |
711 | |
712 | if ((scroll_visible && *lines == view->bottom_startline) && |
713 | (scrollcount >= view->bottom_subline)) { |
714 | *subline = view->bottom_subline; |
715 | realcount += view->bottom_subline; |
716 | scrollcount = 0; |
717 | break; |
718 | } |
719 | |
720 | realcount += linecount; |
721 | scrollcount -= linecount; |
722 | if (scrollcount < 0) { |
723 | realcount += scrollcount; |
724 | *subline = linecount+scrollcount; |
725 | scrollcount = 0; |
726 | break; |
727 | } |
728 | |
729 | if ((*lines)->next == NULL((void *)0)) |
730 | break; |
731 | |
732 | *lines = (*lines)->next; |
733 | } |
734 | |
735 | /* scroll up */ |
736 | while (scrollcount < 0 && (*lines)->prev != NULL((void *)0)) { |
737 | *lines = (*lines)->prev; |
738 | linecount = view_get_linecount(view, *lines)textbuffer_view_get_line_cache(view, *lines)->count; |
739 | |
740 | realcount -= linecount; |
741 | scrollcount += linecount; |
742 | if (scrollcount > 0) { |
743 | realcount += scrollcount; |
744 | *subline = scrollcount; |
745 | break; |
746 | } |
747 | } |
748 | |
749 | if (scroll_visible && realcount != 0 && view->window != NULL((void *)0)) { |
750 | if (realcount <= -view->height || realcount >= view->height) { |
751 | /* scrolled more than screenful, redraw the |
752 | whole view */ |
753 | textbuffer_view_redraw(view); |
754 | } else { |
755 | term_set_color(view->window, ATTR_RESET(0x0100|0x0200)); |
756 | term_window_scroll(view->window, realcount); |
757 | |
758 | if (draw_nonclean) { |
759 | if (realcount < 0) |
760 | view_draw_top(view, -realcount, TRUE)view_draw(view, (view)->startline, (view)->subline, 0, - realcount, (!(0))); |
761 | else |
762 | view_draw_bottom(view, realcount); |
763 | } |
764 | |
765 | term_refresh(view->window); |
766 | } |
767 | } |
768 | |
769 | return realcount >= 0 ? realcount : -realcount; |
770 | } |
771 | |
772 | /* Resize the view. */ |
773 | void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height) |
774 | { |
775 | int linecount; |
776 | |
777 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
778 | g_return_if_fail(width > 0)do{ if (width > 0) { } else { g_return_if_fail_warning ("TextBufferView" , __PRETTY_FUNCTION__, "width > 0"); return; }; }while (0); |
779 | |
780 | if (view->width != width) { |
781 | /* line cache needs to be recreated */ |
782 | textbuffer_cache_unref(view->cache); |
783 | view->cache = textbuffer_cache_get(view->siblings, width); |
784 | } |
785 | |
786 | view->width = width > 10 ? width : 10; |
787 | view->height = height > 1 ? height : 1; |
788 | |
789 | if (view->buffer->first_line == NULL((void *)0)) { |
790 | view->empty_linecount = height; |
791 | return; |
792 | } |
793 | |
794 | textbuffer_view_init_bottom(view); |
795 | |
796 | /* check that we didn't scroll lower than bottom startline.. */ |
797 | if (textbuffer_line_exists_after(view->bottom_startline->next, |
798 | view->startline)) { |
799 | view->startline = view->bottom_startline; |
800 | view->subline = view->bottom_subline; |
801 | } else if (view->startline == view->bottom_startline && |
802 | view->subline > view->bottom_subline) { |
803 | view->subline = view->bottom_subline; |
804 | } else { |
805 | /* make sure the subline is still in allowed range */ |
806 | linecount = view_get_linecount(view, view->startline)textbuffer_view_get_line_cache(view, view->startline)-> count; |
807 | if (view->subline > linecount) |
808 | view->subline = linecount; |
809 | } |
810 | |
811 | textbuffer_view_init_ypos(view); |
812 | if (view->bottom && !view_is_bottom(view)((view)->ypos >= -1 && (view)->ypos < (view )->height)) { |
813 | /* we scrolled to far up, need to get down. go right over |
814 | the empty lines if there's any */ |
815 | view->startline = view->bottom_startline; |
816 | view->subline = view->bottom_subline; |
817 | if (view->empty_linecount > 0) { |
818 | view_scroll(view, &view->startline, &view->subline, |
819 | -view->empty_linecount, FALSE(0)); |
820 | } |
821 | textbuffer_view_init_ypos(view); |
822 | } |
823 | |
824 | view->bottom = view_is_bottom(view)((view)->ypos >= -1 && (view)->ypos < (view )->height); |
825 | if (view->bottom) { |
826 | /* check if we left empty space at the bottom.. */ |
827 | linecount = view_get_linecount_all(view, view->startline) - |
828 | view->subline; |
829 | if (view->empty_linecount < view->height-linecount) |
830 | view->empty_linecount = view->height-linecount; |
831 | view->more_text = FALSE(0); |
832 | } |
833 | |
834 | view->dirty = TRUE(!(0)); |
835 | } |
836 | |
837 | /* Clear the view, don't actually remove any lines from buffer. */ |
838 | void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view) |
839 | { |
840 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
841 | |
842 | view->ypos = -1; |
843 | view->bottom_startline = view->startline = |
844 | textbuffer_line_last(view->buffer); |
845 | view->bottom_subline = view->subline = |
846 | view->buffer->cur_line == NULL((void *)0) ? 0 : |
847 | view_get_linecount(view, view->buffer->cur_line)textbuffer_view_get_line_cache(view, view->buffer->cur_line )->count; |
848 | view->empty_linecount = view->height; |
849 | view->bottom = TRUE(!(0)); |
850 | view->more_text = FALSE(0); |
851 | |
852 | textbuffer_view_redraw(view); |
853 | } |
854 | |
855 | /* Scroll the view up/down */ |
856 | void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines) |
857 | { |
858 | int count; |
859 | |
860 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
861 | |
862 | count = view_scroll(view, &view->startline, &view->subline, |
863 | lines, TRUE(!(0))); |
864 | view->ypos += lines < 0 ? count : -count; |
865 | view->bottom = view_is_bottom(view)((view)->ypos >= -1 && (view)->ypos < (view )->height); |
866 | if (view->bottom) view->more_text = FALSE(0); |
867 | |
868 | if (view->window != NULL((void *)0)) |
869 | term_refresh(view->window); |
870 | } |
871 | |
872 | /* Scroll to specified line */ |
873 | void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) |
874 | { |
875 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
876 | |
877 | if (textbuffer_line_exists_after(view->bottom_startline->next, line)) { |
878 | view->startline = view->bottom_startline; |
879 | view->subline = view->bottom_subline; |
880 | } else { |
881 | view->startline = line; |
882 | view->subline = 0; |
883 | } |
884 | |
885 | textbuffer_view_init_ypos(view); |
886 | view->bottom = view_is_bottom(view)((view)->ypos >= -1 && (view)->ypos < (view )->height); |
887 | if (view->bottom) view->more_text = FALSE(0); |
888 | |
889 | textbuffer_view_redraw(view); |
890 | } |
891 | |
892 | /* Return line cache */ |
893 | LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view, |
894 | LINE_REC *line) |
895 | { |
896 | LINE_CACHE_REC *cache; |
897 | |
898 | g_assert(view != NULL)do { if (view != ((void *)0)) ; else g_assertion_message_expr ("TextBufferView", "textbuffer-view.c", 898, ((const char*) ( __PRETTY_FUNCTION__)), "view != NULL"); } while (0); |
899 | g_assert(line != NULL)do { if (line != ((void *)0)) ; else g_assertion_message_expr ("TextBufferView", "textbuffer-view.c", 899, ((const char*) ( __PRETTY_FUNCTION__)), "line != NULL"); } while (0); |
900 | |
901 | cache = g_hash_table_lookup(view->cache->line_cache, line); |
902 | if (cache == NULL((void *)0)) |
903 | cache = view_update_line_cache(view, line); |
904 | else |
905 | cache->last_access = time(NULL((void *)0)); |
906 | |
907 | return cache; |
908 | } |
909 | |
910 | static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) |
911 | { |
912 | int linecount, ypos, subline; |
913 | |
914 | if (!view->bottom) |
915 | view->more_text = TRUE(!(0)); |
916 | |
917 | if (view->bottom_startline == NULL((void *)0)) { |
918 | view->startline = view->bottom_startline = |
919 | view->buffer->first_line; |
920 | } |
921 | |
922 | if (view->buffer->cur_line != line && |
923 | !textbuffer_line_exists_after(view->bottom_startline, line)) |
924 | return; |
925 | |
926 | linecount = view->cache->last_linecount; |
927 | view->ypos += linecount; |
928 | if (view->empty_linecount > 0) { |
929 | view->empty_linecount -= linecount; |
930 | if (view->empty_linecount >= 0) |
931 | linecount = 0; |
932 | else { |
933 | linecount = -view->empty_linecount; |
934 | view->empty_linecount = 0; |
935 | } |
936 | } |
937 | |
938 | if (linecount > 0) { |
939 | view_scroll(view, &view->bottom_startline, |
940 | &view->bottom_subline, linecount, FALSE(0)); |
941 | } |
942 | |
943 | if (view->bottom) { |
944 | if (view->scroll && view->ypos >= view->height) { |
945 | linecount = view->ypos-view->height+1; |
946 | view_scroll(view, &view->startline, |
947 | &view->subline, linecount, FALSE(0)); |
948 | view->ypos -= linecount; |
949 | } else { |
950 | view->bottom = view_is_bottom(view)((view)->ypos >= -1 && (view)->ypos < (view )->height); |
951 | } |
952 | |
953 | if (view->window != NULL((void *)0)) { |
954 | ypos = view->ypos+1 - view->cache->last_linecount; |
955 | if (ypos >= 0) |
956 | subline = 0; |
957 | else { |
958 | subline = -ypos; |
959 | ypos = 0; |
960 | } |
961 | if (ypos < view->height) { |
962 | view_line_draw(view, line, subline, ypos, |
963 | view->height - ypos); |
964 | } |
965 | } |
966 | } |
967 | |
968 | if (view->window != NULL((void *)0)) |
969 | term_refresh(view->window); |
970 | } |
971 | |
972 | /* Update some line in the buffer which has been modified using |
973 | textbuffer_append() or textbuffer_insert(). */ |
974 | void textbuffer_view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) |
975 | { |
976 | GSList *tmp; |
977 | unsigned char update_counter; |
978 | |
979 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
980 | g_return_if_fail(line != NULL)do{ if (line != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "line != NULL"); return ; }; }while (0); |
981 | |
982 | if (!view->buffer->last_eol) |
983 | return; |
984 | |
985 | update_counter = view->cache->update_counter+1; |
986 | view_update_cache(view, line, update_counter); |
987 | view_insert_line(view, line); |
988 | |
989 | for (tmp = view->siblings; tmp != NULL((void *)0); tmp = tmp->next) { |
990 | TEXT_BUFFER_VIEW_REC *rec = tmp->data; |
991 | |
992 | view_update_cache(rec, line, update_counter); |
993 | view_insert_line(rec, line); |
994 | } |
995 | } |
996 | |
997 | typedef struct { |
998 | LINE_REC *remove_line; |
999 | GSList *remove_list; |
1000 | } BOOKMARK_FIND_REC; |
1001 | |
1002 | static void bookmark_check_remove(char *key, LINE_REC *line, |
1003 | BOOKMARK_FIND_REC *rec) |
1004 | { |
1005 | if (line == rec->remove_line) |
1006 | rec->remove_list = g_slist_append(rec->remove_list, key); |
1007 | } |
1008 | |
1009 | static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) |
1010 | { |
1011 | BOOKMARK_FIND_REC rec; |
1012 | GSList *tmp; |
1013 | |
1014 | rec.remove_line = line; |
1015 | rec.remove_list = NULL((void *)0); |
1016 | g_hash_table_foreach(view->bookmarks, |
1017 | (GHFunc) bookmark_check_remove, &rec); |
1018 | |
1019 | if (rec.remove_list != NULL((void *)0)) { |
1020 | for (tmp = rec.remove_list; tmp != NULL((void *)0); tmp = tmp->next) { |
1021 | g_hash_table_remove(view->bookmarks, tmp->data); |
1022 | g_free(tmp->data); |
1023 | } |
1024 | g_slist_free(rec.remove_list); |
1025 | } |
1026 | } |
1027 | |
1028 | /* Return number of real lines `lines' list takes - |
1029 | stops counting when the height reaches the view height */ |
1030 | static int view_get_lines_height(TEXT_BUFFER_VIEW_REC *view, |
1031 | LINE_REC *line, int subline, |
1032 | LINE_REC *skip_line) |
1033 | { |
1034 | int height, linecount; |
1035 | |
1036 | height = -subline; |
1037 | while (line != NULL((void *)0) && height < view->height) { |
1038 | if (line != skip_line) { |
1039 | linecount = view_get_linecount(view, line)textbuffer_view_get_line_cache(view, line)->count; |
1040 | height += linecount; |
1041 | } |
1042 | line = line->next; |
1043 | } |
1044 | |
1045 | return height < view->height ? height : view->height; |
1046 | } |
1047 | |
1048 | static void view_remove_line_update_startline(TEXT_BUFFER_VIEW_REC *view, |
1049 | LINE_REC *line, int linecount) |
1050 | { |
1051 | int scroll; |
1052 | |
1053 | if (view->startline == line) { |
1054 | view->startline = view->startline->prev != NULL((void *)0) ? |
1055 | view->startline->prev : view->startline->next; |
1056 | view->subline = 0; |
1057 | } else { |
1058 | scroll = view->height - |
1059 | view_get_lines_height(view, view->startline, |
1060 | view->subline, line); |
1061 | if (scroll > 0) { |
1062 | view_scroll(view, &view->startline, |
1063 | &view->subline, -scroll, FALSE(0)); |
1064 | } |
1065 | } |
1066 | |
1067 | /* FIXME: this is slow and unnecessary, but it's easy and |
1068 | really works :) */ |
1069 | textbuffer_view_init_ypos(view); |
1070 | if (textbuffer_line_exists_after(view->startline, line)) |
1071 | view->ypos -= linecount; |
1072 | } |
1073 | |
1074 | static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, |
1075 | int linecount) |
1076 | { |
1077 | int realcount; |
1078 | |
1079 | view_bookmarks_check(view, line); |
1080 | |
1081 | if (view->buffer->cur_line == line) { |
1082 | /* the last line is being removed */ |
1083 | LINE_REC *prevline; |
1084 | |
1085 | prevline = view->buffer->first_line == line ? NULL((void *)0) : |
1086 | textbuffer_line_last(view->buffer); |
1087 | view->cache->last_linecount = prevline == NULL((void *)0) ? 0 : |
1088 | view_get_linecount(view, prevline)textbuffer_view_get_line_cache(view, prevline)->count; |
1089 | } |
1090 | |
1091 | if (view->buffer->first_line == line) { |
1092 | /* first line in the buffer - this is the most commonly |
1093 | removed line.. */ |
1094 | if (view->bottom_startline == line) { |
1095 | /* very small scrollback.. */ |
1096 | view->bottom_startline = view->bottom_startline->next; |
1097 | view->bottom_subline = 0; |
1098 | } |
1099 | |
1100 | if (view->startline == line) { |
1101 | /* removing the first line in screen */ |
1102 | int is_last = view->startline->next == NULL((void *)0); |
1103 | |
1104 | realcount = view_scroll(view, &view->startline, |
1105 | &view->subline, |
1106 | linecount, FALSE(0)); |
1107 | view->ypos -= realcount; |
1108 | view->empty_linecount += linecount-realcount; |
1109 | if (is_last == 1) |
1110 | view->startline = NULL((void *)0); |
1111 | } |
1112 | } else { |
1113 | if (textbuffer_line_exists_after(view->bottom_startline, |
1114 | line)) { |
1115 | realcount = view_scroll(view, &view->bottom_startline, |
1116 | &view->bottom_subline, |
1117 | -linecount, FALSE(0)); |
1118 | view->empty_linecount += linecount-realcount; |
1119 | } |
1120 | |
1121 | if (textbuffer_line_exists_after(view->startline, |
1122 | line)) { |
1123 | view_remove_line_update_startline(view, line, |
1124 | linecount); |
1125 | } |
1126 | } |
1127 | |
1128 | view->bottom = view_is_bottom(view)((view)->ypos >= -1 && (view)->ypos < (view )->height); |
1129 | if (view->bottom) view->more_text = FALSE(0); |
1130 | if (view->window != NULL((void *)0)) |
1131 | term_refresh(view->window); |
1132 | } |
1133 | |
1134 | /* Remove one line from buffer. */ |
1135 | void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) |
1136 | { |
1137 | GSList *tmp; |
1138 | unsigned char update_counter; |
1139 | int linecount; |
1140 | |
1141 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
1142 | g_return_if_fail(line != NULL)do{ if (line != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "line != NULL"); return ; }; }while (0); |
1143 | |
1144 | linecount = view_get_linecount(view, line)textbuffer_view_get_line_cache(view, line)->count; |
1145 | update_counter = view->cache->update_counter+1; |
1146 | |
1147 | view_remove_line(view, line, linecount); |
1148 | view_remove_cache(view, line, update_counter); |
1149 | |
1150 | for (tmp = view->siblings; tmp != NULL((void *)0); tmp = tmp->next) { |
1151 | TEXT_BUFFER_VIEW_REC *rec = tmp->data; |
1152 | |
1153 | view_remove_line(rec, line, linecount); |
1154 | view_remove_cache(rec, line, update_counter); |
1155 | } |
1156 | |
1157 | textbuffer_remove(view->buffer, line); |
1158 | } |
1159 | |
1160 | void textbuffer_view_remove_lines_by_level(TEXT_BUFFER_VIEW_REC *view, int level) |
1161 | { |
1162 | LINE_REC *line, *next; |
1163 | |
1164 | term_refresh_freeze(); |
1165 | line = textbuffer_view_get_lines(view)((view)->buffer->first_line); |
1166 | |
1167 | while (line != NULL((void *)0)) { |
1168 | next = line->next; |
1169 | |
1170 | if (line->info.level & level) |
1171 | textbuffer_view_remove_line(view, line); |
1172 | line = next; |
1173 | } |
1174 | textbuffer_view_redraw(view); |
1175 | term_refresh_thaw(); |
1176 | } |
1177 | |
1178 | static int g_free_true(void *data) |
1179 | { |
1180 | g_free(data); |
1181 | return TRUE(!(0)); |
1182 | } |
1183 | |
1184 | /* Remove all lines from buffer. */ |
1185 | void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view) |
1186 | { |
1187 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
1188 | |
1189 | textbuffer_remove_all_lines(view->buffer); |
1190 | |
1191 | g_hash_table_foreach_remove(view->bookmarks, |
1192 | (GHRFunc) g_free_true, NULL((void *)0)); |
1193 | |
1194 | view_reset_cache(view); |
1195 | textbuffer_view_clear(view); |
1196 | g_slist_foreach(view->siblings, (GFunc) textbuffer_view_clear, NULL((void *)0)); |
1197 | } |
1198 | |
1199 | /* Set a bookmark in view */ |
1200 | void textbuffer_view_set_bookmark(TEXT_BUFFER_VIEW_REC *view, |
1201 | const char *name, LINE_REC *line) |
1202 | { |
1203 | gpointer key, value; |
1204 | |
1205 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
1206 | g_return_if_fail(name != NULL)do{ if (name != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "name != NULL"); return ; }; }while (0); |
1207 | |
1208 | if (g_hash_table_lookup_extended(view->bookmarks, name, |
1209 | &key, &value)) { |
1210 | g_hash_table_remove(view->bookmarks, key); |
1211 | g_free(key); |
1212 | } |
1213 | |
1214 | g_hash_table_insert(view->bookmarks, g_strdup(name), line); |
1215 | } |
1216 | |
1217 | /* Set a bookmark in view to the bottom line */ |
1218 | void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view, |
1219 | const char *name) |
1220 | { |
1221 | LINE_REC *line; |
1222 | |
1223 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
1224 | g_return_if_fail(name != NULL)do{ if (name != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "name != NULL"); return ; }; }while (0); |
1225 | |
1226 | if (view->bottom_startline != NULL((void *)0)) { |
1227 | line = textbuffer_line_last(view->buffer); |
1228 | textbuffer_view_set_bookmark(view, name, line); |
1229 | } |
1230 | } |
1231 | |
1232 | /* Return the line for bookmark */ |
1233 | LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view, |
1234 | const char *name) |
1235 | { |
1236 | g_return_val_if_fail(view != NULL, NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return (((void *)0)); }; }while (0); |
1237 | g_return_val_if_fail(name != NULL, NULL)do{ if (name != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "name != NULL"); return (((void *)0)); }; }while (0); |
1238 | |
1239 | return g_hash_table_lookup(view->bookmarks, name); |
1240 | } |
1241 | |
1242 | /* Specify window where the changes in view should be drawn, |
1243 | NULL disables it. */ |
1244 | void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, |
1245 | TERM_WINDOW *window) |
1246 | { |
1247 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
1248 | |
1249 | if (view->window != window) { |
1250 | view->window = window; |
1251 | if (window != NULL((void *)0)) |
1252 | view->dirty = TRUE(!(0)); |
1253 | } |
1254 | } |
1255 | |
1256 | /* Redraw a view to window */ |
1257 | void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view) |
1258 | { |
1259 | g_return_if_fail(view != NULL)do{ if (view != ((void *)0)) { } else { g_return_if_fail_warning ("TextBufferView", __PRETTY_FUNCTION__, "view != NULL"); return ; }; }while (0); |
1260 | |
1261 | if (view->window != NULL((void *)0)) { |
1262 | view->dirty = FALSE(0); |
1263 | view_draw_top(view, view->height, TRUE)view_draw(view, (view)->startline, (view)->subline, 0, view ->height, (!(0))); |
1264 | term_refresh(view->window); |
1265 | } |
1266 | } |
1267 | |
1268 | static int line_cache_check_remove(void *key, LINE_CACHE_REC *cache, |
1269 | time_t *now) |
1270 | { |
1271 | if (cache->last_access+LINE_CACHE_KEEP_TIME(10*60) > *now) |
1272 | return FALSE(0); |
1273 | |
1274 | line_cache_destroy(NULL((void *)0), cache); |
1275 | return TRUE(!(0)); |
1276 | } |
1277 | |
1278 | static int sig_check_linecache(void) |
1279 | { |
1280 | GSList *tmp, *caches; |
1281 | time_t now; |
1282 | |
1283 | now = time(NULL((void *)0)); caches = NULL((void *)0); |
1284 | for (tmp = views; tmp != NULL((void *)0); tmp = tmp->next) { |
1285 | TEXT_BUFFER_VIEW_REC *rec = tmp->data; |
1286 | |
1287 | if (g_slist_find(caches, rec->cache) != NULL((void *)0)) |
1288 | continue; |
1289 | |
1290 | caches = g_slist_append(caches, rec->cache); |
1291 | g_hash_table_foreach_remove(rec->cache->line_cache, |
1292 | (GHRFunc) line_cache_check_remove, |
1293 | &now); |
1294 | } |
1295 | |
1296 | g_slist_free(caches); |
1297 | return 1; |
1298 | } |
1299 | |
1300 | void textbuffer_view_init(void) |
1301 | { |
1302 | linecache_tag = g_timeout_add(LINE_CACHE_CHECK_TIME(5*60*1000), (GSourceFunc) sig_check_linecache, NULL((void *)0)); |
1303 | } |
1304 | |
1305 | void textbuffer_view_deinit(void) |
1306 | { |
1307 | g_source_remove(linecache_tag); |
1308 | } |