From e65e41e2d6efccba983cd63aadfb10b5b4a935b2 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Sat, 23 May 2020 20:38:41 +0100 Subject: move content handler specific selection copying into handlers --- content/content_protected.h | 11 +- content/handlers/html/html.c | 1 + content/handlers/html/textselection.c | 227 ++++++++++++++++++++++++ content/handlers/html/textselection.h | 2 + content/handlers/text/textplain.c | 79 ++++++--- content/handlers/text/textplain.h | 14 -- desktop/selection.c | 314 ++-------------------------------- desktop/selection.h | 9 + 8 files changed, 317 insertions(+), 340 deletions(-) diff --git a/content/content_protected.h b/content/content_protected.h index 9a3654beb..02bc8614d 100644 --- a/content/content_protected.h +++ b/content/content_protected.h @@ -47,6 +47,7 @@ struct browser_window_features; struct textsearch_context; struct box; struct selection; +struct selection_string; typedef struct content_handler content_handler; @@ -112,7 +113,10 @@ struct content_handler { nserror (*textsearch_bounds)(struct content *c, unsigned start_idx, unsigned end_idx, struct box *start_ptr, struct box *end_ptr, struct rect *bounds_out); /** - * cause a region of the content to be marked invalid and hence redraw + * redraw an area of selected text + * + * The defined text selection will cause an area of the + * content to be marked as invalid and hence redrawn. * * \param c The content being redrawn * \param start_idx The start index of the text region to be redrawn @@ -121,6 +125,11 @@ struct content_handler { */ nserror (*textselection_redraw)(struct content *c, unsigned start_idx, unsigned end_idx); + /** + * copy selected text into selection string possibly with formatting + */ + nserror (*textselection_copy)(struct content *c, unsigned start_idx, unsigned end_idx, struct selection_string *selstr); + /** * create a selection object */ diff --git a/content/handlers/html/html.c b/content/handlers/html/html.c index 2a3cb1774..a8ae9e452 100644 --- a/content/handlers/html/html.c +++ b/content/handlers/html/html.c @@ -2336,6 +2336,7 @@ static const content_handler html_content_handler = { .textsearch_find = html_textsearch_find, .textsearch_bounds = html_textsearch_bounds, .textselection_redraw = html_textselection_redraw, + .textselection_copy = html_textselection_copy, .create_selection = html_create_selection, .no_share = true, }; diff --git a/content/handlers/html/textselection.c b/content/handlers/html/textselection.c index bbd55732e..11e9deeb4 100644 --- a/content/handlers/html/textselection.c +++ b/content/handlers/html/textselection.c @@ -277,6 +277,7 @@ html_create_selection(struct content *c, struct selection **sel_out) return NSERROR_OK; } +/* exported interface documented in html/textselection.h */ nserror html_textselection_redraw(struct content *c, unsigned start_idx, @@ -301,3 +302,229 @@ html_textselection_redraw(struct content *c, return NSERROR_OK; } + +/** + * Selection traversal routine for appending text to a string + * + * \param text pointer to text being added, or NULL for newline + * \param length length of text to be appended (bytes) + * \param box pointer to text box, or NULL if from textplain + * \param len_ctx Length conversion context + * \param handle selection string to append to + * \param whitespace_text whitespace to place before text for formatting + * may be NULL + * \param whitespace_length length of whitespace_text + * \return true iff successful and traversal should continue + */ +static bool +selection_copy(const char *text, + size_t length, + struct box *box, + const nscss_len_ctx *len_ctx, + struct selection_string *handle, + const char *whitespace_text, + size_t whitespace_length) +{ + bool add_space = false; + plot_font_style_t style; + plot_font_style_t *pstyle = NULL; + + /* add any whitespace which precedes the text from this box */ + if (whitespace_text != NULL && + whitespace_length > 0) { + if (!selection_string_append(whitespace_text, + whitespace_length, + false, + pstyle, + handle)) { + return false; + } + } + + if (box != NULL) { + /* HTML */ + add_space = (box->space != 0); + + if (box->style != NULL) { + /* Override default font style */ + font_plot_style_from_css(len_ctx, box->style, &style); + pstyle = &style; + } else { + /* If there's no style, there must be no text */ + assert(box->text == NULL); + } + } + + /* add the text from this box */ + if (!selection_string_append(text, length, add_space, pstyle, handle)) { + return false; + } + + return true; +} + + +/** + * Traverse the given box subtree, calling selection copy for all + * boxes that lie (partially) within the given range + * + * \param box box subtree + * \param len_ctx Length conversion context. + * \param start_idx start of range within textual representation (bytes) + * \param end_idx end of range + * \param handler handler function to call + * \param handle handle to pass + * \param before type of whitespace to place before next encountered text + * \param first whether this is the first box with text + * \param do_marker whether deal enter any marker box + * \return false iff traversal abandoned part-way through + */ +static bool +traverse_tree(struct box *box, + const nscss_len_ctx *len_ctx, + unsigned start_idx, + unsigned end_idx, + struct selection_string *selstr, + save_text_whitespace *before, + bool *first, + bool do_marker) +{ + struct box *child; + const char *whitespace_text = ""; + size_t whitespace_length = 0; + + assert(box); + + /* If selection starts inside marker */ + if (box->parent && + box->parent->list_marker == box && + !do_marker) { + /* set box to main list element */ + box = box->parent; + } + + /* If box has a list marker */ + if (box->list_marker) { + /* do the marker box before continuing with the rest of the + * list element */ + if (!traverse_tree(box->list_marker, + len_ctx, + start_idx, + end_idx, + selstr, + before, + first, + true)) { + return false; + } + } + + /* we can prune this subtree, it's after the selection */ + if (box->byte_offset >= end_idx) { + return true; + } + + /* read before calling the handler in case it modifies the tree */ + child = box->children; + + /* If nicely formatted output of the selected text is required, work + * out what whitespace should be placed before the next bit of text */ + if (before) { + save_text_solve_whitespace(box, + first, + before, + &whitespace_text, + &whitespace_length); + } else { + whitespace_text = NULL; + } + + if ((box->type != BOX_BR) && + !((box->type == BOX_FLOAT_LEFT || + box->type == BOX_FLOAT_RIGHT) && + !box->text)) { + unsigned start_offset; + unsigned end_offset; + + if (selected_part(box, + start_idx, + end_idx, + &start_offset, + &end_offset)) { + if (!selection_copy(box->text + start_offset, + min(box->length, end_offset) - start_offset, + box, + len_ctx, + selstr, + whitespace_text, + whitespace_length)) { + return false; + } + if (before) { + *first = false; + *before = WHITESPACE_NONE; + } + } + } + + /* find the first child that could lie partially within the selection; + * this is important at the top-levels of the tree for pruning subtrees + * that lie entirely before the selection */ + + if (child) { + struct box *next = child->next; + + while (next && next->byte_offset < start_idx) { + child = next; + next = child->next; + } + + while (child) { + /* read before calling the handler in case it modifies + * the tree */ + struct box *next = child->next; + + if (!traverse_tree(child, + len_ctx, + start_idx, + end_idx, + selstr, + before, + first, + false)) { + return false; + } + + child = next; + } + } + + return true; +} + +/* exported interface documented in html/textselection.h */ +nserror +html_textselection_copy(struct content *c, + unsigned start_idx, + unsigned end_idx, + struct selection_string *selstr) +{ + html_content *html = (html_content *)c; + save_text_whitespace before = WHITESPACE_NONE; + bool first = true; + bool res; + + res = traverse_tree(html->layout, + &html->len_ctx, + start_idx, + end_idx, + selstr, + &before, + &first, + false); + + if (res == false) { + return NSERROR_NOMEM; + } + return NSERROR_OK; +} diff --git a/content/handlers/html/textselection.h b/content/handlers/html/textselection.h index 0287f3c58..4a866b0b2 100644 --- a/content/handlers/html/textselection.h +++ b/content/handlers/html/textselection.h @@ -34,4 +34,6 @@ nserror html_create_selection(struct content *c, struct selection **sel_out); nserror html_textselection_redraw(struct content *c, unsigned start_idx, unsigned end_idx); +nserror html_textselection_copy(struct content *c, unsigned start_idx, unsigned end_idx, struct selection_string *selstr); + #endif diff --git a/content/handlers/text/textplain.c b/content/handlers/text/textplain.c index d8f61be89..275efda83 100644 --- a/content/handlers/text/textplain.c +++ b/content/handlers/text/textplain.c @@ -1513,6 +1513,43 @@ textplain_coords_from_range(struct content *c, } +/** + * Return a pointer to the raw UTF-8 data, as opposed to the reformatted + * text to fit the window width. Thus only hard newlines are preserved + * in the saved/copied text of a selection. + * + * \param[in] c content of type CONTENT_TEXTPLAIN + * \param[in] start starting byte offset within UTF-8 text + * \param[in] end ending byte offset + * \param[out] plen receives validated length + * \return pointer to text, or NULL if no text + */ +static char * +textplain_get_raw_data(struct content *c, + unsigned start, + unsigned end, + size_t *plen) +{ + textplain_content *text = (textplain_content *) c; + size_t utf8_size; + + assert(c != NULL); + + utf8_size = text->utf8_data_size; + + /* any text at all? */ + if (!utf8_size) return NULL; + + /* clamp to valid offset range */ + if (start >= utf8_size) start = utf8_size; + if (end >= utf8_size) end = utf8_size; + + *plen = end - start; + + return text->utf8_data + start; +} + + /** * get bounds of a free text search match */ @@ -1570,6 +1607,23 @@ textplain_textselection_redraw(struct content *c, return NSERROR_OK; } +static nserror +textplain_textselection_copy(struct content *c, + unsigned start_idx, + unsigned end_idx, + struct selection_string *selstr) +{ + const char *text; + size_t length; + bool res; + + text = textplain_get_raw_data(c, start_idx, end_idx, &length); + res = selection_string_append(text, length, false, NULL, selstr); + if (res == false) { + return NSERROR_NOMEM; + } + return NSERROR_OK; +} /** * plain text content handler table @@ -1593,6 +1647,7 @@ static const content_handler textplain_content_handler = { .textsearch_find = textplain_textsearch_find, .textsearch_bounds = textplain_textsearch_bounds, .textselection_redraw = textplain_textselection_redraw, + .textselection_copy = textplain_textselection_copy, .create_selection = textplain_create_selection, .no_share = true, }; @@ -1643,28 +1698,4 @@ size_t textplain_size(struct content *c) -/* exported interface documented in html/textplain.h */ -char * -textplain_get_raw_data(struct content *c, - unsigned start, - unsigned end, - size_t *plen) -{ - textplain_content *text = (textplain_content *) c; - size_t utf8_size; - assert(c != NULL); - - utf8_size = text->utf8_data_size; - - /* any text at all? */ - if (!utf8_size) return NULL; - - /* clamp to valid offset range */ - if (start >= utf8_size) start = utf8_size; - if (end >= utf8_size) end = utf8_size; - - *plen = end - start; - - return text->utf8_data + start; -} diff --git a/content/handlers/text/textplain.h b/content/handlers/text/textplain.h index 206c5831e..b94ee33d3 100644 --- a/content/handlers/text/textplain.h +++ b/content/handlers/text/textplain.h @@ -46,18 +46,4 @@ nserror textplain_init(void); size_t textplain_size(struct content *c); -/** - * Return a pointer to the raw UTF-8 data, as opposed to the reformatted - * text to fit the window width. Thus only hard newlines are preserved - * in the saved/copied text of a selection. - * - * \param[in] c content of type CONTENT_TEXTPLAIN - * \param[in] start starting byte offset within UTF-8 text - * \param[in] end ending byte offset - * \param[out] plen receives validated length - * \return pointer to text, or NULL if no text - */ -char *textplain_get_raw_data(struct content *c, unsigned start, unsigned end, size_t *plen); - - #endif diff --git a/desktop/selection.c b/desktop/selection.c index 11cd1fb06..d4fa7ca7d 100644 --- a/desktop/selection.c +++ b/desktop/selection.c @@ -57,11 +57,6 @@ #define SPACE_LEN(b) ((b->space == 0) ? 0 : 1) -struct rdw_info { - bool inited; - struct rect r; -}; - struct selection_string { char *buffer; size_t buffer_len; @@ -72,15 +67,6 @@ struct selection_string { }; -typedef bool (*seln_traverse_handler)(const char *text, - size_t length, - struct box *box, - const nscss_len_ctx *len_ctx, - void *handle, - const char *whitespace_text, - size_t whitespace_length); - - /** * Label each text box in the given box subtree with its position * in a textual representation of the content. @@ -112,206 +98,6 @@ static unsigned selection_label_subtree(struct box *box, unsigned idx) } -/** - * Tests whether a text box lies partially within the given range of - * byte offsets, returning the start and end indexes of the bytes - * that are enclosed. - * - * \param box box to be tested - * \param start_idx byte offset of start of range - * \param end_idx byte offset of end of range - * \param start_offset receives the start offset of the selected part - * \param end_offset receives the end offset of the selected part - * \return true iff the range encloses at least part of the box - */ -static bool -selected_part(struct box *box, - unsigned start_idx, - unsigned end_idx, - unsigned *start_offset, - unsigned *end_offset) -{ - size_t box_length = box->length + SPACE_LEN(box); - - if (box_length > 0) { - if ((box->byte_offset >= start_idx) && - (box->byte_offset + box_length <= end_idx)) { - - /* fully enclosed */ - *start_offset = 0; - *end_offset = box_length; - return true; - } else if ((box->byte_offset + box_length > start_idx) && - (box->byte_offset < end_idx)) { - /* partly enclosed */ - int offset = 0; - int len; - - if (box->byte_offset < start_idx) { - offset = start_idx - box->byte_offset; - } - - len = box_length - offset; - - if (box->byte_offset + box_length > end_idx) { - len = end_idx - (box->byte_offset + offset); - } - - *start_offset = offset; - *end_offset = offset + len; - - return true; - } - } - return false; -} - - -/** - * Traverse the given box subtree, calling the handler function (with - * its handle) for all boxes that lie (partially) within the given - * range - * - * \param box box subtree - * \param len_ctx Length conversion context. - * \param start_idx start of range within textual representation (bytes) - * \param end_idx end of range - * \param handler handler function to call - * \param handle handle to pass - * \param before type of whitespace to place before next encountered text - * \param first whether this is the first box with text - * \param do_marker whether deal enter any marker box - * \return false iff traversal abandoned part-way through - */ -static bool -traverse_tree(struct box *box, - const nscss_len_ctx *len_ctx, - unsigned start_idx, - unsigned end_idx, - seln_traverse_handler handler, - void *handle, - save_text_whitespace *before, - bool *first, - bool do_marker) -{ - struct box *child; - const char *whitespace_text = ""; - size_t whitespace_length = 0; - - assert(box); - - /* If selection starts inside marker */ - if (box->parent && - box->parent->list_marker == box && - !do_marker) { - /* set box to main list element */ - box = box->parent; - } - - /* If box has a list marker */ - if (box->list_marker) { - /* do the marker box before continuing with the rest of the - * list element */ - if (!traverse_tree(box->list_marker, - len_ctx, - start_idx, - end_idx, - handler, - handle, - before, - first, - true)) { - return false; - } - } - - /* we can prune this subtree, it's after the selection */ - if (box->byte_offset >= end_idx) { - return true; - } - - /* read before calling the handler in case it modifies the tree */ - child = box->children; - - /* If nicely formatted output of the selected text is required, work - * out what whitespace should be placed before the next bit of text */ - if (before) { - save_text_solve_whitespace(box, - first, - before, - &whitespace_text, - &whitespace_length); - } else { - whitespace_text = NULL; - } - - if ((box->type != BOX_BR) && - !((box->type == BOX_FLOAT_LEFT || - box->type == BOX_FLOAT_RIGHT) && - !box->text)) { - unsigned start_offset; - unsigned end_offset; - - if (selected_part(box, - start_idx, - end_idx, - &start_offset, - &end_offset)) { - if (!handler(box->text + start_offset, - min(box->length, end_offset) - start_offset, - box, - len_ctx, - handle, - whitespace_text, - whitespace_length)) { - return false; - } - if (before) { - *first = false; - *before = WHITESPACE_NONE; - } - } - } - - /* find the first child that could lie partially within the selection; - * this is important at the top-levels of the tree for pruning subtrees - * that lie entirely before the selection */ - - if (child) { - struct box *next = child->next; - - while (next && next->byte_offset < start_idx) { - child = next; - next = child->next; - } - - while (child) { - /* read before calling the handler in case it modifies - * the tree */ - struct box *next = child->next; - - if (!traverse_tree(child, - len_ctx, - start_idx, - end_idx, - handler, - handle, - before, - first, - false)) { - return false; - } - - child = next; - } - } - - return true; -} - - - - /** * Redraws the given range of text. * @@ -404,35 +190,22 @@ static void selection_set_end(struct selection *s, unsigned offset) * \return false iff traversal abandoned part-way through */ static bool -selection_traverse(struct selection *s, - seln_traverse_handler handler, - void *handle) +selection_copy(struct selection *s, struct selection_string *selstr) { - save_text_whitespace before = WHITESPACE_NONE; - bool first = true; - const char *text; - size_t length; - - if (!s->defined) { - return true; /* easy case, nothing to do */ - } + nserror res; - if (s->root) { - /* HTML */ - return traverse_tree(s->root, &s->len_ctx, - s->start_idx, s->end_idx, - handler, handle, - &before, &first, false); + if (s->c->handler->textselection_copy != NULL) { + res = s->c->handler->textselection_copy(s->c, + s->start_idx, + s->end_idx, + selstr); + } else { + res = NSERROR_NOT_IMPLEMENTED; } - /* Text */ - text = textplain_get_raw_data(s->c, s->start_idx, s->end_idx, &length); - - if (text && - !handler(text, length, NULL, NULL, handle, NULL, 0)) { + if (res != NSERROR_OK) { return false; } - return true; } @@ -447,7 +220,7 @@ selection_traverse(struct selection *s, * \param sel_string string to append to, may be resized * \return true iff successful */ -static bool +bool selection_string_append(const char *text, size_t length, bool space, @@ -509,67 +282,6 @@ selection_string_append(const char *text, } -/** - * Selection traversal routine for appending text to a string - * - * \param text pointer to text being added, or NULL for newline - * \param length length of text to be appended (bytes) - * \param box pointer to text box, or NULL if from textplain - * \param len_ctx Length conversion context - * \param handle selection string to append to - * \param whitespace_text whitespace to place before text for formatting - * may be NULL - * \param whitespace_length length of whitespace_text - * \return true iff successful and traversal should continue - */ -static bool -selection_copy_handler(const char *text, - size_t length, - struct box *box, - const nscss_len_ctx *len_ctx, - void *handle, - const char *whitespace_text, - size_t whitespace_length) -{ - bool add_space = false; - plot_font_style_t style; - plot_font_style_t *pstyle = NULL; - - /* add any whitespace which precedes the text from this box */ - if (whitespace_text != NULL && - whitespace_length > 0) { - if (!selection_string_append(whitespace_text, - whitespace_length, - false, - pstyle, - handle)) { - return false; - } - } - - if (box != NULL) { - /* HTML */ - add_space = (box->space != 0); - - if (box->style != NULL) { - /* Override default font style */ - font_plot_style_from_css(len_ctx, box->style, &style); - pstyle = &style; - } else { - /* If there's no style, there must be no text */ - assert(box->text == NULL); - } - } - - /* add the text from this box */ - if (!selection_string_append(text, length, add_space, pstyle, handle)) { - return false; - } - - return true; -} - - /* exported interface documented in desktop/selection.h */ struct selection *selection_create(struct content *c, bool is_html) { @@ -821,7 +533,7 @@ char *selection_get_copy(struct selection *s) if (s == NULL || !s->defined) return NULL; - if (!selection_traverse(s, selection_copy_handler, &sel_string)) { + if (!selection_copy(s, &sel_string)) { free(sel_string.buffer); free(sel_string.styles); return NULL; @@ -849,7 +561,7 @@ bool selection_copy_to_clipboard(struct selection *s) return false; } - if (!selection_traverse(s, selection_copy_handler, &sel_string)) { + if (!selection_copy(s, &sel_string)) { free(sel_string.buffer); free(sel_string.styles); return false; diff --git a/desktop/selection.h b/desktop/selection.h index cfaf59baf..cc8d8da4e 100644 --- a/desktop/selection.h +++ b/desktop/selection.h @@ -29,6 +29,8 @@ struct box; struct browser_window; +struct plot_font_style; +struct selection_string; typedef enum { DRAG_NONE, @@ -218,4 +220,11 @@ char *selection_get_copy(struct selection *s); */ bool selection_highlighted(const struct selection *s, unsigned start, unsigned end, unsigned *start_idx, unsigned *end_idx); +bool +selection_string_append(const char *text, + size_t length, + bool space, + struct plot_font_style *style, + struct selection_string *sel_string); + #endif -- cgit v1.2.3