From 44a1aa9c87d72bd5020e68ef6e62f5a363a54b54 Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Sat, 14 Jan 2006 22:44:00 +0100 Subject: [PATCH] Witekfl's UTF-8 patch v5. --- src/bfu/button.c | 25 +++++ src/bfu/dialog.c | 12 ++- src/bfu/inpfield.c | 164 +++++++++++++++++++++++++++-- src/bfu/menu.c | 29 +++++ src/dialogs/options.c | 1 - src/document/dom/renderer.c | 40 +++++++ src/document/html/renderer.c | 103 +++++++++++++----- src/document/options.h | 1 + src/document/plain/renderer.c | 136 ++++++++++++++++++++++++ src/intl/charsets.c | 106 ++++++++++++++++++- src/intl/charsets.h | 4 + src/terminal/draw.c | 45 +++++++- src/terminal/draw.h | 6 +- src/terminal/event.c | 14 +-- src/terminal/screen.c | 90 +++++++--------- src/terminal/terminal.h | 3 + src/viewer/dump/dump.c | 92 +++++++++++++++- src/viewer/text/form.c | 169 ++++++++++++++++++++++++++--- src/viewer/text/form.h | 1 + src/viewer/text/link.c | 8 +- src/viewer/text/textarea.c | 193 ++++++++++++++++++++++++++++------ src/viewer/text/textarea.h | 16 +-- 22 files changed, 1099 insertions(+), 159 deletions(-) diff --git a/src/bfu/button.c b/src/bfu/button.c index 48afb3a6..5c1ff8a4 100644 --- a/src/bfu/button.c +++ b/src/bfu/button.c @@ -167,6 +167,31 @@ display_button(struct dialog_data *dlg_data, struct widget_data *widget_data) attr = get_opt_bool("ui.dialogs.underline_button_shortcuts") ? SCREEN_ATTR_UNDERLINE : 0; + if (term->utf8) { + unsigned char *text2 = text; + unsigned char *end = text + + widget_data->widget->info.button.truetextlen; + int hk_state = 0; + int x1; + + for (x1 = 0; x1 - !!hk_state < len && *text2; x1++) { + uint16_t data; + + data = (uint16_t)utf_8_to_unicode(&text2, end); + if (!hk_state && (int)(text2 - text) == hk_pos + 1) { + hk_state = 1; + continue; + } + if (hk_state == 1) { + draw_char(term, x + x1 - 1, pos->y, data, attr, shortcut_color); + hk_state = 2; + } else { + draw_char(term, x + x1 - !!hk_state, pos->y, data, 0, color); + } + + } + len = x1 - !!hk_state; + } else if (hk_pos >= 0) { int right = widget_data->widget->info.button.truetextlen - hk_pos - 1; diff --git a/src/bfu/dialog.c b/src/bfu/dialog.c index 042a172c..554b9dbc 100644 --- a/src/bfu/dialog.c +++ b/src/bfu/dialog.c @@ -12,6 +12,7 @@ #include "bfu/dialog.h" #include "config/kbdbind.h" #include "config/options.h" +#include "intl/charsets.h" #include "intl/gettext/libintl.h" #include "terminal/draw.h" #include "main/timer.h" @@ -96,13 +97,18 @@ redraw_dialog(struct dialog_data *dlg_data, int layout) title_color = get_bfu_color(term, "dialog.title"); if (title_color && box.width > 2) { unsigned char *title = dlg_data->dlg->title; - int titlelen = int_min(box.width - 2, strlen(title)); - int x = (box.width - titlelen) / 2 + box.x; + unsigned char *t2 = title; + int titlelen = strlen(title); + int len = term->utf8 ? strlen_utf8(&t2) : titlelen; +#if 1 + len = int_min(box.width - 2, len); +#endif + int x = (box.width - len) / 2 + box.x; int y = box.y - 1; draw_text(term, x - 1, y, " ", 1, 0, title_color); draw_text(term, x, y, title, titlelen, 0, title_color); - draw_text(term, x + titlelen, y, " ", 1, 0, title_color); + draw_text(term, x + len, y, " ", 1, 0, title_color); } } diff --git a/src/bfu/inpfield.c b/src/bfu/inpfield.c index 5c4359de..969a17aa 100644 --- a/src/bfu/inpfield.c +++ b/src/bfu/inpfield.c @@ -17,6 +17,7 @@ #include "bfu/msgbox.h" #include "bfu/text.h" #include "config/kbdbind.h" +#include "intl/charsets.h" #include "intl/gettext/libintl.h" #include "osdep/osdep.h" #include "session/session.h" @@ -101,6 +102,71 @@ check_nonempty(struct dialog_data *dlg_data, struct widget_data *widget_data) return EVENT_NOT_PROCESSED; } +#if 0 +void +dlg_format_field(struct terminal *term, + struct widget_data *widget_data, + int x, int *y, int w, int *rw, enum format_align align) +{ + static int max_label_width; + static int *prev_y; /* Assert the uniqueness of y */ /* TODO: get rid of this !! --Zas */ + unsigned char *label = widget_data->widget->text; + struct color_pair *text_color = NULL; + int label_width = 0; + int float_label = widget_data->widget->info.field.flags & (INPFIELD_FLOAT|INPFIELD_FLOAT2); + + if (label && *label && float_label) { + unsigned char *l2 = label; + int len = strlen(label); + + label_width = term->utf8 ? strlen_utf8(&l2) : len; + if (prev_y == y) { + int_lower_bound(&max_label_width, label_width); + } else { + max_label_width = label_width; + prev_y = y; + } + + /* Right align the floating label up against the + * input field */ + x += max_label_width - label_width; + w -= max_label_width - len; + } + + if (label && *label) { + if (term) text_color = get_bfu_color(term, "dialog.text"); + + dlg_format_text_do(term, label, x, y, w, rw, text_color, ALIGN_LEFT); + } + + /* XXX: We want the field and label on the same line if the terminal + * width allows it. */ + if (label && *label && float_label) { + if (widget_data->widget->info.field.flags & INPFIELD_FLOAT) { + (*y) -= INPUTFIELD_HEIGHT; + dlg_format_text_do(term, INPUTFIELD_FLOAT_SEPARATOR, + x + label_width, y, w, rw, + text_color, ALIGN_LEFT); + w -= INPUTFIELD_FLOAT_SEPARATOR_LEN + INPUTFIELD_FLOATLABEL_PADDING; + x += INPUTFIELD_FLOAT_SEPARATOR_LEN + INPUTFIELD_FLOATLABEL_PADDING; + } + + /* FIXME: Is 5 chars for input field enough? --jonas */ + if (label_width < w - 5) { + (*y) -= INPUTFIELD_HEIGHT; + w -= label_width; + x += label_width; + } + } + + if (rw) int_lower_bound(rw, int_min(w, DIALOG_MIN_WIDTH)); + + set_box(&widget_data->box, x, *y, w, INPUTFIELD_HEIGHT); + + (*y) += INPUTFIELD_HEIGHT; +} +#endif + void dlg_format_field(struct terminal *term, struct widget_data *widget_data, @@ -265,11 +331,30 @@ display_field_do(struct dialog_data *dlg_data, struct widget_data *widget_data, struct terminal *term = dlg_data->win->term; struct color_pair *color; int sel = is_selected_widget(dlg_data, widget_data); + int len = 0, left = 0; - int_bounds(&widget_data->info.field.vpos, + if (term->utf8) { + unsigned char *t = widget_data->cdata; + unsigned char *t2 = t; + int p = widget_data->info.field.cpos; + unsigned char tmp = t[p]; + int x; + + t[p] = '\0'; + len = strlen_utf8(&t2); + int_bounds(&left, len - widget_data->box.width + 1, len); + int_lower_bound(&left, 0); + for (t2 = t, x = 0; x < left; x++) { + utf_8_to_unicode(&t2, &t[p]); + } + t[p] = tmp; + widget_data->info.field.vpos = (int)(t2 - t); + } else { + int_bounds(&widget_data->info.field.vpos, widget_data->info.field.cpos - widget_data->box.width + 1, widget_data->info.field.cpos); - int_lower_bound(&widget_data->info.field.vpos, 0); + int_lower_bound(&widget_data->info.field.vpos, 0); + } color = get_bfu_color(term, "dialog.field"); if (color) @@ -277,7 +362,8 @@ display_field_do(struct dialog_data *dlg_data, struct widget_data *widget_data, color = get_bfu_color(term, "dialog.field-text"); if (color) { - int len = strlen(widget_data->cdata + widget_data->info.field.vpos); + unsigned char *text = widget_data->cdata + widget_data->info.field.vpos; + int len = strlen(text); int w = int_min(len, widget_data->box.width); if (!hide) { @@ -287,6 +373,9 @@ display_field_do(struct dialog_data *dlg_data, struct widget_data *widget_data, } else { struct box box; + if (term->utf8) len = strlen_utf8(&text); + w = int_min(len, widget_data->box.width); + copy_box(&box, &widget_data->box); box.width = w; @@ -295,8 +384,14 @@ display_field_do(struct dialog_data *dlg_data, struct widget_data *widget_data, } if (sel) { - int x = widget_data->box.x + widget_data->info.field.cpos - widget_data->info.field.vpos; + int x; + if (term->utf8) { + x = widget_data->box.x + len - left; + } else { + x = widget_data->box.x + widget_data->info.field.cpos - widget_data->info.field.vpos; + + } set_cursor(term, x, widget_data->box.y, 0); set_window_ptr(dlg_data->win, widget_data->box.x, widget_data->box.y); } @@ -435,13 +530,33 @@ kbd_field(struct dialog_data *dlg_data, struct widget_data *widget_data) break; case ACT_EDIT_RIGHT: - if (widget_data->info.field.cpos < strlen(widget_data->cdata)) - widget_data->info.field.cpos++; + if (widget_data->info.field.cpos < strlen(widget_data->cdata)) { + if (term->utf8) { + unsigned char *next = widget_data->cdata + widget_data->info.field.cpos; + unsigned char *end = strchr(next, '\0'); + + utf_8_to_unicode(&next, end); + widget_data->info.field.cpos = (int)(next - widget_data->cdata); + } else + widget_data->info.field.cpos++; + } goto display_field; case ACT_EDIT_LEFT: if (widget_data->info.field.cpos > 0) widget_data->info.field.cpos--; + if (widget_data->info.field.cpos && term->utf8) { + unsigned char *t = widget_data->cdata; + unsigned char *t2 = t; + int p = widget_data->info.field.cpos; + unsigned char tmp = t[p]; + + t[p] = '\0'; + strlen_utf8(&t2); + t[p] = tmp; + widget_data->info.field.cpos = (int)(t2 - t); + + } goto display_field; case ACT_EDIT_HOME: @@ -453,6 +568,19 @@ kbd_field(struct dialog_data *dlg_data, struct widget_data *widget_data) goto display_field; case ACT_EDIT_BACKSPACE: + if (widget_data->info.field.cpos && term->utf8) { + unsigned char *t = widget_data->cdata; + unsigned char *t2 = t; + int p = widget_data->info.field.cpos - 1; + unsigned char tmp = t[p]; + + t[p] = '\0'; + strlen_utf8(&t2); + t[p] = tmp; + memmove(t2, &t[p + 1], strlen(&t[p + 1]) + 1); + widget_data->info.field.cpos = (int)(t2 - t); + goto display_field; + } if (widget_data->info.field.cpos) { memmove(widget_data->cdata + widget_data->info.field.cpos - 1, widget_data->cdata + widget_data->info.field.cpos, @@ -467,6 +595,15 @@ kbd_field(struct dialog_data *dlg_data, struct widget_data *widget_data) if (widget_data->info.field.cpos >= cdata_len) goto display_field; + if (term->utf8) { + unsigned char *next = widget_data->cdata + widget_data->info.field.cpos; + unsigned char *dest = next; + unsigned char *end = strchr(next, '\0'); + + utf_8_to_unicode(&next, end); + memmove(dest, next, strlen(next) + 1); + goto display_field; + } memmove(widget_data->cdata + widget_data->info.field.cpos, widget_data->cdata + widget_data->info.field.cpos + 1, cdata_len - widget_data->info.field.cpos + 1); @@ -548,7 +685,20 @@ kbd_field(struct dialog_data *dlg_data, struct widget_data *widget_data) memmove(text + 1, text, textlen + 1); *text = get_kbd_key(ev); - + if (term->utf8) { + static unsigned char buf[7]; + unsigned char *t = buf; + static int i = 0; + unicode_val_T data; + + buf[i++] = *text; + buf[i] = '\0'; + data = utf_8_to_unicode(&t, buf + i); + if (i == 6) i = 0; + if (data == UCS_NO_CHAR) + return EVENT_PROCESSED; + else i = 0; + } goto display_field; } } diff --git a/src/bfu/menu.c b/src/bfu/menu.c index a7e1fb06..0b71a648 100644 --- a/src/bfu/menu.c +++ b/src/bfu/menu.c @@ -346,10 +346,12 @@ draw_menu_left_text_hk(struct terminal *term, unsigned char *text, struct color_pair *hk_color_sel = get_bfu_color(term, "menu.hotkey.selected"); enum screen_char_attr hk_attr = get_opt_bool("ui.dialogs.underline_hotkeys") ? SCREEN_ATTR_UNDERLINE : 0; + unsigned char *text2, *end; unsigned char c; int xbase = x + L_TEXT_SPACE; int w = width - (L_TEXT_SPACE + R_TEXT_SPACE); int hk_state = 0; + #ifdef CONFIG_DEBUG /* For redundant hotkeys highlighting. */ int double_hk = 0; @@ -366,6 +368,7 @@ draw_menu_left_text_hk(struct terminal *term, unsigned char *text, hk_color_sel = tmp; } + if (term->utf8) goto utf8; for (x = 0; x - !!hk_state < w && (c = text[x]); x++) { if (!hk_state && x == hotkey_pos - 1) { hk_state = 1; @@ -384,6 +387,32 @@ draw_menu_left_text_hk(struct terminal *term, unsigned char *text, draw_char(term, xbase + x - !!hk_state, y, c, 0, color); } } + return; +utf8: + end = strchr(text, '\0'); + text2 = text; + for (x = 0; x - !!hk_state < w && *text2; x++) { + unicode_val_T data; + + data = utf_8_to_unicode(&text2, end); + if (!hk_state && (int)(text2 - text) == hotkey_pos) { + hk_state = 1; + continue; + } + if (hk_state == 1) { +#ifdef CONFIG_DEBUG + draw_char(term, xbase + x - 1, y, data, hk_attr, + (double_hk ? hk_color_sel : hk_color)); +#else + draw_char(term, xbase + x - 1, y, data, hk_attr, hk_color); +#endif + hk_state = 2; + } else { + draw_char(term, xbase + x - !!hk_state, y, data, 0, color); + } + + } + } static inline void diff --git a/src/dialogs/options.c b/src/dialogs/options.c index 70836da3..906bc83f 100644 --- a/src/dialogs/options.c +++ b/src/dialogs/options.c @@ -59,7 +59,6 @@ charset_list(struct terminal *term, void *xxx, void *ses_) unsigned char *name = get_cp_name(i); if (!name) break; - if (is_cp_special(i)) continue; add_to_menu(&mi, name, NULL, ACT_MAIN_NONE, display_codepage, get_cp_mime_name(i), 0); diff --git a/src/document/dom/renderer.c b/src/document/dom/renderer.c index 0dfb3890..0461aacb 100644 --- a/src/document/dom/renderer.c +++ b/src/document/dom/renderer.c @@ -250,8 +250,11 @@ render_dom_line(struct dom_renderer *renderer, struct screen_char *template, struct document *document = renderer->document; struct conv_table *convert = renderer->convert_table; enum convert_string_mode mode = renderer->convert_mode; + int utf8 = document->options.utf8; + unsigned char *end, *text; int x; + assert(renderer && template && string && length); string = convert_string(convert, string, length, document->options.cp, @@ -265,6 +268,7 @@ render_dom_line(struct dom_renderer *renderer, struct screen_char *template, add_search_node(renderer, length); + if (utf8) goto utf_8; for (x = 0; x < length; x++, renderer->canvas_x++) { unsigned char data = string[x]; @@ -293,7 +297,42 @@ render_dom_line(struct dom_renderer *renderer, struct screen_char *template, copy_screen_chars(POS(renderer), template, 1); } + goto end; +utf_8: + end = string + length; + for (text = string; text < end; renderer->canvas_x++) { + unsigned char data = *text; + unicode_val_T d2; + /* This is mostly to be able to break out so the indentation + * level won't get to high. */ + switch (data) { + case ASCII_TAB: + { + int tab_width = 7 - (X(renderer) & 7); + int width = WIDTH(renderer, end - text + tab_width); + + template->data = ' '; + + if (!realloc_line(document, width, Y(renderer))) + break; + + /* Only loop over the expanded tab chars and let the + * ``main loop'' add the actual tab char. */ + for (; tab_width-- > 0; renderer->canvas_x++) + copy_screen_chars(POS(renderer), template, 1); + text++; + break; + } + default: + d2 = utf_8_to_unicode(&text, end); + if (d2 == UCS_NO_CHAR) text++; + template->data = (uint16_t)d2; + } + + copy_screen_chars(POS(renderer), template, 1); + } +end: mem_free(string); } @@ -989,6 +1028,7 @@ render_dom_document(struct cache_entry *cached, struct document *document, init_dom_renderer(&renderer, document, buffer, convert_table); document->bgcolor = document->options.default_bg; + document->options.utf8 = is_cp_special(document->options.cp); if (document->options.plain) parser_type = SGML_PARSER_STREAM; diff --git a/src/document/html/renderer.c b/src/document/html/renderer.c index 63915faf..e9a8a5a4 100644 --- a/src/document/html/renderer.c +++ b/src/document/html/renderer.c @@ -23,6 +23,7 @@ #include "document/refresh.h" #include "document/renderer.h" #include "intl/charsets.h" +#include "osdep/types.h" #include "protocol/uri.h" #include "session/session.h" #include "terminal/color.h" @@ -404,7 +405,7 @@ get_format_screen_char(struct html_context *html_context, /* First possibly do the format change and then find out what coordinates * to use since sub- or superscript might change them */ -static inline void +static inline int set_hline(struct html_context *html_context, unsigned char *chars, int charslen, enum link_state link_state) { @@ -413,34 +414,83 @@ set_hline(struct html_context *html_context, unsigned char *chars, int charslen, link_state); int x = part->cx; int y = part->cy; + int x2 = x; + int len = charslen; + int utf8 = html_context->options->utf8; assert(part); - if_assert_failed return; + if_assert_failed return len; + + assert(charslen >= 0); if (realloc_spaces(part, x + charslen)) - return; + return len; if (part->document) { if (realloc_line(html_context, part->document, Y(y), X(x) + charslen - 1)) - return; + return len; + if (utf8) { + unsigned char *end; - for (; charslen > 0; charslen--, x++, chars++) { - if (*chars == NBSP_CHAR) { - schar->data = ' '; - part->spaces[x] = html_context->options->wrap_nbsp; - } else { - part->spaces[x] = (*chars == ' '); - schar->data = *chars; + for (end = chars + charslen; chars < end; x++) { + if (*chars == NBSP_CHAR) { + schar->data = ' '; + part->spaces[x] = html_context->options->wrap_nbsp; + chars++; + } else { + unicode_val_T data; + + part->spaces[x] = (*chars == ' '); + data = utf_8_to_unicode(&chars, end); + if (data == UCS_NO_CHAR) { + /* HR */ + unsigned char attr = schar->attr; + + schar->data = *chars++; + schar->attr = SCREEN_ATTR_FRAME; + copy_screen_chars(&POS(x, y), schar, 1); + schar->attr = attr; + continue; + } else { + schar->data = (uint16_t)data; + } + } + copy_screen_chars(&POS(x, y), schar, 1); + } + } else { + for (; charslen > 0; charslen--, x++, chars++) { + if (*chars == NBSP_CHAR) { + schar->data = ' '; + part->spaces[x] = html_context->options->wrap_nbsp; + } else { + part->spaces[x] = (*chars == ' '); + schar->data = *chars; + } + copy_screen_chars(&POS(x, y), schar, 1); } - copy_screen_chars(&POS(x, y), schar, 1); } + len = x - x2; } else { - for (; charslen > 0; charslen--, x++, chars++) { - part->spaces[x] = (*chars == ' '); + if (utf8) { + unsigned char *end; + + for (end = chars + charslen; chars < end; x++) { + unicode_val_T data; + + part->spaces[x] = (*chars == ' '); + data = utf_8_to_unicode(&chars, end); + if (data == UCS_NO_CHAR) chars++; + } + len = x - x2; + } else { + for (; charslen > 0; charslen--, x++, chars++) { + part->spaces[x] = (*chars == ' '); + } } } + return len; } static void @@ -1170,7 +1220,7 @@ done_link_state_info(void) static inline void process_link(struct html_context *html_context, enum link_state link_state, - unsigned char *chars, int charslen) + unsigned char *chars, int charslen, int utf8_len) { struct part *part = html_context->part; struct link *link; @@ -1222,6 +1272,7 @@ process_link(struct html_context *html_context, enum link_state link_state, if (x_offset) { charslen -= x_offset; chars += x_offset; + utf8_len -= x_offset; } link = new_link(html_context, chars, charslen); @@ -1236,14 +1287,14 @@ process_link(struct html_context *html_context, enum link_state link_state, } /* Add new canvas positions to the link. */ - if (realloc_points(link, link->npoints + charslen)) { + if (realloc_points(link, link->npoints + utf8_len)) { struct point *point = &link->points[link->npoints]; int x = X(part->cx) + x_offset; int y = Y(part->cy); - link->npoints += charslen; + link->npoints += utf8_len; - for (; charslen > 0; charslen--, point++, x++) { + for (; utf8_len > 0; utf8_len--, point++, x++) { point->x = x; point->y = y; } @@ -1295,6 +1346,7 @@ put_chars(struct html_context *html_context, unsigned char *chars, int charslen) enum link_state link_state; int update_after_subscript = renderer_context.subscript; struct part *part; + int utf8_len; assert(html_context); if_assert_failed return; @@ -1353,9 +1405,7 @@ put_chars(struct html_context *html_context, unsigned char *chars, int charslen) else if (html_context->options->links_numbering) put_link_number(html_context); } - - set_hline(html_context, chars, charslen, link_state); - + utf8_len = set_hline(html_context, chars, charslen, link_state); if (link_state != LINK_STATE_NONE) { #define is_drawing_subs_or_sups() \ @@ -1375,15 +1425,15 @@ put_chars(struct html_context *html_context, unsigned char *chars, int charslen) } #undef is_drawing_subs_or_sups - - process_link(html_context, link_state, chars, charslen); + process_link(html_context, link_state, chars, charslen, + utf8_len); } if (renderer_context.nowrap - && part->cx + charslen > overlap(par_format)) + && part->cx + utf8_len > overlap(par_format)) return; - part->cx += charslen; + part->cx += utf8_len; renderer_context.nobreak = 0; if (!(html_context->options->wrap || html_is_preformatted())) { @@ -1399,7 +1449,7 @@ put_chars(struct html_context *html_context, unsigned char *chars, int charslen) } assert(charslen > 0); - part->xa += charslen; + part->xa += utf8_len; int_lower_bound(&part->max_width, part->xa + par_format.leftmargin + par_format.rightmargin - (chars[charslen - 1] == ' ' @@ -1963,6 +2013,7 @@ render_html_document(struct cache_entry *cached, struct document *document, &document->cp, &document->cp_status, document->options.hard_assume); + html_context->options->utf8 = is_cp_special(document->options.cp); if (title.length) { document->title = convert_string(renderer_context.convert_table, diff --git a/src/document/options.h b/src/document/options.h index a540fd7f..db510b33 100644 --- a/src/document/options.h +++ b/src/document/options.h @@ -71,6 +71,7 @@ struct document_options { unsigned int plain:1; unsigned int wrap:1; + unsigned int utf8:1; /* XXX: Everything past this comment is specialy handled by compare_opt() */ unsigned char *framename; diff --git a/src/document/plain/renderer.c b/src/document/plain/renderer.c index bd4ffa49..f19af844 100644 --- a/src/document/plain/renderer.c +++ b/src/document/plain/renderer.c @@ -234,8 +234,10 @@ add_document_line(struct plain_renderer *renderer, struct screen_char *template = &renderer->template; struct screen_char saved_renderer_template = *template; struct screen_char *pos, *startpos; + unsigned char *end, *text; int lineno = renderer->lineno; int expanded = 0; + int utf8 = document->options.utf8; int width = line_width; int line_pos; @@ -276,6 +278,7 @@ add_document_line(struct plain_renderer *renderer, assert(expanded >= 0); + if (utf8) goto utf_8; startpos = pos = realloc_line(document, width + expanded, lineno); if (!pos) { mem_free(line); @@ -401,7 +404,139 @@ add_document_line(struct plain_renderer *renderer, *template = saved_renderer_template; } } + goto end; +utf_8: + end = line + width; + startpos = pos = realloc_line(document, width + expanded, lineno); + if (!pos) { + mem_free(line); + return 0; + } + expanded = 0; + for (text = line; text < end; ) { + unsigned char line_char = *text; + unsigned char next_char, prev_char; + + line_pos = text - line; + prev_char = text > line ? *(text - 1) : '\0'; + next_char = (text + 1 < end) ? *(text + 1) : '\0'; + + /* Do not expand tabs that precede back-spaces; this saves the + * back-space code some trouble. */ + if (line_char == ASCII_TAB && next_char != ASCII_BS) { + int tab_width = 7 - ((line_pos + expanded) & 7); + + expanded += tab_width; + + template->data = ' '; + do + copy_screen_chars(pos++, template, 1); + while (tab_width--); + + *template = saved_renderer_template; + text++; + } else if (line_char == ASCII_BS) { + if (!(expanded + line_pos)) { + /* We've backspaced to the start of the line */ + if (expanded > 0) + expanded--; /* Don't count it */ + continue; + } + + if (pos > startpos) + pos--; /* Backspace */ + + /* Handle x^H_ as _^Hx, but prevent an infinite loop + * swapping two underscores. */ + if (next_char == '_' && prev_char != '_') { + /* x^H_ becomes _^Hx */ + if (text - 1 >= line) + *(text - 1) = next_char; + if (text + 1 < end) + *(text + 1) = prev_char; + + /* Go back and reparse the swapped characters */ + if (text - 2 >= line) + text -= 2; + continue; + } + + if (expanded - 2 >= 0) { + /* Don't count the backspace character or the + * deleted character when returning the line's + * width or when expanding tabs. */ + expanded -= 2; + } + + if (pos->data == '_' && next_char == '_') { + /* Is _^H_ an underlined underscore + * or an emboldened underscore? */ + + if (expanded + line_pos >= 0 + && pos - 1 >= startpos + && (pos - 1)->attr) { + /* There is some preceding text, + * and it has an attribute; copy it */ + template->attr |= (pos - 1)->attr; + } else { + /* Default to bold; seems more useful + * than underlining the underscore */ + template->attr |= SCREEN_ATTR_BOLD; + } + + } else if (pos->data == '_') { + /* Underline _^Hx */ + + template->attr |= SCREEN_ATTR_UNDERLINE; + + } else if (pos->data == next_char) { + /* Embolden x^Hx */ + + template->attr |= SCREEN_ATTR_BOLD; + } + + /* Handle _^Hx^Hx as both bold and underlined */ + if (template->attr) + template->attr |= pos->attr; + text++; + } else { + int added_chars = 0; + + if (document->options.plain_display_links + && isalpha(line_char) && isalpha(next_char)) { + /* We only want to check for a URI if there are + * at least two consecutive alphabetic + * characters, or if we are at the very start of + * the line. It improves performance a bit. + * --Zas */ + added_chars = print_document_link(renderer, + lineno, line, + line_pos, + width, + expanded, + pos); + } + + if (added_chars) { + text += added_chars; + pos += added_chars; + } else { + unicode_val_T data = utf_8_to_unicode(&text, end); + + if (data == UCS_NO_CHAR) text++; + template->data = (uint16_t)data; + copy_screen_chars(pos++, template, 1); + + /* Detect copy of nul chars to screen, this + * should not occur. --Zas */ + assert(line_char); + } + + *template = saved_renderer_template; + } + } +end: mem_free(line); realloc_line(document, pos - startpos, lineno); @@ -559,6 +694,7 @@ render_plain_document(struct cache_entry *cached, struct document *document, document->bgcolor = document->options.default_bg; document->width = 0; + document->options.utf8 = is_cp_special(document->options.cp); /* Setup the style */ init_template(&renderer.template, &document->options); diff --git a/src/intl/charsets.c b/src/intl/charsets.c index d25c558f..ca564d6a 100644 --- a/src/intl/charsets.c +++ b/src/intl/charsets.c @@ -140,6 +140,12 @@ u2cp_(unicode_val_T u, int to, int no_nbsp_hack) int s; if (u < 128) return strings[u]; + + to &= ~SYSTEM_CHARSET_FLAG; + + if (codepages[to].table == table_utf_8) + return encode_utf_8(u); + /* To mark non breaking spaces, we use a special char NBSP_CHAR. */ if (u == 0xa0) return no_nbsp_hack ? " " : NBSP_CHAR_STRING; if (u == 0xad) return ""; @@ -151,7 +157,6 @@ u2cp_(unicode_val_T u, int to, int no_nbsp_hack) return u2cp_(strange, to, no_nbsp_hack); } - to &= ~SYSTEM_CHARSET_FLAG; for (j = 0; codepages[to].table[j].c; j++) if (codepages[to].table[j].u == u) @@ -165,7 +170,7 @@ u2cp_(unicode_val_T u, int to, int no_nbsp_hack) static unsigned char utf_buffer[7]; -static unsigned char * +inline unsigned char * encode_utf_8(unicode_val_T u) { memset(utf_buffer, 0, 7); @@ -200,6 +205,91 @@ encode_utf_8(unicode_val_T u) return utf_buffer; } +inline int +strlen_utf8(unsigned char **str) +{ + unsigned char *s = *str; + unsigned char *end = strchr(s, '\0'); + int x; + int len; + + for (x = 0;; x++, s += len) { + if (*s < 0x80) len = 1; + else if (*s < 0xe0) len = 2; + else if (*s < 0xf0) len = 3; + else if (*s < 0xf8) len = 4; + else if (*s < 0xfc) len = 5; + else len = 6; + if (s + len > end) break; + } + *str = s; + return x; +} + +inline unicode_val_T +utf_8_to_unicode(unsigned char **string, unsigned char *end) +{ + unsigned char *str = *string; + unicode_val_T u; + int length; + + if (str[0] < 0x80) + length = 1; + else if (str[0] < 0xe0) + length = 2; + else if (str[0] < 0xf0) + length = 3; + else if (str[0] < 0xf8) + length = 4; + else if (str[0] < 0xfc) + length = 5; + else + length = 6; + + if (str + length > end) { + return UCS_NO_CHAR; + } + + switch (length) { + case 1: + u = str[0]; + break; + case 2: + u = (str[0] & 0x1f) << 6; + u += (str[1] & 0x3f); + break; + case 3: + u = (str[0] & 0x0f) << 12; + u += ((str[1] & 0x3f) << 6); + u += (str[2] & 0x3f); + break; + case 4: + u = (str[0] & 0x0f) << 18; + u += ((str[1] & 0x3f) << 12); + u += ((str[2] & 0x3f) << 6); + u += (str[3] & 0x3f); + break; + case 5: + u = (str[0] & 0x0f) << 24; + u += ((str[1] & 0x3f) << 18); + u += ((str[2] & 0x3f) << 12); + u += ((str[3] & 0x3f) << 6); + u += (str[4] & 0x3f); + break; + case 6: + default: + u = (str[0] & 0x01) << 30; + u += ((str[1] & 0x3f) << 24); + u += ((str[2] & 0x3f) << 18); + u += ((str[3] & 0x3f) << 12); + u += ((str[4] & 0x3f) << 6); + u += (str[5] & 0x3f); + break; + } + *string = str + length; + return u > 0xffff ? '*' : u; +} + /* This slow and ugly code is used by the terminal utf_8_io */ unsigned char * cp2utf_8(int from, int c) @@ -430,11 +520,16 @@ get_entity_string(const unsigned char *str, const int strlen, int encoding) static struct entity_cache entity_cache[ENTITY_CACHE_MAXLEN][ENTITY_CACHE_SIZE]; static unsigned int nb_entity_cache[ENTITY_CACHE_MAXLEN]; static int first_time = 1; - unsigned int slen; + unsigned int slen = 0; unsigned char *result = NULL; if (strlen <= 0) return NULL; + /* TODO: caching UTF-8 */ + encoding &= ~SYSTEM_CHARSET_FLAG; + if (codepages[encoding].table == table_utf_8) + goto skip; + if (first_time) { memset(&nb_entity_cache, 0, ENTITY_CACHE_MAXLEN * sizeof(unsigned int)); first_time = 0; @@ -488,7 +583,7 @@ get_entity_string(const unsigned char *str, const int strlen, int encoding) fprintf(stderr, "miss\n"); #endif } - +skip: if (*str == '#') { /* Numeric entity. */ int l = (int) strlen; unsigned char *st = (unsigned char *) str; @@ -540,6 +635,9 @@ get_entity_string(const unsigned char *str, const int strlen, int encoding) if (element) result = u2cp(element->c, encoding); } + if (codepages[encoding].table == table_utf_8) { + return result; + } end: /* Take care of potential buffer overflow. */ if (strlen < sizeof(entity_cache[slen][0].str)) { diff --git a/src/intl/charsets.h b/src/intl/charsets.h index b99d3e67..364c484d 100644 --- a/src/intl/charsets.h +++ b/src/intl/charsets.h @@ -53,6 +53,10 @@ unsigned char *get_cp_name(int); unsigned char *get_cp_mime_name(int); int is_cp_special(int); void free_conv_table(void); +inline unsigned char *encode_utf_8(unicode_val_T); +inline int strlen_utf8(unsigned char **); +inline unicode_val_T utf_8_to_unicode(unsigned char **, unsigned char *); + unsigned char *cp2utf_8(int, int); unsigned char *u2cp_(unicode_val_T, int, int no_nbsp_hack); diff --git a/src/terminal/draw.c b/src/terminal/draw.c index b691f23b..b44b8b9c 100644 --- a/src/terminal/draw.c +++ b/src/terminal/draw.c @@ -7,6 +7,7 @@ #include "elinks.h" #include "config/options.h" +#include "intl/charsets.h" #include "terminal/color.h" #include "terminal/draw.h" #include "terminal/screen.h" @@ -102,7 +103,7 @@ draw_char_color(struct terminal *term, int x, int y, struct color_pair *color) } void -draw_char_data(struct terminal *term, int x, int y, unsigned char data) +draw_char_data(struct terminal *term, int x, int y, uint16_t data) { struct screen_char *screen_char = get_char(term, x, y); @@ -200,7 +201,7 @@ draw_border(struct terminal *term, struct box *box, void draw_char(struct terminal *term, int x, int y, - unsigned char data, enum screen_char_attr attr, + uint16_t data, enum screen_char_attr attr, struct color_pair *color) { struct screen_char *screen_char = get_char(term, x, y); @@ -277,6 +278,41 @@ draw_shadow(struct terminal *term, struct box *box, draw_box(term, &dbox, ' ', 0, color); } +static void +draw_text_utf8(struct terminal *term, int x, int y, + unsigned char *text, int length, + enum screen_char_attr attr, struct color_pair *color) +{ + struct screen_char *start, *pos; + unsigned char *end = text + length; + unicode_val_T data; + + assert(text && length >= 0); + if_assert_failed return; + + if (length <= 0) return; + if (x >= term->width) return; + + data = utf_8_to_unicode(&text, end); + if (data == UCS_NO_CHAR) return; + start = get_char(term, x++, y); + start->data = (uint16_t)data; + if (color) { + start->attr = attr; + set_term_color(start, color, 0, + get_opt_int_tree(term->spec, "colors")); + } + + for (pos = start + 1; x < term->width; x++, pos++) { + data = utf_8_to_unicode(&text, end); + if (data == UCS_NO_CHAR) break; + if (color) copy_screen_chars(pos, start, 1); + pos->data = (uint16_t)data; + } + set_screen_dirty(term->screen, y, y); + +} + void draw_text(struct terminal *term, int x, int y, unsigned char *text, int length, @@ -288,6 +324,11 @@ draw_text(struct terminal *term, int x, int y, assert(text && length >= 0); if_assert_failed return; + if (term->utf8) { + draw_text_utf8(term, x, y, text, length, attr, color); + return; + } + if (length <= 0) return; pos = get_char(term, x, y); if (!pos) return; diff --git a/src/terminal/draw.h b/src/terminal/draw.h index fd9a6812..8b1e6f63 100644 --- a/src/terminal/draw.h +++ b/src/terminal/draw.h @@ -19,7 +19,7 @@ enum screen_char_attr { /* One position in the terminal screen's image. */ struct screen_char { /* Contains either character value or frame data. */ - unsigned char data; + uint16_t data; /* Attributes are screen_char_attr bits. */ unsigned char attr; @@ -202,7 +202,7 @@ void draw_char_color(struct terminal *term, int x, int y, struct color_pair *color); /* Sets the data of a screen position. */ -void draw_char_data(struct terminal *term, int x, int y, unsigned char data); +void draw_char_data(struct terminal *term, int x, int y, uint16_t data); /* Sets the data to @border and of a screen position. */ void draw_border_char(struct terminal *term, int x, int y, @@ -214,7 +214,7 @@ void draw_border_cross(struct terminal *, int x, int y, /* Draws a char. */ void draw_char(struct terminal *term, int x, int y, - unsigned char data, enum screen_char_attr attr, + uint16_t data, enum screen_char_attr attr, struct color_pair *color); /* Draws area defined by @box using the same colors and attributes. */ diff --git a/src/terminal/event.c b/src/terminal/event.c index 37e86b37..29c61d33 100644 --- a/src/terminal/event.c +++ b/src/terminal/event.c @@ -245,7 +245,6 @@ handle_interlink_event(struct terminal *term, struct term_event *ev) case EVENT_KBD: { - int utf8_io = -1; int key = get_kbd_key(ev); reset_timer(); @@ -260,9 +259,7 @@ handle_interlink_event(struct terminal *term, struct term_event *ev) } if (interlink->utf_8.len) { - utf8_io = get_opt_bool_tree(term->spec, "utf_8_io"); - - if ((key & 0xC0) == 0x80 && utf8_io) { + if ((key & 0xC0) == 0x80 && term->utf8) { interlink->utf_8.ucs <<= 6; interlink->utf_8.ucs |= key & 0x3F; if (! --interlink->utf_8.len) { @@ -280,15 +277,14 @@ handle_interlink_event(struct terminal *term, struct term_event *ev) } } - if (key < 0x80 || key > 0xFF - || (utf8_io == -1 - ? !get_opt_bool_tree(term->spec, "utf_8_io") - : !utf8_io)) { + if (key < 0x80 || key > 0xFF || !term->utf8) { term_send_event(term, ev); break; - } else if ((key & 0xC0) == 0xC0 && (key & 0xFE) != 0xFE) { + } + + else if ((key & 0xC0) == 0xC0 && (key & 0xFE) != 0xFE) { unsigned int mask, cov = 0x80; int len = 0; diff --git a/src/terminal/screen.c b/src/terminal/screen.c index 1d44f595..a4ac73ba 100644 --- a/src/terminal/screen.c +++ b/src/terminal/screen.c @@ -30,6 +30,7 @@ unsigned char frame_dumb[48] = " ||||++||++++++--|-+||++--|-+----++++++++ "; static unsigned char frame_vt100[48] = "aaaxuuukkuxkjjjkmvwtqnttmlvwtqnvvwwmmllnnjla "; +#if 0 /* For UTF8 I/O */ static unsigned char frame_vt100_u[48] = { 177, 177, 177, 179, 180, 180, 180, 191, @@ -39,6 +40,7 @@ static unsigned char frame_vt100_u[48] = { 193, 194, 194, 192, 192, 218, 218, 197, 197, 217, 218, 177, 32, 32, 32, 32 }; +#endif static unsigned char frame_freebsd[48] = { 130, 138, 128, 153, 150, 150, 150, 140, @@ -78,6 +80,11 @@ static struct string m11_hack_frame_seqs[] = { /* begin border: */ TERM_STRING("\033[11m"), }; +static struct string utf8_linux_frame_seqs[] = { + /* end border: */ TERM_STRING("\033[10m\033%G"), + /* begin border: */ TERM_STRING("\033%@\033[11m"), +}; + static struct string vt100_frame_seqs[] = { /* end border: */ TERM_STRING("\x0f"), /* begin border: */ TERM_STRING("\x0e"), @@ -99,11 +106,6 @@ struct screen_driver { * uniquely identify the screen_driver. */ enum term_mode_type type; - /* Charsets when doing UTF8 I/O. */ - /* [0] is the common charset and [1] is the frame charset. - * Test wether to use UTF8 I/O using the use_utf8_io() macro. */ - int charsets[2]; - /* The frame translation table. May be NULL. */ unsigned char *frame; @@ -119,6 +121,9 @@ struct screen_driver { /* These are directly derived from the terminal options. */ unsigned int transparent:1; + /* UTF-8 I/O */ + unsigned int utf8:1; + /* The terminal._template_ name. */ unsigned char name[1]; /* XXX: Keep last! */ }; @@ -126,56 +131,56 @@ struct screen_driver { static struct screen_driver dumb_screen_driver = { NULL_LIST_HEAD, /* type: */ TERM_DUMB, - /* charsets: */ { -1, -1 }, /* No UTF8 I/O */ /* frame: */ frame_dumb, /* frame_seqs: */ NULL, /* underline: */ underline_seqs, /* color_mode: */ COLOR_MODE_16, /* transparent: */ 1, + /* utf-8: */ 0, }; static struct screen_driver vt100_screen_driver = { NULL_LIST_HEAD, /* type: */ TERM_VT100, - /* charsets: */ { -1, -1 }, /* No UTF8 I/O */ /* frame: */ frame_vt100, /* frame_seqs: */ vt100_frame_seqs, /* underline: */ underline_seqs, /* color_mode: */ COLOR_MODE_16, /* transparent: */ 1, + /* utf-8: */ 0, }; static struct screen_driver linux_screen_driver = { NULL_LIST_HEAD, /* type: */ TERM_LINUX, - /* charsets: */ { -1, -1 }, /* No UTF8 I/O */ /* frame: */ NULL, /* No restrict_852 */ /* frame_seqs: */ NULL, /* No m11_hack */ /* underline: */ underline_seqs, /* color_mode: */ COLOR_MODE_16, /* transparent: */ 1, + /* utf-8: */ 0, }; static struct screen_driver koi8_screen_driver = { NULL_LIST_HEAD, /* type: */ TERM_KOI8, - /* charsets: */ { -1, -1 }, /* No UTF8 I/O */ /* frame: */ frame_koi, /* frame_seqs: */ NULL, /* underline: */ underline_seqs, /* color_mode: */ COLOR_MODE_16, /* transparent: */ 1, + /* utf-8: */ 0, }; static struct screen_driver freebsd_screen_driver = { NULL_LIST_HEAD, /* type: */ TERM_FREEBSD, - /* charsets: */ { -1, -1 }, /* No UTF8 I/O */ /* frame: */ frame_freebsd, /* frame_seqs: */ NULL, /* No m11_hack */ /* underline: */ underline_seqs, /* color_mode: */ COLOR_MODE_16, /* transparent: */ 1, + /* utf-8: */ 0, }; /* XXX: Keep in sync with enum term_mode_type. */ @@ -187,13 +192,14 @@ static struct screen_driver *screen_drivers[] = { /* TERM_FREEBSD: */ &freebsd_screen_driver, }; +#define use_utf8_io(driver) ((driver)->utf8) static INIT_LIST_HEAD(active_screen_drivers); static void update_screen_driver(struct screen_driver *driver, struct option *term_spec) { - int utf8_io = get_opt_bool_tree(term_spec, "utf_8_io"); + driver->utf8 = get_opt_bool_tree(term_spec, "utf_8_io"); driver->color_mode = get_opt_int_tree(term_spec, "colors"); driver->transparent = get_opt_bool_tree(term_spec, "transparency"); @@ -204,44 +210,22 @@ update_screen_driver(struct screen_driver *driver, struct option *term_spec) driver->underline = NULL; } - if (utf8_io) { - driver->charsets[0] = get_opt_codepage_tree(term_spec, "charset"); - if (driver->type == TERM_LINUX) { - if (get_opt_bool_tree(term_spec, "restrict_852")) - driver->frame = frame_restrict; + if (driver->type == TERM_LINUX) { + if (get_opt_bool_tree(term_spec, "restrict_852")) + driver->frame = frame_restrict; - driver->charsets[1] = get_cp_index("cp437"); + if (get_opt_bool_tree(term_spec, "m11_hack")) + driver->frame_seqs = m11_hack_frame_seqs; - } else if (driver->type == TERM_FREEBSD) { - driver->charsets[1] = get_cp_index("cp437"); + if (driver->utf8) + driver->frame_seqs = utf8_linux_frame_seqs; - } else if (driver->type == TERM_VT100) { - driver->frame = frame_vt100_u; - driver->charsets[1] = get_cp_index("cp437"); + } else if (driver->type == TERM_FREEBSD) { + if (get_opt_bool_tree(term_spec, "m11_hack")) + driver->frame_seqs = m11_hack_frame_seqs; - } else if (driver->type == TERM_KOI8) { - driver->charsets[1] = get_cp_index("koi8-r"); - - } else { - driver->charsets[1] = driver->charsets[0]; - } - - } else { - driver->charsets[0] = -1; - if (driver->type == TERM_LINUX) { - if (get_opt_bool_tree(term_spec, "restrict_852")) - driver->frame = frame_restrict; - - if (get_opt_bool_tree(term_spec, "m11_hack")) - driver->frame_seqs = m11_hack_frame_seqs; - - } else if (driver->type == TERM_FREEBSD) { - if (get_opt_bool_tree(term_spec, "m11_hack")) - driver->frame_seqs = m11_hack_frame_seqs; - - } else if (driver->type == TERM_VT100) { - driver->frame = frame_vt100; - } + } else if (driver->type == TERM_VT100) { + driver->frame = frame_vt100; } } @@ -281,6 +265,8 @@ add_screen_driver(enum term_mode_type type, struct terminal *term, int env_len) term->spec->change_hook = screen_driver_change_hook; + term->utf8 = use_utf8_io(driver); + return driver; } @@ -299,6 +285,7 @@ get_screen_driver(struct terminal *term) /* Some simple probably useless MRU ;) */ move_to_top_of_list(active_screen_drivers, driver); + term->utf8 = use_utf8_io(driver); return driver; } @@ -364,11 +351,10 @@ struct screen_state { #define compare_bg_color(a, b) (TERM_COLOR_BACKGROUND(a) == TERM_COLOR_BACKGROUND(b)) #define compare_fg_color(a, b) (TERM_COLOR_FOREGROUND(a) == TERM_COLOR_FOREGROUND(b)) -#define use_utf8_io(driver) ((driver)->charsets[0] != -1) static inline void add_char_data(struct string *screen, struct screen_driver *driver, - unsigned char data, unsigned char border) + unicode_val_T data, unsigned char border) { if (!isscreensafe(data)) { add_char_to_string(screen, ' '); @@ -379,13 +365,15 @@ add_char_data(struct string *screen, struct screen_driver *driver, data = driver->frame[data - 176]; if (use_utf8_io(driver)) { - int charset = driver->charsets[!!border]; - - add_to_string(screen, cp2utf_8(charset, data)); + if (border) + add_char_to_string(screen, (unsigned char)data); + else + if (data != UCS_NO_CHAR) + add_to_string(screen, encode_utf_8(data)); return; } - add_char_to_string(screen, data); + add_char_to_string(screen, (unsigned char)data); } /* Time critical section. */ diff --git a/src/terminal/terminal.h b/src/terminal/terminal.h index 0689f098..685afb0a 100644 --- a/src/terminal/terminal.h +++ b/src/terminal/terminal.h @@ -110,6 +110,9 @@ struct terminal { * work and even maintaining these structures ;-). */ unsigned int master:1; + /* Indicates whether UTF-8 I/O is used */ + unsigned int utf8:1; + /* The current tab number. */ int current_tab; diff --git a/src/viewer/dump/dump.c b/src/viewer/dump/dump.c index dc12ca5c..6a50fa04 100644 --- a/src/viewer/dump/dump.c +++ b/src/viewer/dump/dump.c @@ -26,6 +26,7 @@ #include "document/options.h" #include "document/renderer.h" #include "document/view.h" +#include "intl/charsets.h" #include "intl/gettext/libintl.h" #include "main/select.h" #include "main/main.h" @@ -322,6 +323,9 @@ add_document_to_string(struct string *string, struct document *document) assert(string && document); if_assert_failed return NULL; + if (is_cp_special(document->options.cp)) + goto utf_8; + for (y = 0; y < document->height; y++) { int white = 0; int x; @@ -354,7 +358,43 @@ add_document_to_string(struct string *string, struct document *document) add_char_to_string(string, '\n'); } + goto end; +utf_8: + for (y = 0; y < document->height; y++) { + struct screen_char *pos = document->data[y].chars; + int white = 0; + int x; + for (x = 0; x < document->data[y].length; x++) { + uint16_t data = pos->data; + unsigned int frame = (pos->attr & SCREEN_ATTR_FRAME); + + if (!isscreensafe(data)) { + white++; + continue; + } else if (frame && data >= 176 && data < 224) { + data = frame_dumb[data - 176]; + + if (data <= ' ') { + /* Count spaces. */ + white++; + } else { + /* Print spaces if any. */ + if (white) { + add_xchar_to_string(string, ' ', white); + white = 0; + } + if (frame) + add_char_to_string(string, data); + else + add_to_string(string, encode_utf_8(data)); + } + } + } + + add_char_to_string(string, '\n'); + } +end: return string; } @@ -382,6 +422,9 @@ dump_to_file(struct document *document, int fd) if (!buf) return -1; + if (is_cp_special(document->options.cp)) + goto utf_8; + for (y = 0; y < document->height; y++) { int white = 0; int x; @@ -418,13 +461,60 @@ dump_to_file(struct document *document, int fd) if (write_char('\n', fd, buf, &bptr)) goto fail; } + goto ref; +utf_8: + for (y = 0; y < document->height; y++) { + int white = 0; + int x; + + for (x = 0; x < document->data[y].length; x++) { + uint16_t c; + unsigned char attr = document->data[y].chars[x].attr; + + c = document->data[y].chars[x].data; + + if ((attr & SCREEN_ATTR_FRAME) + && c >= 176 && c < 224) + c = frame_dumb[c - 176]; + else { + unsigned char *utf8_buf = encode_utf_8(c); + + while (*utf8_buf) { + if (write_char(*utf8_buf++, + fd, buf, &bptr)) goto fail; + } + continue; + } + + if (c <= ' ') { + /* Count spaces. */ + white++; + continue; + } + + /* Print spaces if any. */ + while (white) { + if (write_char(' ', fd, buf, &bptr)) + goto fail; + white--; + } + + /* Print normal char. */ + if (write_char(c, fd, buf, &bptr)) + goto fail; + } + + /* Print end of line. */ + if (write_char('\n', fd, buf, &bptr)) + goto fail; + } if (hard_write(fd, buf, bptr) != bptr) { fail: mem_free(buf); return -1; } - +ref: if (document->nlinks && get_opt_bool("document.dump.references")) { int x; unsigned char *header = "\nReferences\n\n Visible links\n"; diff --git a/src/viewer/text/form.c b/src/viewer/text/form.c index 90b809f1..3b3f499e 100644 --- a/src/viewer/text/form.c +++ b/src/viewer/text/form.c @@ -158,16 +158,22 @@ init_form_state(struct form_control *fc, struct form_state *fs) mem_free_set(&fs->value, NULL); switch (fc->type) { + unsigned char *text; + case FC_TEXT: case FC_PASSWORD: case FC_TEXTAREA: fs->value = stracpy(fc->default_value); fs->state = strlen(fc->default_value); + text = fs->value; + if (fc->type != FC_TEXTAREA) + fs->utf8_pos = strlen_utf8(&text); fs->vpos = 0; break; case FC_FILE: fs->value = stracpy(""); fs->state = 0; + fs->utf8_pos = 0; fs->vpos = 0; break; case FC_SELECT: @@ -330,12 +336,14 @@ draw_form_entry(struct terminal *term, struct document_view *doc_view, dy = box->y - vs->y; switch (fc->type) { unsigned char *s; + unsigned char *text, *end; int len; int i, x, y; case FC_TEXT: case FC_PASSWORD: case FC_FILE: + if (term->utf8) goto utf_8; int_bounds(&fs->vpos, fs->state - fc->size + 1, fs->state); if (!link->npoints) break; @@ -360,6 +368,36 @@ draw_form_entry(struct terminal *term, struct document_view *doc_view, draw_char_data(term, x, y, data); } break; +utf_8: + text = fs->value; + end = strchr(text, '\0'); + int_bounds(&fs->vpos, fs->utf8_pos - fc->size + 1, fs->utf8_pos); + if (!link->npoints) break; + + y = link->points[0].y + dy; + if (!row_is_in_box(box, y)) + break; + for (i = 0; i < fs->vpos; i++) { + utf_8_to_unicode(&text, end); + } + s = text; + len = strlen_utf8(&s); + x = link->points[0].x + dx; + + for (i = 0; i < fc->size; i++, x++) { + uint16_t data; + + if (!col_is_in_box(box, x)) continue; + + if (fs->value && i >= -fs->vpos && i < len) + data = fc->type != FC_PASSWORD + ? utf_8_to_unicode(&text, end) : '*'; + else + data = '_'; + + draw_char_data(term, x, y, data); + } + break; case FC_TEXTAREA: draw_textarea(term, fs, doc_view, link); break; @@ -378,6 +416,7 @@ draw_form_entry(struct terminal *term, struct document_view *doc_view, else /* XXX: when can this happen? --pasky */ s = ""; + if (term->utf8) goto utf_8_select; len = s ? strlen(s) : 0; for (i = 0; i < link->npoints; i++) { x = link->points[i].x + dx; @@ -386,6 +425,18 @@ draw_form_entry(struct terminal *term, struct document_view *doc_view, draw_char_data(term, x, y, i < len ? s[i] : '_'); } break; +utf_8_select: + text = s; + end = strchr(s, '\0'); + len = strlen_utf8(&text); + for (i = 0; i < link->npoints; i++) { + x = link->points[i].x + dx; + y = link->points[i].y + dy; + if (is_in_box(box, x, y)) + draw_char_data(term, x, y, i < len + ? utf_8_to_unicode(&s, end) : '_'); + } + break; case FC_SUBMIT: case FC_IMAGE: case FC_RESET: @@ -1195,6 +1246,7 @@ field_op(struct session *ses, struct document_view *doc_view, unsigned char *text; int length; enum frame_event_status status = FRAME_EVENT_REFRESH; + int utf8 = ses->tab->term->utf8; assert(ses && doc_view && link && ev); if_assert_failed return FRAME_EVENT_OK; @@ -1214,49 +1266,79 @@ field_op(struct session *ses, struct document_view *doc_view, switch (action_id) { case ACT_EDIT_LEFT: - fs->state = int_max(fs->state - 1, 0); + if (utf8) { + unsigned char *text = fs->value; + unsigned char *end = fs->value + fs->state - 1; + int old = fs->state; + + while (utf_8_to_unicode(&text, end) != UCS_NO_CHAR); + fs->state = (int)(text - fs->value); + if (old != fs->state) fs->utf8_pos--; + } else + fs->state = int_max(fs->state - 1, 0); break; case ACT_EDIT_RIGHT: - fs->state = int_min(fs->state + 1, strlen(fs->value)); + if (utf8) { + unsigned char *text = fs->value + fs->state; + unsigned char *end = strchr(text, '\0'); + int old = fs->state; + + utf_8_to_unicode(&text, end); + fs->state = (int)(text - fs->value); + if (old != fs->state) fs->utf8_pos++; + } else + fs->state = int_min(fs->state + 1, strlen(fs->value)); break; case ACT_EDIT_HOME: if (fc->type == FC_TEXTAREA) { - status = textarea_op_home(fs, fc); + status = textarea_op_home(fs, fc, utf8); } else { fs->state = 0; + fs->utf8_pos = 0; } break; case ACT_EDIT_UP: if (fc->type != FC_TEXTAREA) status = FRAME_EVENT_IGNORED; else - status = textarea_op_up(fs, fc); + status = textarea_op_up(fs, fc, utf8); break; case ACT_EDIT_DOWN: if (fc->type != FC_TEXTAREA) status = FRAME_EVENT_IGNORED; else - status = textarea_op_down(fs, fc); + status = textarea_op_down(fs, fc, utf8); break; case ACT_EDIT_END: if (fc->type == FC_TEXTAREA) { - status = textarea_op_end(fs, fc); + status = textarea_op_end(fs, fc, utf8); } else { fs->state = strlen(fs->value); + if (utf8) { + unsigned char *text = fs->value; + + fs->utf8_pos = strlen_utf8(&text); + } } break; case ACT_EDIT_BEGINNING_OF_BUFFER: if (fc->type == FC_TEXTAREA) { - status = textarea_op_bob(fs, fc); + status = textarea_op_bob(fs, fc, utf8); } else { fs->state = 0; + fs->utf8_pos = 0; } break; case ACT_EDIT_END_OF_BUFFER: if (fc->type == FC_TEXTAREA) { - status = textarea_op_eob(fs, fc); + status = textarea_op_eob(fs, fc, utf8); } else { fs->state = strlen(fs->value); + if (utf8) { + unsigned char *text = fs->value; + + fs->utf8_pos = strlen_utf8(&text); + } } break; case ACT_EDIT_OPEN_EXTERNAL: @@ -1274,6 +1356,7 @@ field_op(struct session *ses, struct document_view *doc_view, if (!form_field_is_readonly(fc)) fs->value[0] = 0; fs->state = 0; + fs->utf8_pos = 0; break; case ACT_EDIT_PASTE_CLIPBOARD: if (form_field_is_readonly(fc)) break; @@ -1289,13 +1372,19 @@ field_op(struct session *ses, struct document_view *doc_view, fs->value = v; memmove(v, text, length + 1); fs->state = strlen(fs->value); + if (utf8 && fc->type != FC_TEXTAREA) { + unsigned char *text = fs->value; + + fs->utf8_pos = strlen_utf8(&text); + } + } } mem_free(text); break; case ACT_EDIT_ENTER: if (fc->type == FC_TEXTAREA) { - status = textarea_op_enter(fs, fc); + status = textarea_op_enter(fs, fc, utf8); break; } @@ -1320,7 +1409,19 @@ field_op(struct session *ses, struct document_view *doc_view, status = FRAME_EVENT_OK; break; } + if (utf8) { + int i; + unsigned char *text = fs->value; + unsigned char *end = fs->value + fs->state; + for (i = 0; i < fs->utf8_pos - 1; i++) + utf_8_to_unicode(&text, end); + length = strlen(end) + 1; + memmove(text, end, length); + fs->state = (int)(text - fs->value); + fs->utf8_pos--; + break; + } length = strlen(fs->value + fs->state) + 1; text = fs->value + fs->state; @@ -1338,7 +1439,18 @@ field_op(struct session *ses, struct document_view *doc_view, status = FRAME_EVENT_OK; break; } + if (utf8) { + unsigned char *end = fs->value + length; + unsigned char *text = fs->value + fs->state; + unsigned char *old = text; + utf_8_to_unicode(&text, end); + if (old != text) { + memmove(old, text, + (int)(end - text) + 1); + } + break; + } text = fs->value + fs->state; memmove(text, text + 1, length - fs->state); @@ -1368,6 +1480,11 @@ field_op(struct session *ses, struct document_view *doc_view, memmove(text, fs->value + fs->state, length); fs->state = (int) (text - fs->value); + if (utf8 && fc->type != FC_TEXTAREA) { + unsigned char *text = fs->value; + + fs->utf8_pos = strlen_utf8(&text); + } break; case ACT_EDIT_KILL_TO_EOL: if (form_field_is_readonly(fc)) { @@ -1421,13 +1538,41 @@ field_op(struct session *ses, struct document_view *doc_view, } if (form_field_is_readonly(fc) - || strlen(fs->value) >= fc->maxlength - || !insert_in_string(&fs->value, fs->state, "?", 1)) { + || strlen(fs->value) >= fc->maxlength) { status = FRAME_EVENT_OK; break; } + if (utf8) { + static unsigned char buf[7]; + static int i = 0; + unicode_val_T data; + unsigned char *t; - fs->value[fs->state++] = get_kbd_key(ev); + t = buf; + buf[i++] = get_kbd_key(ev); + buf[i] = 0; + data = utf_8_to_unicode(&t, buf + i); + if (data != UCS_NO_CHAR) { + if (!insert_in_string(&fs->value, fs->state, buf, i)) { + i = 0; + return FRAME_EVENT_OK; + } + fs->state += i; + fs->utf8_pos++; + i = 0; + break; + } + if (i == 6) { + i = 0; + return FRAME_EVENT_OK; + } else { + return FRAME_EVENT_OK; + } + } else { + if (!insert_in_string(&fs->value, fs->state, "?", 1)) + return FRAME_EVENT_OK; + fs->value[fs->state++] = get_kbd_key(ev); + } break; } diff --git a/src/viewer/text/form.h b/src/viewer/text/form.h index 1bf47982..fd034cfb 100644 --- a/src/viewer/text/form.h +++ b/src/viewer/text/form.h @@ -39,6 +39,7 @@ struct form_state { unsigned char *value; int state; + int utf8_pos; int vpos; int vypos; diff --git a/src/viewer/text/link.c b/src/viewer/text/link.c index d9a0617d..2fec2f42 100644 --- a/src/viewer/text/link.c +++ b/src/viewer/text/link.c @@ -112,6 +112,7 @@ get_link_cursor_offset(struct document_view *doc_view, struct link *link) { struct form_control *fc; struct form_state *fs; + int utf8 = doc_view->document->options.utf8; switch (link->type) { case LINK_CHECKBOX: @@ -123,12 +124,15 @@ get_link_cursor_offset(struct document_view *doc_view, struct link *link) case LINK_FIELD: fc = get_link_form_control(link); fs = find_form_state(doc_view, fc); - return fs ? fs->state - fs->vpos : 0; + if (utf8) { + return fs ? fs->utf8_pos - fs->vpos : 0; + } else + return fs ? fs->state - fs->vpos : 0; case LINK_AREA: fc = get_link_form_control(link); fs = find_form_state(doc_view, fc); - return fs ? area_cursor(fc, fs) : 0; + return fs ? area_cursor(fc, fs, utf8) : 0; case LINK_HYPERTEXT: case LINK_MAP: diff --git a/src/viewer/text/textarea.c b/src/viewer/text/textarea.c index 6d5b512e..e4ecdc9c 100644 --- a/src/viewer/text/textarea.c +++ b/src/viewer/text/textarea.c @@ -138,7 +138,7 @@ get_textarea_line_number(struct line_info *line, int cursor_position) /* Fixes up the vpos and vypos members of the form_state. Returns the * logical position in the textarea view. */ int -area_cursor(struct form_control *fc, struct form_state *fs) +area_cursor(struct form_control *fc, struct form_state *fs, int utf8) { struct line_info *line; int x, y; @@ -155,7 +155,15 @@ area_cursor(struct form_control *fc, struct form_state *fs) return 0; } - x = fs->state - line[y].start; + if (utf8) { + unsigned char *text = fs->value + line[y].start; + unsigned char tmp = fs->value[fs->state]; + + fs->value[fs->state] = '\0'; + x = strlen_utf8(&text); + fs->value[fs->state] = tmp; + } else + x = fs->state - line[y].start; mem_free(line); @@ -170,8 +178,8 @@ area_cursor(struct form_control *fc, struct form_state *fs) return y * fc->cols + x; } -void -draw_textarea(struct terminal *term, struct form_state *fs, +static void +draw_textarea_utf8(struct terminal *term, struct form_state *fs, struct document_view *doc_view, struct link *link) { struct line_info *line, *linex; @@ -192,7 +200,91 @@ draw_textarea(struct terminal *term, struct form_state *fs, vy = doc_view->vs->y; if (!link->npoints) return; - area_cursor(fc, fs); + area_cursor(fc, fs, 1); + linex = format_text(fs->value, fc->cols, fc->wrap, 0); + if (!linex) return; + line = linex; + sl = fs->vypos; + while (line->start != -1 && sl) sl--, line++; + + x = link->points[0].x + box->x - vx; + y = link->points[0].y + box->y - vy; + ye = y + fc->rows; + + for (; line->start != -1 && y < ye; line++, y++) { + int i; + unsigned char *text, *end; + + text = fs->value + line->start; + end = fs->value + line->end; + + for (i = 0; i < fs->vpos; i++) + utf_8_to_unicode(&text, end); + + if (!row_is_in_box(box, y)) continue; + + for (i = 0; i < fc->cols; i++) { + uint16_t data; + int xi = x + i; + + if (!col_is_in_box(box, xi)) + continue; + + if (i >= -fs->vpos + && text < end) + data = utf_8_to_unicode(&text, end); + else + data = '_'; + + draw_char_data(term, xi, y, data); + } + } + + for (; y < ye; y++) { + int i; + + if (!row_is_in_box(box, y)) continue; + + for (i = 0; i < fc->cols; i++) { + int xi = x + i; + + if (col_is_in_box(box, xi)) + draw_char_data(term, xi, y, '_'); + } + } + + mem_free(linex); +} + + +void +draw_textarea(struct terminal *term, struct form_state *fs, + struct document_view *doc_view, struct link *link) +{ + struct line_info *line, *linex; + struct form_control *fc; + struct box *box; + int vx, vy; + int sl, ye; + int x, y; + + assert(term && doc_view && doc_view->document && doc_view->vs && link); + if_assert_failed return; + + if (term->utf8) { + draw_textarea_utf8(term, fs, doc_view, link); + return; + } + fc = get_link_form_control(link); + assertm(fc, "link %d has no form control", (int) (link - doc_view->document->links)); + if_assert_failed return; + + box = &doc_view->box; + vx = doc_view->vs->x; + vy = doc_view->vs->y; + + if (!link->npoints) return; + area_cursor(fc, fs, 0); linex = format_text(fs->value, fc->cols, fc->wrap, 0); if (!linex) return; line = linex; @@ -448,8 +540,8 @@ menu_textarea_edit(struct terminal *term, void *xxx, void *ses_) } static enum frame_event_status -textarea_op(struct form_state *fs, struct form_control *fc, - int (*do_op)(struct form_state *, struct line_info *, int)) +textarea_op(struct form_state *fs, struct form_control *fc, int utf8, + int (*do_op)(struct form_state *, struct line_info *, int, int)) { struct line_info *line; int current, state; @@ -462,47 +554,87 @@ textarea_op(struct form_state *fs, struct form_control *fc, current = get_textarea_line_number(line, fs->state); state = fs->state; - if (do_op(fs, line, current)) { + if (do_op(fs, line, current, utf8)) { mem_free(line); return FRAME_EVENT_IGNORED; } mem_free(line); - return fs->state == state ? FRAME_EVENT_OK : FRAME_EVENT_REFRESH; } static int -do_op_home(struct form_state *fs, struct line_info *line, int current) +x_pos(struct form_state *fs, struct line_info *line, int current) +{ + unsigned char *text = fs->value + line[current].start; + unsigned char tmp = fs->value[fs->state]; + int len; + + fs->value[fs->state] = '\0'; + len = strlen_utf8(&text); + fs->value[fs->state] = tmp; + return len; +} + +static void +new_pos(struct form_state *fs, struct line_info *line, int current, int len) +{ + unsigned char *text = fs->value + line[current].start; + unsigned char *end = fs->value + line[current].end; + int i; + + for (i = 0; i < len; i++) { + unicode_val_T data = utf_8_to_unicode(&text, end); + + if (data == UCS_NO_CHAR) break; + } + fs->state = (int)(text - fs->value); +} + +static int +do_op_home(struct form_state *fs, struct line_info *line, int current, int utf8) { if (current != -1) fs->state = line[current].start; return 0; } static int -do_op_up(struct form_state *fs, struct line_info *line, int current) +do_op_up(struct form_state *fs, struct line_info *line, int current, int utf8) { if (current == -1) return 0; if (!current) return 1; + if (utf8) { + int len = x_pos(fs, line, current); + + new_pos(fs, line, current - 1, len); + return 0; + } + fs->state -= line[current].start - line[current-1].start; int_upper_bound(&fs->state, line[current-1].end); return 0; } static int -do_op_down(struct form_state *fs, struct line_info *line, int current) +do_op_down(struct form_state *fs, struct line_info *line, int current, int utf8) { if (current == -1) return 0; if (line[current+1].start == -1) return 1; + if (utf8) { + int len = x_pos(fs, line, current); + + new_pos(fs, line, current + 1, len); + return 0; + } fs->state += line[current+1].start - line[current].start; int_upper_bound(&fs->state, line[current+1].end); return 0; } static int -do_op_end(struct form_state *fs, struct line_info *line, int current) +do_op_end(struct form_state *fs, struct line_info *line, int current, int utf8) { if (current == -1) { fs->state = strlen(fs->value); @@ -517,7 +649,7 @@ do_op_end(struct form_state *fs, struct line_info *line, int current) } static int -do_op_bob(struct form_state *fs, struct line_info *line, int current) +do_op_bob(struct form_state *fs, struct line_info *line, int current, int utf8) { if (current == -1) return 0; @@ -527,7 +659,7 @@ do_op_bob(struct form_state *fs, struct line_info *line, int current) } static int -do_op_eob(struct form_state *fs, struct line_info *line, int current) +do_op_eob(struct form_state *fs, struct line_info *line, int current, int utf8) { if (current == -1) { fs->state = strlen(fs->value); @@ -544,35 +676,35 @@ do_op_eob(struct form_state *fs, struct line_info *line, int current) } enum frame_event_status -textarea_op_home(struct form_state *fs, struct form_control *fc) +textarea_op_home(struct form_state *fs, struct form_control *fc, int utf8) { - return textarea_op(fs, fc, do_op_home); + return textarea_op(fs, fc, utf8, do_op_home); } enum frame_event_status -textarea_op_up(struct form_state *fs, struct form_control *fc) +textarea_op_up(struct form_state *fs, struct form_control *fc, int utf8) { - return textarea_op(fs, fc, do_op_up); + return textarea_op(fs, fc, utf8, do_op_up); } enum frame_event_status -textarea_op_down(struct form_state *fs, struct form_control *fc) +textarea_op_down(struct form_state *fs, struct form_control *fc, int utf8) { - return textarea_op(fs, fc, do_op_down); + return textarea_op(fs, fc, utf8, do_op_down); } enum frame_event_status -textarea_op_end(struct form_state *fs, struct form_control *fc) +textarea_op_end(struct form_state *fs, struct form_control *fc, int utf8) { - return textarea_op(fs, fc, do_op_end); + return textarea_op(fs, fc, utf8, do_op_end); } /* Set the form state so the cursor is on the first line of the buffer. * Preserve the column if possible. */ enum frame_event_status -textarea_op_bob(struct form_state *fs, struct form_control *fc) +textarea_op_bob(struct form_state *fs, struct form_control *fc, int utf8) { - return textarea_op(fs, fc, do_op_bob); + return textarea_op(fs, fc, utf8, do_op_bob); } /* Set the form state so the cursor is on the last line of the buffer. Preserve @@ -580,13 +712,13 @@ textarea_op_bob(struct form_state *fs, struct form_control *fc) * then shifting the state by the delta of both lines start position bounding * the whole thing to the end of the last line. */ enum frame_event_status -textarea_op_eob(struct form_state *fs, struct form_control *fc) +textarea_op_eob(struct form_state *fs, struct form_control *fc, int utf8) { - return textarea_op(fs, fc, do_op_eob); + return textarea_op(fs, fc, utf8, do_op_eob); } enum frame_event_status -textarea_op_enter(struct form_state *fs, struct form_control *fc) +textarea_op_enter(struct form_state *fs, struct form_control *fc, int utf8) { assert(fs && fs->value && fc); if_assert_failed return FRAME_EVENT_OK; @@ -607,6 +739,7 @@ set_textarea(struct document_view *doc_view, int direction) struct form_control *fc; struct form_state *fs; struct link *link; + int utf8 = doc_view->document->options.utf8; assert(doc_view && doc_view->vs && doc_view->document); assert(direction == 1 || direction == -1); @@ -628,7 +761,7 @@ set_textarea(struct document_view *doc_view, int direction) /* Depending on which way we entered the textarea move cursor so that * it is available at end or start. */ if (direction == 1) - textarea_op_eob(fs, fc); + textarea_op_eob(fs, fc, utf8); else - textarea_op_bob(fs, fc); + textarea_op_bob(fs, fc, utf8); } diff --git a/src/viewer/text/textarea.h b/src/viewer/text/textarea.h index 03e67070..0a27f58f 100644 --- a/src/viewer/text/textarea.h +++ b/src/viewer/text/textarea.h @@ -13,7 +13,7 @@ struct link; struct session; struct terminal; -int area_cursor(struct form_control *fc, struct form_state *fs); +int area_cursor(struct form_control *fc, struct form_state *fs, int utf8); void draw_textarea(struct terminal *term, struct form_state *fs, struct document_view *doc_view, struct link *link); unsigned char *encode_textarea(struct submitted_value *sv); @@ -21,13 +21,13 @@ extern int textarea_editor; void textarea_edit(int, struct terminal *, struct form_state *, struct document_view *, struct link *); void menu_textarea_edit(struct terminal *term, void *xxx, void *ses_); -enum frame_event_status textarea_op_home(struct form_state *fs, struct form_control *fc); -enum frame_event_status textarea_op_up(struct form_state *fs, struct form_control *fc); -enum frame_event_status textarea_op_down(struct form_state *fs, struct form_control *fc); -enum frame_event_status textarea_op_end(struct form_state *fs, struct form_control *fc); -enum frame_event_status textarea_op_bob(struct form_state *fs, struct form_control *fc); -enum frame_event_status textarea_op_eob(struct form_state *fs, struct form_control *fc); -enum frame_event_status textarea_op_enter(struct form_state *fs, struct form_control *fc); +enum frame_event_status textarea_op_home(struct form_state *fs, struct form_control *fc, int utf8); +enum frame_event_status textarea_op_up(struct form_state *fs, struct form_control *fc, int utf8); +enum frame_event_status textarea_op_down(struct form_state *fs, struct form_control *fc, int utf8); +enum frame_event_status textarea_op_end(struct form_state *fs, struct form_control *fc, int utf8); +enum frame_event_status textarea_op_bob(struct form_state *fs, struct form_control *fc, int utf8); +enum frame_event_status textarea_op_eob(struct form_state *fs, struct form_control *fc, int utf8); +enum frame_event_status textarea_op_enter(struct form_state *fs, struct form_control *fc, int utf8); void set_textarea(struct document_view *doc_view, int direction);