mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-26 22:09:43 +03:00
move content handler specific selection copying into handlers
This commit is contained in:
parent
36b9262e14
commit
e65e41e2d6
@ -47,6 +47,7 @@ struct browser_window_features;
|
|||||||
struct textsearch_context;
|
struct textsearch_context;
|
||||||
struct box;
|
struct box;
|
||||||
struct selection;
|
struct selection;
|
||||||
|
struct selection_string;
|
||||||
|
|
||||||
typedef struct content_handler content_handler;
|
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);
|
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 c The content being redrawn
|
||||||
* \param start_idx The start index of the text region to be 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);
|
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
|
* create a selection object
|
||||||
*/
|
*/
|
||||||
|
@ -2336,6 +2336,7 @@ static const content_handler html_content_handler = {
|
|||||||
.textsearch_find = html_textsearch_find,
|
.textsearch_find = html_textsearch_find,
|
||||||
.textsearch_bounds = html_textsearch_bounds,
|
.textsearch_bounds = html_textsearch_bounds,
|
||||||
.textselection_redraw = html_textselection_redraw,
|
.textselection_redraw = html_textselection_redraw,
|
||||||
|
.textselection_copy = html_textselection_copy,
|
||||||
.create_selection = html_create_selection,
|
.create_selection = html_create_selection,
|
||||||
.no_share = true,
|
.no_share = true,
|
||||||
};
|
};
|
||||||
|
@ -277,6 +277,7 @@ html_create_selection(struct content *c, struct selection **sel_out)
|
|||||||
return NSERROR_OK;
|
return NSERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* exported interface documented in html/textselection.h */
|
||||||
nserror
|
nserror
|
||||||
html_textselection_redraw(struct content *c,
|
html_textselection_redraw(struct content *c,
|
||||||
unsigned start_idx,
|
unsigned start_idx,
|
||||||
@ -301,3 +302,229 @@ html_textselection_redraw(struct content *c,
|
|||||||
|
|
||||||
return NSERROR_OK;
|
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;
|
||||||
|
}
|
||||||
|
@ -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_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
|
#endif
|
||||||
|
@ -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
|
* get bounds of a free text search match
|
||||||
*/
|
*/
|
||||||
@ -1570,6 +1607,23 @@ textplain_textselection_redraw(struct content *c,
|
|||||||
return NSERROR_OK;
|
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
|
* plain text content handler table
|
||||||
@ -1593,6 +1647,7 @@ static const content_handler textplain_content_handler = {
|
|||||||
.textsearch_find = textplain_textsearch_find,
|
.textsearch_find = textplain_textsearch_find,
|
||||||
.textsearch_bounds = textplain_textsearch_bounds,
|
.textsearch_bounds = textplain_textsearch_bounds,
|
||||||
.textselection_redraw = textplain_textselection_redraw,
|
.textselection_redraw = textplain_textselection_redraw,
|
||||||
|
.textselection_copy = textplain_textselection_copy,
|
||||||
.create_selection = textplain_create_selection,
|
.create_selection = textplain_create_selection,
|
||||||
.no_share = true,
|
.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;
|
|
||||||
}
|
|
||||||
|
@ -46,18 +46,4 @@ nserror textplain_init(void);
|
|||||||
size_t textplain_size(struct content *c);
|
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
|
#endif
|
||||||
|
@ -57,11 +57,6 @@
|
|||||||
#define SPACE_LEN(b) ((b->space == 0) ? 0 : 1)
|
#define SPACE_LEN(b) ((b->space == 0) ? 0 : 1)
|
||||||
|
|
||||||
|
|
||||||
struct rdw_info {
|
|
||||||
bool inited;
|
|
||||||
struct rect r;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct selection_string {
|
struct selection_string {
|
||||||
char *buffer;
|
char *buffer;
|
||||||
size_t buffer_len;
|
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
|
* Label each text box in the given box subtree with its position
|
||||||
* in a textual representation of the content.
|
* 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.
|
* 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
|
* \return false iff traversal abandoned part-way through
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
selection_traverse(struct selection *s,
|
selection_copy(struct selection *s, struct selection_string *selstr)
|
||||||
seln_traverse_handler handler,
|
|
||||||
void *handle)
|
|
||||||
{
|
{
|
||||||
save_text_whitespace before = WHITESPACE_NONE;
|
nserror res;
|
||||||
bool first = true;
|
|
||||||
const char *text;
|
|
||||||
size_t length;
|
|
||||||
|
|
||||||
if (!s->defined) {
|
if (s->c->handler->textselection_copy != NULL) {
|
||||||
return true; /* easy case, nothing to do */
|
res = s->c->handler->textselection_copy(s->c,
|
||||||
|
s->start_idx,
|
||||||
|
s->end_idx,
|
||||||
|
selstr);
|
||||||
|
} else {
|
||||||
|
res = NSERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->root) {
|
if (res != NSERROR_OK) {
|
||||||
/* 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)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +220,7 @@ selection_traverse(struct selection *s,
|
|||||||
* \param sel_string string to append to, may be resized
|
* \param sel_string string to append to, may be resized
|
||||||
* \return true iff successful
|
* \return true iff successful
|
||||||
*/
|
*/
|
||||||
static bool
|
bool
|
||||||
selection_string_append(const char *text,
|
selection_string_append(const char *text,
|
||||||
size_t length,
|
size_t length,
|
||||||
bool space,
|
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 */
|
/* exported interface documented in desktop/selection.h */
|
||||||
struct selection *selection_create(struct content *c, bool is_html)
|
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)
|
if (s == NULL || !s->defined)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (!selection_traverse(s, selection_copy_handler, &sel_string)) {
|
if (!selection_copy(s, &sel_string)) {
|
||||||
free(sel_string.buffer);
|
free(sel_string.buffer);
|
||||||
free(sel_string.styles);
|
free(sel_string.styles);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -849,7 +561,7 @@ bool selection_copy_to_clipboard(struct selection *s)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selection_traverse(s, selection_copy_handler, &sel_string)) {
|
if (!selection_copy(s, &sel_string)) {
|
||||||
free(sel_string.buffer);
|
free(sel_string.buffer);
|
||||||
free(sel_string.styles);
|
free(sel_string.styles);
|
||||||
return false;
|
return false;
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
struct box;
|
struct box;
|
||||||
struct browser_window;
|
struct browser_window;
|
||||||
|
struct plot_font_style;
|
||||||
|
struct selection_string;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DRAG_NONE,
|
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_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
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user