move content handler specific selection copying into handlers

This commit is contained in:
Vincent Sanders 2020-05-23 20:38:41 +01:00
parent 36b9262e14
commit e65e41e2d6
8 changed files with 317 additions and 340 deletions

View File

@ -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
*/

View File

@ -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,
};

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
nserror res;
if (!s->defined) {
return true; /* easy case, nothing to do */
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;
}
if (s->root) {
/* HTML */
return traverse_tree(s->root, &s->len_ctx,
s->start_idx, s->end_idx,
handler, handle,
&before, &first, false);
}
/* 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;

View File

@ -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