1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-09-27 02:56:18 -04:00
elinks/src/viewer/text/textarea.c
Witold Filipczyk a67188413c [lists] LIST_HEAD -> LIST_HEAD_EL to not clash with libevent's LIST_HEAD. Also added curl implementation of ftpes and sftp
Implementation of ftpes and sftp is based on curl's hiperfifo example. It requires libevent.
ftpes only encrypts control channel. There were problems when both control and data were encrypted. It stucked on SIZE.
Only successful connections work, errors are not handled properly.
2023-06-19 18:43:53 +02:00

1261 lines
28 KiB
C

/** Textarea form item handlers
* @file */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* XXX: we want memrchr() ! */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "elinks.h"
#include "bfu/dialog.h"
#include "document/document.h"
#include "document/forms.h"
#include "document/view.h"
#include "intl/libintl.h"
#include "session/session.h"
#include "terminal/draw.h"
#include "terminal/window.h"
#include "util/error.h"
#include "util/file.h"
#include "util/memory.h"
#include "util/string.h"
#include "viewer/action.h"
#include "viewer/text/form.h"
#include "viewer/text/textarea.h"
#include "viewer/text/view.h"
struct line_info {
int start;
int end;
#ifdef CONFIG_UTF8
int last_char_width;
int split_prev:1;
int split_next:1;
#endif /* CONFIG_UTF8 */
};
/** We add two extra entries to the table so the ending info can be added
* without reallocating. */
#define realloc_line_info(info, size) \
mem_align_alloc(info, size, (size) + 3, 0xFF)
#ifdef CONFIG_UTF8
/** Allocates a line_info table describing the layout of the textarea buffer.
*
* @param text the text to format; must be in UTF-8
* @param width is max width and the offset at which @a text will be
* wrapped
* @param wrap controls how the wrapping of @a text is performed
* @param format is non zero the @a text will be modified to make it
* suitable for encoding it for form posting
*/
static struct line_info *
format_textutf8(char *text, int width, enum form_wrap wrap, int format)
{
struct line_info *line = NULL;
int line_number = 0;
int begin = 0;
int pos = 0;
char *text_end;
int skip;
char *wrappos=NULL;
int chars_cells=0; /* Number of console chars on line */
assert(text);
if_assert_failed return NULL;
/* Allocate the ending entries */
if (!realloc_line_info(&line, 0))
return NULL;
text_end = text + strlen(text);
while (text[pos]) {
int char_cells = utf8_char2cells(&text[pos], text_end);
if (text[pos] == ' ')
wrappos = &text[pos];
if (text[pos] == '\n') {
skip = 1;
} else if (wrap == FORM_WRAP_NONE || chars_cells + char_cells < width) {
pos += utf8charlen(&text[pos]);
chars_cells += char_cells;
continue;
} else {
if (wrappos) {
/* When formatting text for form submitting we
* have to apply the wrapping mode. */
if (wrap == FORM_WRAP_HARD && format)
*wrappos = '\n';
pos = wrappos - text;
}
skip = !!wrappos;
}
if (!realloc_line_info(&line, line_number)) {
mem_free_if(line);
return NULL;
}
line[line_number].last_char_width = char_cells;
line[line_number].split_next = !skip;
line[line_number].start = begin;
line[line_number++].end = pos;
line[line_number].split_prev = !skip;
begin = pos += skip;
chars_cells = 0;
wrappos = NULL;
}
line[line_number].split_next = 0;
/* Flush the last text before the loop ended */
line[line_number].start = begin;
line[line_number++].end = pos;
/* Add end marker */
line[line_number].start = line[line_number].end = -1;
line[line_number].split_next = line[line_number].split_prev = 0;
line[0].split_prev = 0;
return line;
}
#endif /* CONFIG_UTF8 */
/** Allocates a line_info table describing the layout of the textarea buffer.
*
* @param text the text to format; must be in a unibyte charset
* @param width is max width and the offset at which @a text will be
* wrapped
* @param wrap controls how the wrapping of @a text is performed
* @param format is non zero the @a text will be modified to make it
* suitable for encoding it for form posting
*/
static struct line_info *
format_text(char *text, int width, enum form_wrap wrap, int format)
{
struct line_info *line = NULL;
int line_number = 0;
int begin = 0;
int pos = 0;
int skip;
assert(text);
if_assert_failed return NULL;
/* Allocate the ending entries */
if (!realloc_line_info(&line, 0))
return NULL;
while (text[pos]) {
if (text[pos] == '\n') {
skip = 1;
} else if (wrap == FORM_WRAP_NONE || pos - begin < width) {
pos++;
continue;
} else {
char *wrappos;
/* Find a place to wrap the text */
wrappos = (char *)memrchr(&text[begin], ' ', pos - begin);
if (wrappos) {
/* When formatting text for form submitting we
* have to apply the wrapping mode. */
if (wrap == FORM_WRAP_HARD && format)
*wrappos = '\n';
pos = wrappos - text;
}
skip = !!wrappos;
}
if (!realloc_line_info(&line, line_number)) {
mem_free_if(line);
return NULL;
}
line[line_number].start = begin;
line[line_number++].end = pos;
begin = pos += skip;
}
/* Flush the last text before the loop ended */
line[line_number].start = begin;
line[line_number++].end = pos;
/* Add end marker */
line[line_number].start = line[line_number].end = -1;
return line;
}
/** Searches for @a cursor_position (aka. position in the
* form_state.value string) for the corresponding entry in the @a line
* info. Returns the index or -1 if position is not found. */
static int
get_textarea_line_number(struct line_info *line, int cursor_position)
{
int idx;
for (idx = 0; line[idx].start != -1; idx++) {
int wrap;
if (cursor_position < line[idx].start) continue;
wrap = (line[idx + 1].start == line[idx].end);
if (cursor_position >= line[idx].end + !wrap) continue;
return idx;
}
return -1;
}
/** Fixes up the form_state.vpos and form_state.vypos members.
* @returns the logical position in the textarea view. */
#ifdef CONFIG_UTF8
int
area_cursor(struct el_form_control *fc, struct form_state *fs, int utf8)
{
struct line_info *line;
int x, y;
assert(fc && fs);
if_assert_failed return 0;
if (utf8)
line = format_textutf8(fs->value, fc->cols, fc->wrap, 0);
else
line = format_text(fs->value, fc->cols, fc->wrap, 0);
if (!line) return 0;
if (fs->state_cell)
y = get_textarea_line_number(line, fs->state_cell);
else
y = get_textarea_line_number(line, fs->state);
if (y == -1) {
mem_free(line);
return 0;
}
if (utf8) {
if (fs->state_cell) {
x = utf8_ptr2cells(fs->value + line[y].start,
fs->value + fs->state_cell);
x += line[y].last_char_width;
} else
x = utf8_ptr2cells(fs->value + line[y].start,
fs->value + fs->state);
} else {
x = fs->state - line[y].start;
if (fc->wrap && x == fc->cols) x--;
}
mem_free(line);
int_bounds(&fs->vpos, x - fc->cols + 1, x);
int_bounds(&fs->vypos, y - fc->rows + 1, y);
x -= fs->vpos;
y -= fs->vypos;
return y * fc->cols + x;
}
#else
int
area_cursor(struct el_form_control *fc, struct form_state *fs)
{
struct line_info *line;
int x, y;
assert(fc && fs);
if_assert_failed return 0;
line = format_text(fs->value, fc->cols, fc->wrap, 0);
if (!line) return 0;
y = get_textarea_line_number(line, fs->state);
if (y == -1) {
mem_free(line);
return 0;
}
x = fs->state - line[y].start;
mem_free(line);
if (fc->wrap && x == fc->cols) x--;
int_bounds(&fs->vpos, x - fc->cols + 1, x);
int_bounds(&fs->vypos, y - fc->rows + 1, y);
x -= fs->vpos;
y -= fs->vypos;
return y * fc->cols + x;
}
#endif
#ifdef CONFIG_UTF8
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;
struct el_form_control *fc;
struct el_box *box;
int vx, vy;
int sl, ye;
int x, xbase, y;
assert(term && doc_view && doc_view->document && doc_view->vs && link);
if_assert_failed return;
fc = get_link_form_control(link);
assertm(fc != NULL, "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, 1);
linex = format_textutf8(fs->value, fc->cols, fc->wrap, 0);
if (!linex) return;
line = linex;
sl = fs->vypos;
while (line->start != -1 && sl) sl--, line++;
xbase = 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;
char *text, *end;
text = fs->value + line->start;
end = fs->value + line->end;
text += utf8_cells2bytes(text, fs->vpos, end);
if (!row_is_in_box(box, y)) continue;
for (i = 0, x = xbase; i < fc->cols; i++, x++) {
unicode_val_T data;
if (i >= -fs->vpos && text < end) {
/* utf8_to_unicode will increment text. */
data = utf8_to_unicode(&text, end);
} else
data = '_';
if (col_is_in_box(box, x)) {
int cell = unicode_to_cell(data);
if (cell == 2) {
draw_char_data(term, x++, y, data);
i++;
data = UCS_NO_CHAR;
}
draw_char_data(term, x, y, data);
}
}
}
for (; y < ye; y++) {
int i;
if (!row_is_in_box(box, y)) continue;
for (i = 0, x = xbase; i < fc->cols; i++, x++) {
if (col_is_in_box(box, x))
draw_char_data(term, x, y, '_');
}
}
mem_free(linex);
}
#endif /* CONFIG_UTF8 */
void
draw_textarea(struct terminal *term, struct form_state *fs,
struct document_view *doc_view, struct link *link)
{
struct line_info *line, *linex;
struct el_form_control *fc;
struct el_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;
#ifdef CONFIG_UTF8
if (term->utf8_cp) {
draw_textarea_utf8(term, fs, doc_view, link);
return;
}
#endif /* CONFIG_UTF8 */
fc = get_link_form_control(link);
assertm(fc != NULL, "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;
#ifdef CONFIG_UTF8
area_cursor(fc, fs, 0);
#else
area_cursor(fc, fs);
#endif /* CONFIG_UTF8 */
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;
if (!row_is_in_box(box, y)) continue;
for (i = 0; i < fc->cols; i++) {
unsigned char data;
int xi = x + i;
if (!col_is_in_box(box, xi))
continue;
if (i >= -fs->vpos
&& i + fs->vpos < line->end - line->start)
data = fs->value[line->start + i + fs->vpos];
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);
}
char *
encode_textarea(struct submitted_value *sv)
{
struct el_form_control *fc;
void *blabla;
assert(sv && sv->value);
if_assert_failed return NULL;
fc = sv->form_control;
/* We need to reformat text now if it has to be wrapped hard, just
* before encoding it. */
/* TODO: Do we need here UTF-8 format or not? --scrool */
blabla = format_text(sv->value, fc->cols, fc->wrap, 1);
mem_free_if(blabla);
return encode_crlf(sv);
}
/** We use some evil hacking in order to make external textarea editor working.
* We need to have some way how to be notified that the editor finished and we
* should reload content of the textarea. So we use global variable
* @c textarea_editor as a flag whether we have one running, and if we have, we
* just call textarea_edit(1, ...). Then we recover our state from static
* variables, reload content of textarea back from file and clean up.
*
* Unfortunately, we can't support calling of editor from non-master links
* session, as it would be extremely ugly to hack (you would have to transfer
* the content of it back to master somehow, add special flags for not deleting
* of 'delete' etc) and I'm not going to do that now. Inter-links communication
* *NEEDS* rewrite, as it looks just like quick messy hack now. --pasky */
static char *
save_textarea_file(char *value)
{
char *filename;
FILE *fp = NULL;
int fd;
size_t nmemb, len;
filename = get_tempdir_filename("elinks-area-XXXXXX");
if (!filename) return NULL;
fd = safe_mkstemp(filename);
if (fd < 0) {
mem_free(filename);
return NULL;
}
len = strlen(value);
if (len == 0) return filename;
fp = fdopen(fd, "w");
if (!fp) {
error:
unlink(filename);
mem_free(filename);
close(fd);
return NULL;
}
nmemb = fwrite(value, len, 1, fp);
if (nmemb != 1) {
fclose(fp);
goto error;
}
if (fclose(fp) != 0)
goto error;
return filename;
}
struct textarea_data {
LIST_HEAD_EL(struct textarea_data);
size_t fc_maxlength;
struct form_state *fs;
struct terminal *term;
struct document_view *doc_view;
struct link *link;
char *fn;
};
static struct textarea_data *
init_textarea_data(struct terminal *term, struct form_state *fs,
struct document_view *doc_view, struct link *link)
{
struct textarea_data *td;
assert(fs && doc_view && link && term);
td = (struct textarea_data *)mem_calloc(1, sizeof(*td));
if (!td) return NULL;
td->fn = save_textarea_file(fs->value);
if (!td->fn) {
mem_free(td);
return NULL;
}
td->fs = fs;
td->doc_view = doc_view;
td->link = link;
td->fc_maxlength = get_link_form_control(link)->maxlength;
td->term = term;
return td;
}
static void
done_textarea_data(struct textarea_data *td)
{
assert(td);
mem_free(td->fn);
mem_free(td);
}
void
free_textarea_data(struct terminal *term)
{
assert(term);
if (term->textarea_data)
done_textarea_data((struct textarea_data *)term->textarea_data);
term->textarea_data = NULL;
}
void
textarea_edit(int op, struct terminal *term_, struct form_state *fs_,
struct document_view *doc_view_, struct link *link_)
{
struct textarea_data *td = NULL;
assert ((op == 0 || op == 1) && term_);
if_assert_failed return;
if (op == 0 && get_cmd_opt_bool("anonymous")) {
info_box(term_, 0, N_("Error"), ALIGN_CENTER,
N_("You cannot launch an external"
" editor in the anonymous mode."));
return;
}
if (op == 0) {
const char *ed;
char *ex;
assert(fs_ && doc_view_ && link_ && term_);
td = init_textarea_data(term_, fs_, doc_view_, link_);
if (!td)
return;
ed = get_opt_str("document.browse.forms.editor",
doc_view_->session);
if (!ed || !*ed) {
ed = getenv("EDITOR");
if (!ed || !*ed) ed = "vi";
}
ex = straconcat(ed, " ", td->fn, (char *) NULL);
if (!ex) {
unlink(td->fn);
done_textarea_data(td);
return;
}
td->term->textarea_data = td;
exec_on_terminal(td->term, ex, "", TERM_EXEC_FG);
mem_free(ex);
return;
} else if (op == 1) {
struct string file;
td = (struct textarea_data *)term_->textarea_data;
term_->textarea_data = NULL;
assert(td);
if (!td->fs || !init_string(&file)
|| !add_file_to_string(&file, td->fn)) {
done_textarea_data(td);
return;
}
if (file.length > td->fc_maxlength) {
file.source[td->fc_maxlength] = '\0';
/* Casting size_t fc_maxlength to unsigned int
* and formatting it with "%u" is safe,
* because fc_maxlength is smaller than
* file.length, which is an int. */
info_box(td->term, MSGBOX_FREE_TEXT, N_("Warning"),
ALIGN_CENTER,
msg_text(td->term,
N_("You have exceeded the textarea's"
" size limit: your input is %d"
" bytes, but the maximum is %u"
" bytes.\n\n"
"Your input has been truncated,"
" but you can still recover the"
" text that you entered from"
" this file: %s"), file.length,
(unsigned int) td->fc_maxlength, td->fn));
} else {
unlink(td->fn);
}
mem_free(td->fs->value);
td->fs->value = file.source;
td->fs->state = file.length;
if (td->doc_view && td->link)
draw_form_entry(td->term, td->doc_view, td->link);
}
done_textarea_data(td);
}
/* menu_func_T */
void
menu_textarea_edit(struct terminal *term, void *xxx, void *ses_)
{
struct session *ses = (struct session *)ses_;
struct document_view *doc_view;
struct link *link;
struct form_state *fs;
struct el_form_control *fc;
assert(term && ses);
if_assert_failed return;
doc_view = current_frame(ses);
assert(doc_view && doc_view->vs && doc_view->document);
if_assert_failed return;
link = get_current_link(doc_view);
if (!link) return;
fc = get_link_form_control(link);
if (form_field_is_readonly(fc))
return;
fs = find_form_state(doc_view, fc);
if (!fs) return;
textarea_edit(0, term, fs, doc_view, link);
}
#ifdef CONFIG_UTF8
static enum frame_event_status
textarea_op(struct form_state *fs, struct el_form_control *fc, int utf8,
int (*do_op)(struct form_state *, struct line_info *, int, int))
{
struct line_info *line;
int current, state;
int state_cell;
assert(fs && fs->value && fc);
if_assert_failed return FRAME_EVENT_OK;
if (utf8)
line = format_textutf8(fs->value, fc->cols, fc->wrap, 0);
else
line = format_text(fs->value, fc->cols, fc->wrap, 0);
if (!line) return FRAME_EVENT_OK;
current = get_textarea_line_number(line, fs->state);
state = fs->state;
state_cell = fs->state_cell;
if (do_op(fs, line, current, utf8)) {
mem_free(line);
return FRAME_EVENT_IGNORED;
}
mem_free(line);
return (fs->state == state && fs->state_cell == state_cell)
? FRAME_EVENT_OK : FRAME_EVENT_REFRESH;
}
#else
static enum frame_event_status
textarea_op(struct form_state *fs, struct el_form_control *fc,
int (*do_op)(struct form_state *, struct line_info *, int))
{
struct line_info *line;
int current, state;
assert(fs && fs->value && fc);
if_assert_failed return FRAME_EVENT_OK;
line = format_text(fs->value, fc->cols, fc->wrap, 0);
if (!line) return FRAME_EVENT_OK;
current = get_textarea_line_number(line, fs->state);
state = fs->state;
if (do_op(fs, line, current)) {
mem_free(line);
return FRAME_EVENT_IGNORED;
}
mem_free(line);
return fs->state == state ? FRAME_EVENT_OK : FRAME_EVENT_REFRESH;
}
#endif /* CONFIG_UTF8 */
#ifdef CONFIG_UTF8
void
new_pos(struct form_state *fs, struct line_info *line, int current, int max_cells)
{
char *text = fs->value + line[current].start;
char *end = fs->value + line[current].end;
int cells = 0;
while(cells < max_cells) {
unicode_val_T data = utf8_to_unicode(&text, end);
if (data == UCS_NO_CHAR) break;
cells += unicode_to_cell(data);
}
fs->state = (int)(text - fs->value);
}
#endif /* CONFIG_UTF8 */
static int
#ifdef CONFIG_UTF8
do_op_home(struct form_state *fs, struct line_info *line, int current, int utf8)
#else
do_op_home(struct form_state *fs, struct line_info *line, int current)
#endif /* CONFIG_UTF8 */
{
if (current == -1)
return 0;
#ifdef CONFIG_UTF8
if (utf8)
fs->state = line[current - !!fs->state_cell].start;
else
#endif /* CONFIG_UTF8 */
fs->state = line[current].start;
return 0;
}
#ifdef CONFIG_UTF8
static int
do_op_up(struct form_state *fs, struct line_info *line, int current, int utf8)
{
int old_state;
if (current == -1) return 0;
if (!(current - !!fs->state_cell)) return 1;
if (!utf8) {
fs->state -= line[current].start - line[current-1].start;
int_upper_bound(&fs->state, line[current-1].end);
return 0;
}
old_state = fs->state;
if (fs->state_cell) {
int len = utf8_ptr2cells(fs->value + line[current - 1].start,
fs->value + fs->state_cell);
new_pos(fs, line, current - 2, len + line[current - 1].last_char_width);
} else {
int len = utf8_ptr2cells(fs->value + line[current].start,
fs->value + fs->state);
new_pos(fs, line, current - 1, len);
}
if (old_state != fs->state ) {
if (fs->state_cell && fs->state == line[current - 1].start) {
char *new_value;
new_value = utf8_prevchar(fs->value + fs->state, 1, fs->value);
fs->state_cell = new_value - fs->value;
} else
fs->state_cell = 0;
}
return 0;
}
#else
static int
do_op_up(struct form_state *fs, struct line_info *line, int current)
{
if (current == -1) return 0;
if (!current) return 1;
fs->state -= line[current].start - line[current-1].start;
int_upper_bound(&fs->state, line[current-1].end);
return 0;
}
#endif /* CONFIG_UTF8 */
#ifdef CONFIG_UTF8
static int
do_op_down(struct form_state *fs, struct line_info *line, int current, int utf8)
{
int old_state;
if (current == -1) return 0;
if (line[current + 1 - !!fs->state_cell].start == -1) return 1;
if (!utf8) {
fs->state += line[current+1].start - line[current].start;
int_upper_bound(&fs->state, line[current+1].end);
return 0;
}
old_state = fs->state;
if (fs->state_cell) {
int len = utf8_ptr2cells(fs->value + line[current - 1].start,
fs->value + fs->state_cell);
new_pos(fs, line, current, len + line[current - 1].last_char_width);
} else {
int len = utf8_ptr2cells(fs->value + line[current].start,
fs->value + fs->state);
new_pos(fs, line, current + 1, len);
}
if (old_state != fs->state ) {
if (fs->state_cell && fs->state == line[current+1].start) {
char *new_value;
new_value = utf8_prevchar(fs->value + fs->state, 1, fs->value);
fs->state_cell = new_value - fs->value;
} else
fs->state_cell = 0;
}
return 0;
}
#else
static int
do_op_down(struct form_state *fs, struct line_info *line, int current)
{
if (current == -1) return 0;
if (line[current+1].start == -1) return 1;
fs->state += line[current+1].start - line[current].start;
int_upper_bound(&fs->state, line[current+1].end);
return 0;
}
#endif /* CONFIG_UTF8 */
#ifdef CONFIG_UTF8
static int
do_op_end(struct form_state *fs, struct line_info *line, int current, int utf8)
{
if (current == -1) {
fs->state = strlen(fs->value);
return 0;
}
if (!utf8) {
int wrap = line[current + 1].start == line[current].end;
/* Don't jump to next line when wrapping. */
fs->state = int_max(0, line[current].end - wrap);
return 0;
}
current -= !!fs->state_cell;
fs->state = line[current].end;
if (line[current].split_next) {
char *new_value;
new_value = utf8_prevchar(fs->value + fs->state, 1, fs->value);
fs->state_cell = new_value - fs->value;
} else {
fs->state_cell = 0;
}
return 0;
}
#else
static int
do_op_end(struct form_state *fs, struct line_info *line, int current)
{
if (current == -1) {
fs->state = strlen(fs->value);
} else {
int wrap = line[current + 1].start == line[current].end;
/* Don't jump to next line when wrapping. */
fs->state = int_max(0, line[current].end - wrap);
}
return 0;
}
#endif /* CONFIG_UTF8 */
static int
#ifdef CONFIG_UTF8
do_op_bob(struct form_state *fs, struct line_info *line, int current, int utf8)
#else
do_op_bob(struct form_state *fs, struct line_info *line, int current)
#endif /* CONFIG_UTF8 */
{
if (current == -1) return 0;
fs->state -= line[current].start;
int_upper_bound(&fs->state, line[0].end);
return 0;
}
static int
#ifdef CONFIG_UTF8
do_op_eob(struct form_state *fs, struct line_info *line, int current, int utf8)
#else
do_op_eob(struct form_state *fs, struct line_info *line, int current)
#endif /* CONFIG_UTF8 */
{
if (current == -1) {
fs->state = strlen(fs->value);
} else {
int last = get_textarea_line_number(line, strlen(fs->value));
assertm(last != -1, "line info corrupt");
fs->state += line[last].start - line[current].start;
int_upper_bound(&fs->state, line[last].end);
}
return 0;
}
#ifdef CONFIG_UTF8
enum frame_event_status
textarea_op_home(struct form_state *fs, struct el_form_control *fc, int utf8)
{
return textarea_op(fs, fc, utf8, do_op_home);
}
#else
enum frame_event_status
textarea_op_home(struct form_state *fs, struct el_form_control *fc)
{
return textarea_op(fs, fc, do_op_home);
}
#endif /* CONFIG_UTF8 */
#ifdef CONFIG_UTF8
enum frame_event_status
textarea_op_up(struct form_state *fs, struct el_form_control *fc, int utf8)
{
return textarea_op(fs, fc, utf8, do_op_up);
}
#else
enum frame_event_status
textarea_op_up(struct form_state *fs, struct el_form_control *fc)
{
return textarea_op(fs, fc, do_op_up);
}
#endif /* CONFIG_UTF8 */
#ifdef CONFIG_UTF8
enum frame_event_status
textarea_op_down(struct form_state *fs, struct el_form_control *fc, int utf8)
{
return textarea_op(fs, fc, utf8, do_op_down);
}
#else
enum frame_event_status
textarea_op_down(struct form_state *fs, struct el_form_control *fc)
{
return textarea_op(fs, fc, do_op_down);
}
#endif /* CONFIG_UTF8 */
#ifdef CONFIG_UTF8
enum frame_event_status
textarea_op_end(struct form_state *fs, struct el_form_control *fc, int utf8)
{
return textarea_op(fs, fc, utf8, do_op_end);
}
#else
enum frame_event_status
textarea_op_end(struct form_state *fs, struct el_form_control *fc)
{
return textarea_op(fs, fc, do_op_end);
}
#endif /* CONFIG_UTF8 */
/* Set the form state so the cursor is on the first line of the buffer.
* Preserve the column if possible. */
#ifdef CONFIG_UTF8
enum frame_event_status
textarea_op_bob(struct form_state *fs, struct el_form_control *fc, int utf8)
{
return textarea_op(fs, fc, utf8, do_op_bob);
}
#else
enum frame_event_status
textarea_op_bob(struct form_state *fs, struct el_form_control *fc)
{
return textarea_op(fs, fc, do_op_bob);
}
#endif /* CONFIG_UTF8 */
/** Set the form state so the cursor is on the last line of the buffer. Preserve
* the column if possible. This is done by getting current and last line and
* then shifting the state by the delta of both lines start position bounding
* the whole thing to the end of the last line. */
#ifdef CONFIG_UTF8
enum frame_event_status
textarea_op_eob(struct form_state *fs, struct el_form_control *fc, int utf8)
{
return textarea_op(fs, fc, utf8, do_op_eob);
}
#else
enum frame_event_status
textarea_op_eob(struct form_state *fs, struct el_form_control *fc)
{
return textarea_op(fs, fc, do_op_eob);
}
#endif /* CONFIG_UTF8 */
enum frame_event_status
#ifdef CONFIG_UTF8
textarea_op_enter(struct form_state *fs, struct el_form_control *fc, int utf8)
#else
textarea_op_enter(struct form_state *fs, struct el_form_control *fc)
#endif /* CONFIG_UTF8 */
{
assert(fs && fs->value && fc);
if_assert_failed return FRAME_EVENT_OK;
if (form_field_is_readonly(fc)
|| strlen(fs->value) >= fc->maxlength
|| !insert_in_string(&fs->value, fs->state, "\n", 1))
return FRAME_EVENT_OK;
fs->state++;
return FRAME_EVENT_REFRESH;
}
#ifdef CONFIG_UTF8
static int
do_op_left(struct form_state *fs, struct line_info *line, int current, int utf8)
{
int old_state;
int new_state;
char *new_value;
if (!utf8) {
fs->state = int_max(fs->state - 1, 0);
return 0;
}
if (fs->state_cell) {
fs->state = fs->state_cell;
fs->state_cell = 0;
return 0;
}
old_state = fs->state;
new_value = utf8_prevchar(fs->value + fs->state, 1, fs->value);
new_state = new_value - fs->value;
if (old_state != new_state) {
if (old_state == line[current].start && line[current].split_prev)
fs->state_cell = new_state;
else
fs->state = new_state;
}
return 0;
}
static int
do_op_right(struct form_state *fs, struct line_info *line, int current, int utf8)
{
char *text, *end;
int old_state;
if (!utf8) {
/* TODO: zle */
fs->state = int_min(fs->state + 1, strlen(fs->value));
return 0;
}
if (fs->state_cell) {
fs->state_cell = 0;
return 0;
}
text = fs->value + fs->state;
end = strchr(text, '\0');
old_state = fs->state;
utf8_to_unicode(&text, end);
fs->state = text - fs->value;
if (old_state != fs->state && line[current].split_next) {
fs->state_cell = (fs->state == line[current].end) ? old_state:0;
} else if (!line[current].split_next) {
fs->state_cell = 0;
}
return 0;
}
#endif /* CONFIG_UTF8 */
#ifdef CONFIG_UTF8
enum frame_event_status
textarea_op_left(struct form_state *fs, struct el_form_control *fc, int utf8)
{
return textarea_op(fs, fc, utf8, do_op_left);
}
enum frame_event_status
textarea_op_right(struct form_state *fs, struct el_form_control *fc, int utf8)
{
return textarea_op(fs, fc, utf8, do_op_right);
}
#endif /* CONFIG_UTF8 */
void
set_textarea(struct document_view *doc_view, int direction)
{
struct el_form_control *fc;
struct form_state *fs;
struct link *link;
#ifdef CONFIG_UTF8
int utf8 = doc_view->document->options.utf8;
#endif /* CONFIG_UTF8 */
assert(doc_view && doc_view->vs && doc_view->document);
assert(direction == 1 || direction == -1);
if_assert_failed return;
link = get_current_link(doc_view);
if (!link || link->type != LINK_AREA)
return;
fc = get_link_form_control(link);
assertm(fc != NULL, "link has no form control");
if_assert_failed return;
if (fc->mode == FORM_MODE_DISABLED) return;
fs = find_form_state(doc_view, fc);
if (!fs || !fs->value) return;
/* Depending on which way we entered the textarea move cursor so that
* it is available at end or start. */
#ifdef CONFIG_UTF8
if (direction == 1)
textarea_op_eob(fs, fc, utf8);
else
textarea_op_bob(fs, fc, utf8);
#else
if (direction == 1)
textarea_op_eob(fs, fc);
else
textarea_op_bob(fs, fc);
#endif /* CONFIG_UTF8 */
}