Initial commit.
[gnt.git] / gnttextview.c
blob0b8c293f78c74606fe8a2cc9e0a7389c2ab60625
1 /**
2 * GNT - The GLib Ncurses Toolkit
4 * GNT is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "gntstyle.h"
24 #include "gnttextview.h"
25 #include "gntutils.h"
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
31 enum
33 SIGS = 1,
36 typedef struct
38 GntTextFormatFlags tvflag;
39 chtype flags;
40 int start;
41 int end; /* This is the next byte of the last character of this segment */
42 } GntTextSegment;
44 typedef struct
46 GList *segments; /* A list of GntTextSegments */
47 int length; /* The current length of the line so far (ie. onscreen width) */
48 gboolean soft; /* TRUE if it's an overflow from prev. line */
49 } GntTextLine;
51 typedef struct
53 char *name;
54 int start;
55 int end;
56 } GntTextTag;
58 static GntWidgetClass *parent_class = NULL;
60 static gchar *select_start;
61 static gchar *select_end;
62 static gboolean double_click;
64 static void
65 gnt_text_view_draw(GntWidget *widget)
67 GntTextView *view = GNT_TEXT_VIEW(widget);
68 int i = 0;
69 GList *lines;
70 int rows, scrcol;
71 int comp = 0; /* Used for top-aligned text */
72 gboolean has_scroll = !(view->flags & GNT_TEXT_VIEW_NO_SCROLL);
74 wbkgd(widget->window, gnt_color_pair(GNT_COLOR_NORMAL));
75 werase(widget->window);
77 if ((view->flags & GNT_TEXT_VIEW_TOP_ALIGN) &&
78 g_list_length(view->list) < widget->priv.height) {
79 GList *now = view->list;
80 comp = widget->priv.height - g_list_length(view->list);
81 view->list = g_list_nth_prev(view->list, comp);
82 if (!view->list) {
83 view->list = g_list_first(now);
84 comp = widget->priv.height - g_list_length(view->list);
85 } else {
86 comp = 0;
90 for (i = 0, lines = view->list; i < widget->priv.height && lines; i++, lines = lines->next)
92 GList *iter;
93 GntTextLine *line = lines->data;
95 wmove(widget->window, widget->priv.height - 1 - i - comp, 0);
97 for (iter = line->segments; iter; iter = iter->next)
99 GntTextSegment *seg = iter->data;
100 char *end = view->string->str + seg->end;
101 char back = *end;
102 chtype fl = seg->flags;
103 *end = '\0';
104 if (select_start < view->string->str + seg->start && select_end > view->string->str + seg->end) {
105 fl |= A_REVERSE;
106 wattrset(widget->window, fl);
107 wprintw(widget->window, "%s", (view->string->str + seg->start));
108 } else if (select_start && select_end &&
109 ((select_start >= view->string->str + seg->start && select_start <= view->string->str + seg->end) ||
110 (select_end <= view->string->str + seg->end && select_start <= view->string->str + seg->start))) {
111 char *cur = view->string->str + seg->start;
112 while (*cur != '\0') {
113 gchar *last = g_utf8_next_char(cur);
114 gchar *str;
115 if (cur >= select_start && cur <= select_end)
116 fl |= A_REVERSE;
117 else
118 fl = seg->flags;
119 str = g_strndup(cur, last - cur);
120 wattrset(widget->window, fl);
121 waddstr(widget->window, str);
122 g_free(str);
123 cur = g_utf8_next_char(cur);
125 } else {
126 wattrset(widget->window, fl);
127 wprintw(widget->window, "%s", (view->string->str + seg->start));
129 *end = back;
131 wattroff(widget->window, A_UNDERLINE | A_BLINK | A_REVERSE);
132 whline(widget->window, ' ', widget->priv.width - line->length - has_scroll);
135 scrcol = widget->priv.width - 1;
136 rows = widget->priv.height - 2;
137 if (has_scroll && rows > 0)
139 int total = g_list_length(g_list_first(view->list));
140 int showing, position, up, down;
142 showing = rows * rows / total + 1;
143 showing = MIN(rows, showing);
145 total -= rows;
146 up = g_list_length(lines);
147 down = total - up;
149 position = (rows - showing) * up / MAX(1, up + down);
150 position = MAX((lines != NULL), position);
152 if (showing + position > rows)
153 position = rows - showing;
155 if (showing + position == rows && view->list && view->list->prev)
156 position = MAX(1, rows - 1 - showing);
157 else if (showing + position < rows && view->list && !view->list->prev)
158 position = rows - showing;
160 mvwvline(widget->window, position + 1, scrcol,
161 ACS_CKBOARD | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D), showing);
164 if (has_scroll) {
165 mvwaddch(widget->window, 0, scrcol,
166 (lines ? ACS_UARROW : ' ') | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D));
167 mvwaddch(widget->window, widget->priv.height - 1, scrcol,
168 ((view->list && view->list->prev) ? ACS_DARROW : ' ') |
169 gnt_color_pair(GNT_COLOR_HIGHLIGHT_D));
172 GNTDEBUG;
175 static void
176 gnt_text_view_size_request(GntWidget *widget)
178 if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED))
180 gnt_widget_set_size(widget, 64, 20);
184 static void
185 gnt_text_view_map(GntWidget *widget)
187 if (widget->priv.width == 0 || widget->priv.height == 0)
188 gnt_widget_size_request(widget);
189 GNTDEBUG;
192 static gboolean
193 gnt_text_view_key_pressed(GntWidget *widget, const char *text)
195 return FALSE;
198 static void
199 free_text_segment(gpointer data, gpointer null)
201 GntTextSegment *seg = data;
202 g_free(seg);
205 static void
206 free_text_line(gpointer data, gpointer null)
208 GntTextLine *line = data;
209 g_list_foreach(line->segments, free_text_segment, NULL);
210 g_list_free(line->segments);
211 g_free(line);
214 static void
215 free_tag(gpointer data, gpointer null)
217 GntTextTag *tag = data;
218 g_free(tag->name);
219 g_free(tag);
222 static void
223 gnt_text_view_destroy(GntWidget *widget)
225 GntTextView *view = GNT_TEXT_VIEW(widget);
226 view->list = g_list_first(view->list);
227 g_list_foreach(view->list, free_text_line, NULL);
228 g_list_free(view->list);
229 g_list_foreach(view->tags, free_tag, NULL);
230 g_list_free(view->tags);
231 g_string_free(view->string, TRUE);
234 static char *
235 gnt_text_view_get_p(GntTextView *view, int x, int y)
237 int i = 0;
238 GntWidget *wid = GNT_WIDGET(view);
239 GntTextLine *line;
240 GList *lines;
241 GList *segs;
242 GntTextSegment *seg;
243 gchar *pos;
245 y = wid->priv.height - y;
246 if (g_list_length(view->list) < y) {
247 x = 0;
248 y = g_list_length(view->list) - 1;
251 lines = g_list_nth(view->list, y - 1);
252 if (!lines)
253 return NULL;
254 do {
255 line = lines->data;
256 lines = lines->next;
257 } while (line && !line->segments && lines);
259 if (!line || !line->segments) /* no valid line */
260 return NULL;
261 segs = line->segments;
262 seg = (GntTextSegment *)segs->data;
263 pos = view->string->str + seg->start;
264 x = MIN(x, line->length);
265 while (++i <= x) {
266 gunichar *u;
267 pos = g_utf8_next_char(pos);
268 u = g_utf8_to_ucs4(pos, -1, NULL, NULL, NULL);
269 if (u && g_unichar_iswide(*u))
270 i++;
271 g_free(u);
273 return pos;
276 static GString *
277 select_word_text(GntTextView *view, gchar *c)
279 gchar *start = c;
280 gchar *end = c;
281 gchar *t, *endsize;
282 while ((t = g_utf8_prev_char(start))) {
283 if (!g_ascii_isspace(*t)) {
284 if (start == view->string->str)
285 break;
286 start = t;
287 } else
288 break;
290 while ((t = g_utf8_next_char(end))) {
291 if (!g_ascii_isspace(*t))
292 end = t;
293 else
294 break;
296 select_start = start;
297 select_end = end;
298 endsize = g_utf8_next_char(select_end); /* End at the correct byte */
299 return g_string_new_len(start, endsize - start);
302 static gboolean too_slow(gpointer n)
304 double_click = FALSE;
305 return FALSE;
308 static gboolean
309 gnt_text_view_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
311 if (event == GNT_MOUSE_SCROLL_UP) {
312 gnt_text_view_scroll(GNT_TEXT_VIEW(widget), -1);
313 } else if (event == GNT_MOUSE_SCROLL_DOWN) {
314 gnt_text_view_scroll(GNT_TEXT_VIEW(widget), 1);
315 } else if (event == GNT_LEFT_MOUSE_DOWN) {
316 select_start = gnt_text_view_get_p(GNT_TEXT_VIEW(widget), x - widget->priv.x, y - widget->priv.y);
317 g_timeout_add(500, too_slow, NULL);
318 } else if (event == GNT_MOUSE_UP) {
319 if (select_start) {
320 GString *clip;
321 select_end = gnt_text_view_get_p(GNT_TEXT_VIEW(widget), x - widget->priv.x, y - widget->priv.y);
322 if (select_end < select_start) {
323 gchar *t = select_start;
324 select_start = select_end;
325 select_end = t;
327 if (select_start == select_end) {
328 if (double_click) {
329 clip = select_word_text(GNT_TEXT_VIEW(widget), select_start);
330 double_click = FALSE;
331 } else {
332 double_click = TRUE;
333 select_start = 0;
334 select_end = 0;
335 gnt_widget_draw(widget);
336 return TRUE;
338 } else {
339 gchar *endsize = g_utf8_next_char(select_end); /* End at the correct byte */
340 clip = g_string_new_len(select_start, endsize - select_start);
342 gnt_widget_draw(widget);
343 gnt_set_clipboard_string(clip->str);
344 g_string_free(clip, TRUE);
346 } else
347 return FALSE;
348 return TRUE;
351 static void
352 gnt_text_view_reflow(GntTextView *view)
354 /* This is pretty ugly, and inefficient. Someone do something about it. */
355 GntTextLine *line;
356 GList *back, *iter, *list;
357 GString *string;
358 int pos = 0; /* no. of 'real' lines */
360 list = view->list;
361 while (list->prev) {
362 line = list->data;
363 if (!line->soft)
364 pos++;
365 list = list->prev;
368 back = g_list_last(view->list);
369 view->list = NULL;
371 string = view->string;
372 view->string = NULL;
373 gnt_text_view_clear(view);
375 view->string = g_string_set_size(view->string, string->len);
376 view->string->len = 0;
377 GNT_WIDGET_SET_FLAGS(GNT_WIDGET(view), GNT_WIDGET_DRAWING);
379 for (; back; back = back->prev) {
380 line = back->data;
381 if (back->next && !line->soft) {
382 gnt_text_view_append_text_with_flags(view, "\n", GNT_TEXT_FLAG_NORMAL);
385 for (iter = line->segments; iter; iter = iter->next) {
386 GntTextSegment *seg = iter->data;
387 char *start = string->str + seg->start;
388 char *end = string->str + seg->end;
389 char back = *end;
390 *end = '\0';
391 gnt_text_view_append_text_with_flags(view, start, seg->tvflag);
392 *end = back;
394 free_text_line(line, NULL);
396 g_list_free(list);
398 list = view->list = g_list_first(view->list);
399 /* Go back to the line that was in view before resizing started */
400 while (pos--) {
401 while (((GntTextLine*)list->data)->soft)
402 list = list->next;
403 list = list->next;
405 view->list = list;
406 GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(view), GNT_WIDGET_DRAWING);
407 if (GNT_WIDGET(view)->window)
408 gnt_widget_draw(GNT_WIDGET(view));
409 g_string_free(string, TRUE);
412 static void
413 gnt_text_view_size_changed(GntWidget *widget, int w, int h)
415 if (w != widget->priv.width && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED)) {
416 gnt_text_view_reflow(GNT_TEXT_VIEW(widget));
420 static void
421 gnt_text_view_class_init(GntTextViewClass *klass)
423 parent_class = GNT_WIDGET_CLASS(klass);
424 parent_class->destroy = gnt_text_view_destroy;
425 parent_class->draw = gnt_text_view_draw;
426 parent_class->map = gnt_text_view_map;
427 parent_class->size_request = gnt_text_view_size_request;
428 parent_class->key_pressed = gnt_text_view_key_pressed;
429 parent_class->clicked = gnt_text_view_clicked;
430 parent_class->size_changed = gnt_text_view_size_changed;
432 GNTDEBUG;
435 static void
436 gnt_text_view_init(GTypeInstance *instance, gpointer class)
438 GntWidget *widget = GNT_WIDGET(instance);
439 GntTextView *view = GNT_TEXT_VIEW(widget);
440 GntTextLine *line = g_new0(GntTextLine, 1);
442 GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW |
443 GNT_WIDGET_GROW_Y | GNT_WIDGET_GROW_X);
444 widget->priv.minw = 5;
445 widget->priv.minh = 2;
446 view->string = g_string_new(NULL);
447 view->list = g_list_append(view->list, line);
449 GNTDEBUG;
452 /******************************************************************************
453 * GntTextView API
454 *****************************************************************************/
455 GType
456 gnt_text_view_get_gtype(void)
458 static GType type = 0;
460 if(type == 0)
462 static const GTypeInfo info = {
463 sizeof(GntTextViewClass),
464 NULL, /* base_init */
465 NULL, /* base_finalize */
466 (GClassInitFunc)gnt_text_view_class_init,
467 NULL, /* class_finalize */
468 NULL, /* class_data */
469 sizeof(GntTextView),
470 0, /* n_preallocs */
471 gnt_text_view_init, /* instance_init */
472 NULL /* value_table */
475 type = g_type_register_static(GNT_TYPE_WIDGET,
476 "GntTextView",
477 &info, 0);
480 return type;
483 GntWidget *gnt_text_view_new()
485 GntWidget *widget = g_object_new(GNT_TYPE_TEXT_VIEW, NULL);
487 return widget;
490 void gnt_text_view_append_text_with_flags(GntTextView *view, const char *text, GntTextFormatFlags flags)
492 gnt_text_view_append_text_with_tag(view, text, flags, NULL);
495 void gnt_text_view_append_text_with_tag(GntTextView *view, const char *text,
496 GntTextFormatFlags flags, const char *tagname)
498 GntWidget *widget = GNT_WIDGET(view);
499 int fl = 0;
500 const char *start, *end;
501 GList *list = view->list;
502 GntTextLine *line;
503 int len;
504 gboolean has_scroll = !(view->flags & GNT_TEXT_VIEW_NO_SCROLL);
505 gboolean wrap_word = !(view->flags & GNT_TEXT_VIEW_WRAP_CHAR);
507 if (text == NULL || *text == '\0')
508 return;
510 fl = gnt_text_format_flag_to_chtype(flags);
512 len = view->string->len;
513 view->string = g_string_append(view->string, text);
515 if (tagname) {
516 GntTextTag *tag = g_new0(GntTextTag, 1);
517 tag->name = g_strdup(tagname);
518 tag->start = len;
519 tag->end = view->string->len;
520 view->tags = g_list_append(view->tags, tag);
523 view->list = g_list_first(view->list);
525 start = end = view->string->str + len;
527 while (*start) {
528 GntTextLine *oldl;
529 GntTextSegment *seg = NULL;
531 if (*end == '\n' || *end == '\r') {
532 if (!strncmp(end, "\r\n", 2))
533 end++;
534 end++;
535 start = end;
536 gnt_text_view_next_line(view);
537 view->list = g_list_first(view->list);
538 continue;
541 line = view->list->data;
542 if (line->length == widget->priv.width - has_scroll) {
543 /* The last added line was exactly the same width as the widget */
544 line = g_new0(GntTextLine, 1);
545 line->soft = TRUE;
546 view->list = g_list_prepend(view->list, line);
549 if ((end = strchr(start, '\r')) != NULL ||
550 (end = strchr(start, '\n')) != NULL) {
551 len = gnt_util_onscreen_width(start, end - has_scroll);
552 if (widget->priv.width > 0 &&
553 len >= widget->priv.width - line->length - has_scroll) {
554 end = NULL;
558 if (end == NULL)
559 end = gnt_util_onscreen_width_to_pointer(start,
560 widget->priv.width - line->length - has_scroll, &len);
562 /* Try to append to the previous segment if possible */
563 if (line->segments) {
564 seg = g_list_last(line->segments)->data;
565 if (seg->flags != fl)
566 seg = NULL;
569 if (seg == NULL) {
570 seg = g_new0(GntTextSegment, 1);
571 seg->start = start - view->string->str;
572 seg->tvflag = flags;
573 seg->flags = fl;
574 line->segments = g_list_append(line->segments, seg);
577 oldl = line;
578 if (wrap_word && *end && *end != '\n' && *end != '\r') {
579 const char *tmp = end;
580 while (end && *end != '\n' && *end != '\r' && !g_ascii_isspace(*end)) {
581 end = g_utf8_find_prev_char(seg->start + view->string->str, end);
583 if (!end || !g_ascii_isspace(*end))
584 end = tmp;
585 else
586 end++; /* Remove the space */
588 line = g_new0(GntTextLine, 1);
589 line->soft = TRUE;
590 view->list = g_list_prepend(view->list, line);
592 seg->end = end - view->string->str;
593 oldl->length += len;
594 start = end;
597 view->list = list;
599 gnt_widget_draw(widget);
602 void gnt_text_view_scroll(GntTextView *view, int scroll)
604 if (scroll == 0)
606 view->list = g_list_first(view->list);
608 else if (scroll > 0)
610 GList *list = g_list_nth_prev(view->list, scroll);
611 if (list == NULL)
612 list = g_list_first(view->list);
613 view->list = list;
615 else if (scroll < 0)
617 GList *list = g_list_nth(view->list, -scroll);
618 if (list == NULL)
619 list = g_list_last(view->list);
620 view->list = list;
623 gnt_widget_draw(GNT_WIDGET(view));
626 void gnt_text_view_next_line(GntTextView *view)
628 GntTextLine *line = g_new0(GntTextLine, 1);
629 GList *list = view->list;
631 view->list = g_list_prepend(g_list_first(view->list), line);
632 view->list = list;
633 gnt_widget_draw(GNT_WIDGET(view));
636 chtype gnt_text_format_flag_to_chtype(GntTextFormatFlags flags)
638 chtype fl = 0;
640 if (flags & GNT_TEXT_FLAG_BOLD)
641 fl |= A_BOLD;
642 if (flags & GNT_TEXT_FLAG_UNDERLINE)
643 fl |= A_UNDERLINE;
644 if (flags & GNT_TEXT_FLAG_BLINK)
645 fl |= A_BLINK;
647 if (flags & GNT_TEXT_FLAG_DIM)
648 fl |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED));
649 else if (flags & GNT_TEXT_FLAG_HIGHLIGHT)
650 fl |= (A_DIM | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
651 else
652 fl |= gnt_color_pair(GNT_COLOR_NORMAL);
654 return fl;
657 void gnt_text_view_clear(GntTextView *view)
659 GntTextLine *line;
661 g_list_foreach(view->list, free_text_line, NULL);
662 g_list_free(view->list);
663 view->list = NULL;
665 line = g_new0(GntTextLine, 1);
666 view->list = g_list_append(view->list, line);
667 if (view->string)
668 g_string_free(view->string, TRUE);
669 view->string = g_string_new(NULL);
671 if (GNT_WIDGET(view)->window)
672 gnt_widget_draw(GNT_WIDGET(view));
675 int gnt_text_view_get_lines_below(GntTextView *view)
677 int below = 0;
678 GList *list = view->list;
679 while ((list = list->prev))
680 ++below;
681 return below;
684 int gnt_text_view_get_lines_above(GntTextView *view)
686 int above = 0;
687 GList *list = view->list;
688 list = g_list_nth(view->list, GNT_WIDGET(view)->priv.height);
689 if (!list)
690 return 0;
691 while ((list = list->next))
692 ++above;
693 return above;
697 * XXX: There are quite possibly more than a few bugs here.
699 int gnt_text_view_tag_change(GntTextView *view, const char *name, const char *text, gboolean all)
701 GList *alllines = g_list_first(view->list);
702 GList *list, *next, *iter, *inext;
703 const int text_length = text ? strlen(text) : 0;
704 int count = 0;
705 for (list = view->tags; list; list = next) {
706 GntTextTag *tag = list->data;
707 next = list->next;
708 if (strcmp(tag->name, name) == 0) {
709 int change;
710 char *before, *after;
712 count++;
714 before = g_strndup(view->string->str, tag->start);
715 after = g_strdup(view->string->str + tag->end);
716 change = (tag->end - tag->start) - text_length;
718 g_string_printf(view->string, "%s%s%s", before, text ? text : "", after);
719 g_free(before);
720 g_free(after);
722 /* Update the offsets of the next tags */
723 for (iter = next; iter; iter = iter->next) {
724 GntTextTag *t = iter->data;
725 t->start -= change;
726 t->end -= change;
729 /* Update the offsets of the segments */
730 for (iter = alllines; iter; iter = inext) {
731 GList *segs, *snext;
732 GntTextLine *line = iter->data;
733 inext = iter->next;
734 for (segs = line->segments; segs; segs = snext) {
735 GntTextSegment *seg = segs->data;
736 snext = segs->next;
737 if (seg->start >= tag->end) {
738 /* The segment is somewhere after the tag */
739 seg->start -= change;
740 seg->end -= change;
741 } else if (seg->end <= tag->start) {
742 /* This segment is somewhere in front of the tag */
743 } else if (seg->start >= tag->start) {
744 /* This segment starts in the middle of the tag */
745 if (text == NULL) {
746 free_text_segment(seg, NULL);
747 line->segments = g_list_delete_link(line->segments, segs);
748 if (line->segments == NULL) {
749 free_text_line(line, NULL);
750 if (view->list == iter) {
751 if (inext)
752 view->list = inext;
753 else
754 view->list = iter->prev;
756 alllines = g_list_delete_link(alllines, iter);
758 } else {
759 /* XXX: (null) */
760 seg->start = tag->start;
761 seg->end = tag->end - change;
763 line->length -= change;
764 /* XXX: Make things work if the tagged text spans over several lines. */
765 } else {
766 /* XXX: handle the rest of the conditions */
767 g_printerr("WTF! This needs to be handled properly!!\n");
771 if (text == NULL) {
772 /* Remove the tag */
773 view->tags = g_list_delete_link(view->tags, list);
774 free_tag(tag, NULL);
775 } else {
776 tag->end -= change;
778 if (!all)
779 break;
782 return count;
785 static gboolean
786 scroll_tv(GntWidget *wid, const char *key, GntTextView *tv)
788 if (strcmp(key, GNT_KEY_PGUP) == 0) {
789 gnt_text_view_scroll(tv, -(GNT_WIDGET(tv)->priv.height - 2));
790 } else if (strcmp(key, GNT_KEY_PGDOWN) == 0) {
791 gnt_text_view_scroll(tv, GNT_WIDGET(tv)->priv.height - 2);
792 } else if (strcmp(key, GNT_KEY_DOWN) == 0) {
793 gnt_text_view_scroll(tv, 1);
794 } else if (strcmp(key, GNT_KEY_UP) == 0) {
795 gnt_text_view_scroll(tv, -1);
796 } else {
797 return FALSE;
799 return TRUE;
802 void gnt_text_view_attach_scroll_widget(GntTextView *view, GntWidget *widget)
804 g_signal_connect(G_OBJECT(widget), "key_pressed", G_CALLBACK(scroll_tv), view);
807 void gnt_text_view_set_flag(GntTextView *view, GntTextViewFlag flag)
809 view->flags |= flag;
812 /* Pager and editor setups */
813 struct
815 GntTextView *tv;
816 char *file;
817 } pageditor;
820 static void
821 cleanup_pageditor()
823 unlink(pageditor.file);
824 g_free(pageditor.file);
826 pageditor.file = NULL;
827 pageditor.tv = NULL;
830 static void
831 editor_end_cb(int status, gpointer data)
833 if (status == 0) {
834 char *text = NULL;
835 if (g_file_get_contents(pageditor.file, &text, NULL, NULL)) {
836 gnt_text_view_clear(pageditor.tv);
837 gnt_text_view_append_text_with_flags(pageditor.tv, text, GNT_TEXT_FLAG_NORMAL);
838 gnt_text_view_scroll(GNT_TEXT_VIEW(pageditor.tv), 0);
839 g_free(text);
842 cleanup_pageditor();
845 static void
846 pager_end_cb(int status, gpointer data)
848 cleanup_pageditor();
851 static gboolean
852 check_for_ext_cb(GntWidget *widget, const char *key, GntTextView *view)
854 static const char *pager = NULL;
855 static const char *editor = NULL;
856 char *argv[] = {NULL, NULL, NULL};
857 static char path[1024];
858 static int len = -1;
859 FILE *file;
860 gboolean ret;
861 gboolean pg;
863 if (pager == NULL) {
864 pager = gnt_key_translate(gnt_style_get_from_name("pager", "key"));
865 if (pager == NULL)
866 pager = "\033" "v";
867 editor = gnt_key_translate(gnt_style_get_from_name("editor", "key"));
868 if (editor == NULL)
869 editor = "\033" "e";
870 len = g_snprintf(path, sizeof(path), "%s" G_DIR_SEPARATOR_S "gnt", g_get_tmp_dir());
871 } else {
872 g_snprintf(path + len, sizeof(path) - len, "XXXXXX");
875 if (strcmp(key, pager) == 0) {
876 if (g_object_get_data(G_OBJECT(widget), "pager-for") != view)
877 return FALSE;
878 pg = TRUE;
879 } else if (strcmp(key, editor) == 0) {
880 if (g_object_get_data(G_OBJECT(widget), "editor-for") != view)
881 return FALSE;
882 pg = FALSE;
883 } else {
884 return FALSE;
887 file = fdopen(g_mkstemp(path), "wb");
888 if (!file)
889 return FALSE;
890 fprintf(file, "%s", view->string->str);
891 fclose(file);
893 pageditor.tv = view;
894 pageditor.file = g_strdup(path);
896 argv[0] = gnt_style_get_from_name(pg ? "pager" : "editor", "path");
897 argv[0] = argv[0] ? argv[0] : getenv(pg ? "PAGER" : "EDITOR");
898 argv[0] = argv[0] ? argv[0] : (pg ? "less" : "vim");
899 argv[1] = path;
900 ret = gnt_giveup_console(NULL, argv, NULL, NULL, NULL, NULL, pg ? pager_end_cb : editor_end_cb, NULL);
901 return ret;
904 void gnt_text_view_attach_pager_widget(GntTextView *view, GntWidget *pager)
906 g_signal_connect(pager, "key_pressed", G_CALLBACK(check_for_ext_cb), view);
907 g_object_set_data(G_OBJECT(pager), "pager-for", view);
910 void gnt_text_view_attach_editor_widget(GntTextView *view, GntWidget *wid)
912 g_signal_connect(wid, "key_pressed", G_CALLBACK(check_for_ext_cb), view);
913 g_object_set_data(G_OBJECT(wid), "editor-for", view);