From 1ac2e00134f6174708ce2cec21a96a0ae08f9ad6 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sat, 5 Jan 2013 15:27:53 +0000 Subject: Remove forward declaration. --- desktop/textarea.c | 1031 ++++++++++++++++++++++++++-------------------------- 1 file changed, 512 insertions(+), 519 deletions(-) diff --git a/desktop/textarea.c b/desktop/textarea.c index 92fb02c9e..5c2e26947 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -112,130 +112,366 @@ struct textarea { }; -static bool textarea_insert_text(struct textarea *ta, unsigned int index, - const char *text); -static bool textarea_replace_text(struct textarea *ta, unsigned int start, - unsigned int end, const char *text); -static bool textarea_reflow(struct textarea *ta, unsigned int line); -static unsigned int textarea_get_xy_offset(struct textarea *ta, int x, int y); -static bool textarea_set_caret_xy(struct textarea *ta, int x, int y); -static bool textarea_scroll_visible(struct textarea *ta); -static bool textarea_select(struct textarea *ta, int c_start, int c_end); -static bool textarea_select_fragment(struct textarea *ta); + +/** + * Normalises any line endings within the text, replacing CRLF or CR with + * LF as necessary. If the textarea is single line, then all linebreaks are + * converted into spaces. + * + * \param ta Text area + * \param b_start Byte offset to start at + * \param b_len Byte length to check + */ static void textarea_normalise_text(struct textarea *ta, - unsigned int b_start, unsigned int b_len); + unsigned int b_start, unsigned int b_len) +{ + bool multi = (ta->flags & TEXTAREA_MULTILINE) ? true:false; + unsigned int index; + + /* Remove CR characters. If it's a CRLF pair delete it ot replace it + * with LF otherwise. + */ + for (index = 0; index < b_len; index++) { + if (ta->text[b_start + index] == '\r') { + if (b_start + index + 1 <= ta->text_len && + ta->text[b_start + index + 1] == '\n') { + ta->text_len--; + ta->text_utf8_len--; + memmove(ta->text + b_start + index, + ta->text + b_start + index + 1, + ta->text_len - b_start - index); + } + else + ta->text[b_start + index] = '\n'; + } + /* Replace newlines with spaces if this is a single line + * textarea. + */ + if (!multi && (ta->text[b_start + index] == '\n')) + ta->text[b_start + index] = ' '; + } +} -/* exported interface, documented in textarea.h */ -struct textarea *textarea_create(int width, int height, - textarea_flags flags, const plot_font_style_t *style, - textarea_redraw_request_callback redraw_request, void *data) + +/** + * Selects a character range in the textarea and redraws it + * + * \param ta Text area + * \param c_start First character (inclusive) + * \param c_end Last character (exclusive) + * \return true on success false otherwise + */ +static bool textarea_select(struct textarea *ta, int c_start, int c_end) { - struct textarea *ret; + int swap = -1; - if (redraw_request == NULL) { - LOG(("no callback provided")); - return NULL; + /* if start is after end they get swapped, start won't and end will + be selected this way */ + if (c_start > c_end) { + swap = c_start; + c_start = c_end; + c_end = swap; } - ret = malloc(sizeof(struct textarea)); - if (ret == NULL) { - LOG(("malloc failed")); - return NULL; + ta->selection_start = c_start; + ta->selection_end = c_end; + + if (!(ta->flags & TEXTAREA_READONLY)) { + if (swap == -1) + return textarea_set_caret(ta, c_end); + else + return textarea_set_caret(ta, c_start); } - ret->redraw_request = redraw_request; - ret->data = data; - ret->vis_width = width; - ret->vis_height = height; - ret->scroll_x = 0; - ret->scroll_y = 0; - ret->drag_start_char = 0; + ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height); + return true; +} - ret->flags = flags; - ret->text = malloc(64); - if (ret->text == NULL) { - LOG(("malloc failed")); - free(ret); - return NULL; - } - ret->text[0] = '\0'; - ret->text_alloc = 64; - ret->text_len = 1; - ret->text_utf8_len = 0; - ret->fstyle = *style; +/** + * Selects a text fragment, relative to current caret position. + * + * \param ta Text area + * \return True on success, false otherwise + */ +static bool textarea_select_fragment(struct textarea * ta) +{ + int caret_pos, sel_start = 0, sel_end = 0, index; + size_t b_start, b_end; - ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2), FMUL(nscss_screen_dpi, INTTOFIX((style->size / FONT_SIZE_SCALE))))), F_72)); + /* Fragment separators must be suitable for URLs and ordinary text */ + static const char *sep = " /:.\r\n"; - ret->caret_pos.line = ret->caret_pos.char_off = 0; - ret->caret_x = MARGIN_LEFT; - ret->caret_y = 0; - ret->selection_start = -1; - ret->selection_end = -1; + caret_pos = textarea_get_caret(ta); + if (caret_pos < 0) { + return false; + } - ret->line_count = 0; - ret->lines = NULL; + /* Compute byte offset of caret position */ + for (b_start = 0, index = 0; index < caret_pos; + b_start = utf8_next(ta->text, ta->text_len, + b_start), + index++) { + /* Cache the character offset of the last separator */ + if (strchr(sep, ta->text[b_start]) != NULL) { + /* Add one to start to skip over separator */ + sel_start = index + 1; + } + } - return ret; -} + /* Search for next separator, if any */ + for (b_end = b_start; b_end < ta->text_len; + b_end = utf8_next(ta->text, ta->text_len, + b_end), + index++) { + if (strchr(sep, ta->text[b_end]) != NULL) { + sel_end = index; + break; + } + } + if (sel_start < sel_end) { + textarea_select(ta, sel_start, sel_end); + return true; + } + return false; +} -/* exported interface, documented in textarea.h */ -void textarea_destroy(struct textarea *ta) + +/** + * Scrolls a textarea to make the caret visible (doesn't perform a redraw) + * + * \param ta The text area to be scrolled + * \return true if textarea was scrolled false otherwise + */ +static bool textarea_scroll_visible(struct textarea *ta) { - free(ta->text); - free(ta->lines); - free(ta); + int x0, x1, y0, y1, x, y; + int index, b_off; + bool scrolled = false; + + if (ta->caret_pos.char_off == -1) + return false; + + x0 = MARGIN_LEFT; + x1 = ta->vis_width - MARGIN_RIGHT; + y0 = 0; + y1 = ta->vis_height; + + index = textarea_get_caret(ta); + + /* find byte offset of caret position */ + for (b_off = 0; index-- > 0; + b_off = utf8_next(ta->text, ta->text_len, b_off)) + ; /* do nothing */ + + nsfont.font_width(&ta->fstyle, + ta->text + ta->lines[ta->caret_pos.line].b_start, + b_off - ta->lines[ta->caret_pos.line].b_start, + &x); + + /* top-left of caret */ + x += MARGIN_LEFT - ta->scroll_x; + y = ta->line_height * ta->caret_pos.line - ta->scroll_y; + + /* check and change vertical scroll */ + if (y < y0) { + ta->scroll_y -= y0 - y; + scrolled = true; + } else if (y + ta->line_height > y1) { + ta->scroll_y += y + ta->line_height - y1; + scrolled = true; + } + + + /* check and change horizontal scroll */ + if (x < x0) { + ta->scroll_x -= x0 - x ; + scrolled = true; + } else if (x > x1 - 1) { + ta->scroll_x += x - (x1 - 1); + scrolled = true; + } + + return scrolled; } -/* exported interface, documented in textarea.h */ -bool textarea_set_text(struct textarea *ta, const char *text) +/** + * Reflow a text area from the given line onwards + * + * \param ta Text area to reflow + * \param line Line number to begin reflow on + * \return true on success false otherwise + */ +static bool textarea_reflow(struct textarea *ta, unsigned int line) { - unsigned int len = strlen(text) + 1; + char *text; + unsigned int len; + size_t b_off; + int x; + char *space; + unsigned int line_count = 0; - if (len >= ta->text_alloc) { - char *temp = realloc(ta->text, len + 64); - if (temp == NULL) { - LOG(("realloc failed")); + /** \todo pay attention to line parameter */ + /** \todo create horizontal scrollbar if needed */ + + ta->line_count = 0; + + if (ta->lines == NULL) { + ta->lines = + malloc(LINE_CHUNK_SIZE * sizeof(struct line_info)); + if (ta->lines == NULL) { + LOG(("malloc failed")); return false; } - ta->text = temp; - ta->text_alloc = len + 64; } - memcpy(ta->text, text, len); - ta->text_len = len; - ta->text_utf8_len = utf8_length(ta->text); + if (!(ta->flags & TEXTAREA_MULTILINE)) { + /* Single line */ + ta->lines[line_count].b_start = 0; + ta->lines[line_count++].b_length = ta->text_len - 1; - textarea_normalise_text(ta, 0, len); + ta->line_count = line_count; - return textarea_reflow(ta, 0); + return true; + } + + for (len = ta->text_len - 1, text = ta->text; len > 0; + len -= b_off, text += b_off) { + + nsfont.font_split(&ta->fstyle, text, len, + ta->vis_width - MARGIN_LEFT - MARGIN_RIGHT, + &b_off, &x); + + if (line_count > 0 && line_count % LINE_CHUNK_SIZE == 0) { + struct line_info *temp = realloc(ta->lines, + (line_count + LINE_CHUNK_SIZE) * + sizeof(struct line_info)); + if (temp == NULL) { + LOG(("realloc failed")); + return false; + } + + ta->lines = temp; + } + + /* handle LF */ + for (space = text; space <= text + b_off; space++) { + if (*space == '\n') + break; + } + + if (space <= text + b_off) { + /* Found newline; use it */ + ta->lines[line_count].b_start = text - ta->text; + ta->lines[line_count++].b_length = space - text; + + b_off = space + 1 - text; + + if (len - b_off == 0) { + /* reached end of input => add last line */ + ta->lines[line_count].b_start = + text + b_off - ta->text; + ta->lines[line_count++].b_length = 0; + } + + continue; + } + + if (len - b_off > 0) { + /* find last space (if any) */ + for (space = text + b_off; space > text; space--) + if (*space == ' ') + break; + + if (space != text) + b_off = space + 1 - text; + } + + ta->lines[line_count].b_start = text - ta->text; + ta->lines[line_count++].b_length = b_off; + } + + ta->line_count = line_count; + + return true; } -/* exported interface, documented in textarea.h */ -int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) +/** + * get character offset from the beginning of the text for some coordinates + * + * \param ta Text area + * \param x X coordinate + * \param y Y coordinate + * \return character offset + */ +static unsigned int textarea_get_xy_offset(struct textarea *ta, int x, int y) { - if (buf == NULL && len == 0) { - /* want length */ - return ta->text_len; - } else if (buf == NULL) { - /* Can't write to NULL */ - return -1; - } + size_t b_off, temp; + unsigned int c_off; + int line; - if (len < ta->text_len) { - LOG(("buffer too small")); - return -1; - } + if (!ta->line_count) + return 0; - memcpy(buf, ta->text, ta->text_len); + x = x - MARGIN_LEFT + ta->scroll_x; + y = y + ta->scroll_y; - return ta->text_len; + if (x < 0) + x = 0; + + line = y / ta->line_height; + + if (line < 0) + line = 0; + if (ta->line_count - 1 < line) + line = ta->line_count - 1; + + nsfont.font_position_in_string(&ta->fstyle, + ta->text + ta->lines[line].b_start, + ta->lines[line].b_length, x, &b_off, &x); + + /* If the calculated byte offset corresponds with the number of bytes + * in the line, and the line has been soft-wrapped, then ensure the + * caret offset is before the trailing space character, rather than + * after it. Otherwise, the caret will be placed at the start of the + * following line, which is undesirable. + */ + if (b_off == (unsigned)ta->lines[line].b_length && + ta->text[ta->lines[line].b_start + + ta->lines[line].b_length - 1] == ' ') + b_off--; + + for (temp = 0, c_off = 0; temp < b_off + ta->lines[line].b_start; + temp = utf8_next(ta->text, ta->text_len, temp)) + c_off++; + + return c_off; +} + + +/** + * Set the caret's position + * + * \param ta Text area + * \param x X position of caret in a window relative to text area top left + * \param y Y position of caret in a window relative to text area top left + * \return true on success false otherwise + */ +static bool textarea_set_caret_xy(struct textarea *ta, int x, int y) +{ + unsigned int c_off; + + if (ta->flags & TEXTAREA_READONLY) + return true; + + c_off = textarea_get_xy_offset(ta, x, y); + return textarea_set_caret(ta, c_off); } @@ -247,7 +483,7 @@ int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) * \param text UTF-8 text to insert * \return false on memory exhaustion, true otherwise */ -bool textarea_insert_text(struct textarea *ta, unsigned int index, +static bool textarea_insert_text(struct textarea *ta, unsigned int index, const char *text) { unsigned int b_len = strlen(text); @@ -301,7 +537,7 @@ bool textarea_insert_text(struct textarea *ta, unsigned int index, * \param text UTF-8 text to insert * \return false on memory exhaustion, true otherwise */ -bool textarea_replace_text(struct textarea *ta, unsigned int start, +static bool textarea_replace_text(struct textarea *ta, unsigned int start, unsigned int end, const char *text) { unsigned int b_len = strlen(text); @@ -315,53 +551,169 @@ bool textarea_replace_text(struct textarea *ta, unsigned int start, if (end > ta->text_utf8_len) end = ta->text_utf8_len; - if (start == end) - return textarea_insert_text(ta, start, text); + if (start == end) + return textarea_insert_text(ta, start, text); + + if (start > end) + return false; + + diff = end - start; + + /* find byte offset of replace start */ + for (b_start = 0; start-- > 0; + b_start = utf8_next(ta->text, ta->text_len, b_start)) + ; /* do nothing */ + + /* find byte length of replaced text */ + for (b_end = b_start; diff-- > 0; + b_end = utf8_next(ta->text, ta->text_len, b_end)) + ; /* do nothing */ + + if (b_len + ta->text_len - (b_end - b_start) >= ta->text_alloc) { + char *temp = realloc(ta->text, + b_len + ta->text_len - (b_end - b_start) + 64); + if (temp == NULL) { + LOG(("realloc failed")); + return false; + } + + ta->text = temp; + ta->text_alloc = + b_len + ta->text_len - (b_end - b_start) + 64; + } + + /* Shift text following to new position */ + memmove(ta->text + b_start + b_len, ta->text + b_end, + ta->text_len - b_end); + + /* Insert new text */ + memcpy(ta->text + b_start, text, b_len); + + ta->text_len += b_len - (b_end - b_start); + ta->text_utf8_len = utf8_length(ta->text); + textarea_normalise_text(ta, b_start, b_len); + + /** \todo calculate line to reflow from */ + return textarea_reflow(ta, 0); +} + + + + +/* exported interface, documented in textarea.h */ +struct textarea *textarea_create(int width, int height, + textarea_flags flags, const plot_font_style_t *style, + textarea_redraw_request_callback redraw_request, void *data) +{ + struct textarea *ret; + + if (redraw_request == NULL) { + LOG(("no callback provided")); + return NULL; + } + + ret = malloc(sizeof(struct textarea)); + if (ret == NULL) { + LOG(("malloc failed")); + return NULL; + } + + ret->redraw_request = redraw_request; + ret->data = data; + ret->vis_width = width; + ret->vis_height = height; + ret->scroll_x = 0; + ret->scroll_y = 0; + ret->drag_start_char = 0; + + + ret->flags = flags; + ret->text = malloc(64); + if (ret->text == NULL) { + LOG(("malloc failed")); + free(ret); + return NULL; + } + ret->text[0] = '\0'; + ret->text_alloc = 64; + ret->text_len = 1; + ret->text_utf8_len = 0; + + ret->fstyle = *style; + + ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2), + FMUL(nscss_screen_dpi, + INTTOFIX((style->size / FONT_SIZE_SCALE))))), F_72)); + + ret->caret_pos.line = ret->caret_pos.char_off = 0; + ret->caret_x = MARGIN_LEFT; + ret->caret_y = 0; + ret->selection_start = -1; + ret->selection_end = -1; + + ret->line_count = 0; + ret->lines = NULL; - if (start > end) - return false; + return ret; +} - diff = end - start; - /* find byte offset of replace start */ - for (b_start = 0; start-- > 0; - b_start = utf8_next(ta->text, ta->text_len, b_start)) - ; /* do nothing */ +/* exported interface, documented in textarea.h */ +void textarea_destroy(struct textarea *ta) +{ + free(ta->text); + free(ta->lines); + free(ta); +} - /* find byte length of replaced text */ - for (b_end = b_start; diff-- > 0; - b_end = utf8_next(ta->text, ta->text_len, b_end)) - ; /* do nothing */ - if (b_len + ta->text_len - (b_end - b_start) >= ta->text_alloc) { - char *temp = realloc(ta->text, - b_len + ta->text_len - (b_end - b_start) + 64); +/* exported interface, documented in textarea.h */ +bool textarea_set_text(struct textarea *ta, const char *text) +{ + unsigned int len = strlen(text) + 1; + + if (len >= ta->text_alloc) { + char *temp = realloc(ta->text, len + 64); if (temp == NULL) { LOG(("realloc failed")); return false; } - ta->text = temp; - ta->text_alloc = - b_len + ta->text_len - (b_end - b_start) + 64; + ta->text_alloc = len + 64; } - /* Shift text following to new position */ - memmove(ta->text + b_start + b_len, ta->text + b_end, - ta->text_len - b_end); - - /* Insert new text */ - memcpy(ta->text + b_start, text, b_len); - - ta->text_len += b_len - (b_end - b_start); + memcpy(ta->text, text, len); + ta->text_len = len; ta->text_utf8_len = utf8_length(ta->text); - textarea_normalise_text(ta, b_start, b_len); - /** \todo calculate line to reflow from */ + textarea_normalise_text(ta, 0, len); + return textarea_reflow(ta, 0); } +/* exported interface, documented in textarea.h */ +int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) +{ + if (buf == NULL && len == 0) { + /* want length */ + return ta->text_len; + } else if (buf == NULL) { + /* Can't write to NULL */ + return -1; + } + + if (len < ta->text_len) { + LOG(("buffer too small")); + return -1; + } + + memcpy(buf, ta->text, ta->text_len); + + return ta->text_len; +} + + /* exported interface, documented in textarea.h */ bool textarea_set_caret(struct textarea *ta, int caret) { @@ -457,235 +809,61 @@ bool textarea_set_caret(struct textarea *ta, int caret) /* find byte offset of caret position */ for (b_off = 0; index-- > 0; b_off = utf8_next(ta->text, - ta->text_len, b_off)) - ; /* do nothing */ - - nsfont.font_width(&ta->fstyle, - ta->text + - ta->lines[ta->caret_pos.line].b_start, - b_off - ta->lines[ta->caret_pos.line].b_start, - &x); - - x += MARGIN_LEFT - ta->scroll_x; - ta->caret_x = x; - y = ta->line_height * ta->caret_pos.line - ta->scroll_y; - ta->caret_y = y; - - if (textarea_scroll_visible(ta)) { - ta->redraw_request(ta->data, 0, 0, - ta->vis_width, - ta->vis_height); - } else { - x0 = max(x - 1, MARGIN_LEFT); - y0 = max(y + text_y_offset, 0); - x1 = min(x + 1, ta->vis_width - MARGIN_RIGHT); - y1 = min(y + ta->line_height + text_y_offset, - ta->vis_height); - - width = x1 - x0; - height = y1 - y0; - - if (width > 0 && height > 0) { - ta->redraw_request(ta->data, x0, y0, - width, height); - } - } - } - - return true; -} - - -/** - * get character offset from the beginning of the text for some coordinates - * - * \param ta Text area - * \param x X coordinate - * \param y Y coordinate - * \return character offset - */ -unsigned int textarea_get_xy_offset(struct textarea *ta, int x, int y) -{ - size_t b_off, temp; - unsigned int c_off; - int line; - - if (!ta->line_count) - return 0; - - x = x - MARGIN_LEFT + ta->scroll_x; - y = y + ta->scroll_y; - - if (x < 0) - x = 0; - - line = y / ta->line_height; - - if (line < 0) - line = 0; - if (ta->line_count - 1 < line) - line = ta->line_count - 1; - - nsfont.font_position_in_string(&ta->fstyle, - ta->text + ta->lines[line].b_start, - ta->lines[line].b_length, x, &b_off, &x); - - /* If the calculated byte offset corresponds with the number of bytes - * in the line, and the line has been soft-wrapped, then ensure the - * caret offset is before the trailing space character, rather than - * after it. Otherwise, the caret will be placed at the start of the - * following line, which is undesirable. - */ - if (b_off == (unsigned)ta->lines[line].b_length && - ta->text[ta->lines[line].b_start + - ta->lines[line].b_length - 1] == ' ') - b_off--; - - for (temp = 0, c_off = 0; temp < b_off + ta->lines[line].b_start; - temp = utf8_next(ta->text, ta->text_len, temp)) - c_off++; - - return c_off; -} - - -/** - * Set the caret's position - * - * \param ta Text area - * \param x X position of caret in a window relative to text area top left - * \param y Y position of caret in a window relative to text area top left - * \return true on success false otherwise - */ -bool textarea_set_caret_xy(struct textarea *ta, int x, int y) -{ - unsigned int c_off; - - if (ta->flags & TEXTAREA_READONLY) - return true; - - c_off = textarea_get_xy_offset(ta, x, y); - return textarea_set_caret(ta, c_off); -} - - -/* exported interface, documented in textarea.h */ -int textarea_get_caret(struct textarea *ta) -{ - unsigned int c_off = 0, b_off; - - - /* if the text is a trailing NUL only */ - if (ta->text_utf8_len == 0) - return 0; - - /* Calculate character offset of this line's start */ - for (b_off = 0; b_off < ta->lines[ta->caret_pos.line].b_start; - b_off = utf8_next(ta->text, ta->text_len, b_off)) - c_off++; - - return c_off + ta->caret_pos.char_off; -} - -/** - * Reflow a text area from the given line onwards - * - * \param ta Text area to reflow - * \param line Line number to begin reflow on - * \return true on success false otherwise - */ -bool textarea_reflow(struct textarea *ta, unsigned int line) -{ - char *text; - unsigned int len; - size_t b_off; - int x; - char *space; - unsigned int line_count = 0; - - /** \todo pay attention to line parameter */ - /** \todo create horizontal scrollbar if needed */ - - ta->line_count = 0; - - if (ta->lines == NULL) { - ta->lines = - malloc(LINE_CHUNK_SIZE * sizeof(struct line_info)); - if (ta->lines == NULL) { - LOG(("malloc failed")); - return false; - } - } - - if (!(ta->flags & TEXTAREA_MULTILINE)) { - /* Single line */ - ta->lines[line_count].b_start = 0; - ta->lines[line_count++].b_length = ta->text_len - 1; - - ta->line_count = line_count; - - return true; - } - - for (len = ta->text_len - 1, text = ta->text; len > 0; - len -= b_off, text += b_off) { - - nsfont.font_split(&ta->fstyle, text, len, - ta->vis_width - MARGIN_LEFT - MARGIN_RIGHT, - &b_off, &x); - - if (line_count > 0 && line_count % LINE_CHUNK_SIZE == 0) { - struct line_info *temp = realloc(ta->lines, - (line_count + LINE_CHUNK_SIZE) * - sizeof(struct line_info)); - if (temp == NULL) { - LOG(("realloc failed")); - return false; - } + ta->text_len, b_off)) + ; /* do nothing */ - ta->lines = temp; - } + nsfont.font_width(&ta->fstyle, + ta->text + + ta->lines[ta->caret_pos.line].b_start, + b_off - ta->lines[ta->caret_pos.line].b_start, + &x); - /* handle LF */ - for (space = text; space <= text + b_off; space++) { - if (*space == '\n') - break; - } + x += MARGIN_LEFT - ta->scroll_x; + ta->caret_x = x; + y = ta->line_height * ta->caret_pos.line - ta->scroll_y; + ta->caret_y = y; - if (space <= text + b_off) { - /* Found newline; use it */ - ta->lines[line_count].b_start = text - ta->text; - ta->lines[line_count++].b_length = space - text; + if (textarea_scroll_visible(ta)) { + ta->redraw_request(ta->data, 0, 0, + ta->vis_width, + ta->vis_height); + } else { + x0 = max(x - 1, MARGIN_LEFT); + y0 = max(y + text_y_offset, 0); + x1 = min(x + 1, ta->vis_width - MARGIN_RIGHT); + y1 = min(y + ta->line_height + text_y_offset, + ta->vis_height); - b_off = space + 1 - text; + width = x1 - x0; + height = y1 - y0; - if (len - b_off == 0) { - /* reached end of input => add last line */ - ta->lines[line_count].b_start = - text + b_off - ta->text; - ta->lines[line_count++].b_length = 0; + if (width > 0 && height > 0) { + ta->redraw_request(ta->data, x0, y0, + width, height); } - - continue; } + } - if (len - b_off > 0) { - /* find last space (if any) */ - for (space = text + b_off; space > text; space--) - if (*space == ' ') - break; + return true; +} - if (space != text) - b_off = space + 1 - text; - } - ta->lines[line_count].b_start = text - ta->text; - ta->lines[line_count++].b_length = b_off; - } +/* exported interface, documented in textarea.h */ +int textarea_get_caret(struct textarea *ta) +{ + unsigned int c_off = 0, b_off; - ta->line_count = line_count; - return true; + /* if the text is a trailing NUL only */ + if (ta->text_utf8_len == 0) + return 0; + + /* Calculate character offset of this line's start */ + for (b_off = 0; b_off < ta->lines[ta->caret_pos.line].b_start; + b_off = utf8_next(ta->text, ta->text_len, b_off)) + c_off++; + + return c_off + ta->caret_pos.char_off; } @@ -1197,64 +1375,6 @@ bool textarea_keypress(struct textarea *ta, uint32_t key) return true; } -/** - * Scrolls a textarea to make the caret visible (doesn't perform a redraw) - * - * \param ta The text area to be scrolled - * \return true if textarea was scrolled false otherwise - */ -bool textarea_scroll_visible(struct textarea *ta) -{ - int x0, x1, y0, y1, x, y; - int index, b_off; - bool scrolled = false; - - if (ta->caret_pos.char_off == -1) - return false; - - x0 = MARGIN_LEFT; - x1 = ta->vis_width - MARGIN_RIGHT; - y0 = 0; - y1 = ta->vis_height; - - index = textarea_get_caret(ta); - - /* find byte offset of caret position */ - for (b_off = 0; index-- > 0; - b_off = utf8_next(ta->text, ta->text_len, b_off)) - ; /* do nothing */ - - nsfont.font_width(&ta->fstyle, - ta->text + ta->lines[ta->caret_pos.line].b_start, - b_off - ta->lines[ta->caret_pos.line].b_start, - &x); - - /* top-left of caret */ - x += MARGIN_LEFT - ta->scroll_x; - y = ta->line_height * ta->caret_pos.line - ta->scroll_y; - - /* check and change vertical scroll */ - if (y < y0) { - ta->scroll_y -= y0 - y; - scrolled = true; - } else if (y + ta->line_height > y1) { - ta->scroll_y += y + ta->line_height - y1; - scrolled = true; - } - - - /* check and change horizontal scroll */ - if (x < x0) { - ta->scroll_x -= x0 - x ; - scrolled = true; - } else if (x > x1 - 1) { - ta->scroll_x += x - (x1 - 1); - scrolled = true; - } - - return scrolled; -} - /* exported interface, documented in textarea.h */ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, @@ -1305,133 +1425,6 @@ bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse, return textarea_select(ta, ta->drag_start_char, c_end); } -/** - * Selects a character range in the textarea and redraws it - * - * \param ta Text area - * \param c_start First character (inclusive) - * \param c_end Last character (exclusive) - * \return true on success false otherwise - */ -bool textarea_select(struct textarea *ta, int c_start, int c_end) -{ - int swap = -1; - - /* if start is after end they get swapped, start won't and end will - be selected this way */ - if (c_start > c_end) { - swap = c_start; - c_start = c_end; - c_end = swap; - } - - ta->selection_start = c_start; - ta->selection_end = c_end; - - if (!(ta->flags & TEXTAREA_READONLY)) { - if (swap == -1) - return textarea_set_caret(ta, c_end); - else - return textarea_set_caret(ta, c_start); - } - - ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height); - - return true; -} - - -/** - * Selects a text fragment, relative to current caret position. - * - * \param ta Text area - * \return True on success, false otherwise - */ -static bool textarea_select_fragment(struct textarea * ta) -{ - int caret_pos, sel_start = 0, sel_end = 0, index; - size_t b_start, b_end; - - /* Fragment separators must be suitable for URLs and ordinary text */ - static const char *sep = " /:.\r\n"; - - caret_pos = textarea_get_caret(ta); - if (caret_pos < 0) { - return false; - } - - /* Compute byte offset of caret position */ - for (b_start = 0, index = 0; index < caret_pos; - b_start = utf8_next(ta->text, ta->text_len, - b_start), - index++) { - /* Cache the character offset of the last separator */ - if (strchr(sep, ta->text[b_start]) != NULL) { - /* Add one to start to skip over separator */ - sel_start = index + 1; - } - } - - /* Search for next separator, if any */ - for (b_end = b_start; b_end < ta->text_len; - b_end = utf8_next(ta->text, ta->text_len, - b_end), - index++) { - if (strchr(sep, ta->text[b_end]) != NULL) { - sel_end = index; - break; - } - } - - if (sel_start < sel_end) { - textarea_select(ta, sel_start, sel_end); - return true; - } - - return false; -} - - -/** - * Normalises any line endings within the text, replacing CRLF or CR with - * LF as necessary. If the textarea is single line, then all linebreaks are - * converted into spaces. - * - * \param ta Text area - * \param b_start Byte offset to start at - * \param b_len Byte length to check - */ -void textarea_normalise_text(struct textarea *ta, - unsigned int b_start, unsigned int b_len) -{ - bool multi = (ta->flags & TEXTAREA_MULTILINE) ? true:false; - unsigned int index; - - /* Remove CR characters. If it's a CRLF pair delete it ot replace it - * with LF otherwise. - */ - for (index = 0; index < b_len; index++) { - if (ta->text[b_start + index] == '\r') { - if (b_start + index + 1 <= ta->text_len && - ta->text[b_start + index + 1] == '\n') { - ta->text_len--; - ta->text_utf8_len--; - memmove(ta->text + b_start + index, - ta->text + b_start + index + 1, - ta->text_len - b_start - index); - } - else - ta->text[b_start + index] = '\n'; - } - /* Replace newlines with spaces if this is a single line - * textarea. - */ - if (!multi && (ta->text[b_start + index] == '\n')) - ta->text[b_start + index] = ' '; - } - -} - /* exported interface, documented in textarea.h */ void textarea_get_dimensions(struct textarea *ta, int *width, int *height) -- cgit v1.2.3