split selection redraw into content handler specific implementations

This commit is contained in:
Vincent Sanders 2020-05-23 18:39:25 +01:00
parent 0432d95561
commit 36b9262e14
8 changed files with 500 additions and 226 deletions

View File

@ -111,6 +111,16 @@ 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
*
* \param c The content being redrawn
* \param start_idx The start index of the text region to be redrawn
* \param end_idx The end index of teh text region to be redrawn
* \return NSERROR_OK on success else error code
*/
nserror (*textselection_redraw)(struct content *c, unsigned start_idx, unsigned end_idx);
/**
* create a selection object
*/

View File

@ -20,4 +20,5 @@ S_HTML := box_construct.c \
redraw.c \
redraw_border.c \
script.c \
table.c
table.c \
textselection.c

View File

@ -70,6 +70,7 @@
#include "html/form_internal.h"
#include "html/imagemap.h"
#include "html/layout.h"
#include "html/textselection.h"
#define CHUNK 4096
@ -2303,26 +2304,6 @@ html_textsearch_bounds(struct content *c,
}
/**
* create a selection object suitable for this content
*/
static nserror
html_create_selection(struct content *c, struct selection **sel_out)
{
html_content *html = (html_content *)c;
struct selection *sel;
sel = selection_create(c, true);
if (sel == NULL) {
return NSERROR_NOMEM;
}
selection_init(sel, html->layout, &html->len_ctx);
*sel_out = sel;
return NSERROR_OK;
}
/**
* HTML content handler function table
*/
@ -2354,6 +2335,7 @@ static const content_handler html_content_handler = {
.saw_insecure_objects = html_saw_insecure_objects,
.textsearch_find = html_textsearch_find,
.textsearch_bounds = html_textsearch_bounds,
.textselection_redraw = html_textselection_redraw,
.create_selection = html_create_selection,
.no_share = true,
};

View File

@ -0,0 +1,303 @@
/*
* Copyright 2006 James Bursa <bursa@users.sourceforge.net>
* Copyright 2006 Richard Wilson <info@tinct.net>
* Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
* Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
* NetSurf is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* NetSurf is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* \file
* implementation of user interaction with a CONTENT_HTML.
*/
#include <assert.h>
#include <stdbool.h>
#include <dom/dom.h>
#include "utils/corestrings.h"
#include "utils/messages.h"
#include "utils/utils.h"
#include "utils/log.h"
#include "utils/nsoption.h"
#include "netsurf/content.h"
#include "netsurf/browser_window.h"
#include "netsurf/mouse.h"
#include "netsurf/misc.h"
#include "netsurf/layout.h"
#include "netsurf/keypress.h"
#include "content/hlcache.h"
#include "content/textsearch.h"
#include "desktop/frames.h"
#include "desktop/scrollbar.h"
#include "desktop/selection.h"
#include "desktop/textarea.h"
#include "javascript/js.h"
#include "desktop/gui_internal.h"
#include "desktop/save_text.h"
#include "html/box.h"
#include "html/box_textarea.h"
#include "html/box_inspect.h"
#include "html/font.h"
#include "html/form_internal.h"
#include "html/private.h"
#include "html/imagemap.h"
#include "html/textselection.h"
#define SPACE_LEN(b) ((b->space == 0) ? 0 : 1)
struct rdw_info {
bool inited;
struct rect r;
};
/**
* 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 start_idx start of range within textual representation (bytes)
* \param end_idx end of range
* \param rdwi redraw range to fill in
* \param do_marker whether deal enter any marker box
* \return false iff traversal abandoned part-way through
*/
static bool
coords_from_range(struct box *box,
unsigned start_idx,
unsigned end_idx,
struct rdw_info *rdwi,
bool do_marker)
{
struct box *child;
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 (!coords_from_range(box->list_marker,
start_idx,
end_idx,
rdwi,
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 ((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)) {
int width, height;
int x, y;
/**
* \todo it should be possible to reduce the redrawn
* area using the offsets
*/
box_coords(box, &x, &y);
width = box->padding[LEFT] + box->width + box->padding[RIGHT];
height = box->padding[TOP] + box->height + box->padding[BOTTOM];
if ((box->type == BOX_TEXT) &&
(box->space != 0)) {
width += box->space;
}
if (rdwi->inited) {
if (x < rdwi->r.x0) {
rdwi->r.x0 = x;
}
if (y < rdwi->r.y0) {
rdwi->r.y0 = y;
}
if (x + width > rdwi->r.x1) {
rdwi->r.x1 = x + width;
}
if (y + height > rdwi->r.y1) {
rdwi->r.y1 = y + height;
}
} else {
rdwi->inited = true;
rdwi->r.x0 = x;
rdwi->r.y0 = y;
rdwi->r.x1 = x + width;
rdwi->r.y1 = y + height;
}
}
}
/* 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 (!coords_from_range(child,
start_idx,
end_idx,
rdwi,
false)) {
return false;
}
child = next;
}
}
return true;
}
/**
* create a selection object suitable for this content
*/
nserror
html_create_selection(struct content *c, struct selection **sel_out)
{
html_content *html = (html_content *)c;
struct selection *sel;
sel = selection_create(c, true);
if (sel == NULL) {
return NSERROR_NOMEM;
}
selection_init(sel, html->layout, &html->len_ctx);
*sel_out = sel;
return NSERROR_OK;
}
nserror
html_textselection_redraw(struct content *c,
unsigned start_idx,
unsigned end_idx)
{
html_content *html = (html_content *)c;
struct rdw_info rdw;
rdw.inited = false;
if (!coords_from_range(html->layout, start_idx, end_idx, &rdw, false)) {
return NSERROR_BAD_PARAMETER;
}
if (rdw.inited) {
content__request_redraw(c,
rdw.r.x0,
rdw.r.y0,
rdw.r.x1 - rdw.r.x0,
rdw.r.y1 - rdw.r.y0);
}
return NSERROR_OK;
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
* NetSurf is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* NetSurf is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* \file
* HTML text selection handling
*/
#ifndef NETSURF_HTML_TEXTSELECTION_H
#define NETSURF_HTML_TEXTSELECTION_H
struct content;
struct selection;
/**
* create a selection object suitable for this content
*/
nserror html_create_selection(struct content *c, struct selection **sel_out);
nserror html_textselection_redraw(struct content *c, unsigned start_idx, unsigned end_idx);
#endif

View File

@ -1452,111 +1452,15 @@ textplain_textsearch_find(struct content *c,
/**
* get bounds of a free text search match
* Given a range of byte offsets within a UTF8 textplain content,
* return a box that fully encloses the text
*
* \param[in] c content of type CONTENT_TEXTPLAIN
* \param[in] start byte offset of start of text range
* \param[in] end byte offset of end
* \param[out] r rectangle to be completed
*/
static nserror
textplain_textsearch_bounds(struct content *c,
unsigned start_idx,
unsigned end_idx,
struct box *start_box,
struct box *end_box,
struct rect *bounds)
{
textplain_coords_from_range(c, start_idx, end_idx, bounds);
return NSERROR_OK;
}
/**
* create a selection object suitable for this content
*/
static nserror
textplain_create_selection(struct content *c, struct selection **sel_out)
{
struct selection *sel;
sel = selection_create(c, false);
if (sel == NULL) {
return NSERROR_NOMEM;
}
selection_init(sel, NULL, NULL);
*sel_out = sel;
return NSERROR_OK;
}
/**
* plain text content handler table
*/
static const content_handler textplain_content_handler = {
.fini = textplain_fini,
.create = textplain_create,
.process_data = textplain_process_data,
.data_complete = textplain_convert,
.reformat = textplain_reformat,
.destroy = textplain_destroy,
.mouse_track = textplain_mouse_track,
.mouse_action = textplain_mouse_action,
.keypress = textplain_keypress,
.redraw = textplain_redraw,
.open = textplain_open,
.close = textplain_close,
.get_selection = textplain_get_selection,
.clone = textplain_clone,
.type = textplain_content_type,
.textsearch_find = textplain_textsearch_find,
.textsearch_bounds = textplain_textsearch_bounds,
.create_selection = textplain_create_selection,
.no_share = true,
};
/* exported interface documented in html/textplain.h */
nserror textplain_init(void)
{
lwc_error lerror;
nserror error;
lerror = lwc_intern_string("Windows-1252",
SLEN("Windows-1252"),
&textplain_default_charset);
if (lerror != lwc_error_ok) {
return NSERROR_NOMEM;
}
error = content_factory_register_handler("text/plain",
&textplain_content_handler);
if (error != NSERROR_OK) {
lwc_string_unref(textplain_default_charset);
}
error = content_factory_register_handler("application/json",
&textplain_content_handler);
if (error != NSERROR_OK) {
lwc_string_unref(textplain_default_charset);
}
return error;
}
/* exported interface documented in html/textplain.h */
size_t textplain_size(struct content *c)
{
textplain_content *text = (textplain_content *) c;
assert(c != NULL);
return text->utf8_data_size;
}
/* exported interface documented in html/textplain.h */
void
static void
textplain_coords_from_range(struct content *c,
unsigned start,
unsigned end,
@ -1609,6 +1513,136 @@ textplain_coords_from_range(struct content *c,
}
/**
* get bounds of a free text search match
*/
static nserror
textplain_textsearch_bounds(struct content *c,
unsigned start_idx,
unsigned end_idx,
struct box *start_box,
struct box *end_box,
struct rect *bounds)
{
textplain_coords_from_range(c, start_idx, end_idx, bounds);
return NSERROR_OK;
}
/**
* create a selection object suitable for this content
*/
static nserror
textplain_create_selection(struct content *c, struct selection **sel_out)
{
struct selection *sel;
sel = selection_create(c, false);
if (sel == NULL) {
return NSERROR_NOMEM;
}
selection_init(sel, NULL, NULL);
*sel_out = sel;
return NSERROR_OK;
}
/**
* invalidate a region based on offsets into the text cauing a redraw
*/
static nserror
textplain_textselection_redraw(struct content *c,
unsigned start_idx,
unsigned end_idx)
{
struct rect r;
if (end_idx <= start_idx) {
return NSERROR_BAD_PARAMETER;
}
textplain_coords_from_range(c, start_idx, end_idx, &r);
content__request_redraw(c, r.x0, r.y0, r.x1 - r.x0, r.y1 - r.y0);
return NSERROR_OK;
}
/**
* plain text content handler table
*/
static const content_handler textplain_content_handler = {
.fini = textplain_fini,
.create = textplain_create,
.process_data = textplain_process_data,
.data_complete = textplain_convert,
.reformat = textplain_reformat,
.destroy = textplain_destroy,
.mouse_track = textplain_mouse_track,
.mouse_action = textplain_mouse_action,
.keypress = textplain_keypress,
.redraw = textplain_redraw,
.open = textplain_open,
.close = textplain_close,
.get_selection = textplain_get_selection,
.clone = textplain_clone,
.type = textplain_content_type,
.textsearch_find = textplain_textsearch_find,
.textsearch_bounds = textplain_textsearch_bounds,
.textselection_redraw = textplain_textselection_redraw,
.create_selection = textplain_create_selection,
.no_share = true,
};
/* exported interface documented in html/textplain.h */
nserror textplain_init(void)
{
lwc_error lerror;
nserror error;
lerror = lwc_intern_string("Windows-1252",
SLEN("Windows-1252"),
&textplain_default_charset);
if (lerror != lwc_error_ok) {
return NSERROR_NOMEM;
}
error = content_factory_register_handler("text/plain",
&textplain_content_handler);
if (error != NSERROR_OK) {
lwc_string_unref(textplain_default_charset);
}
error = content_factory_register_handler("application/json",
&textplain_content_handler);
if (error != NSERROR_OK) {
lwc_string_unref(textplain_default_charset);
}
return error;
}
/* exported interface documented in html/textplain.h */
size_t textplain_size(struct content *c)
{
textplain_content *text = (textplain_content *) c;
assert(c != NULL);
return text->utf8_data_size;
}
/* exported interface documented in html/textplain.h */
char *
textplain_get_raw_data(struct content *c,

View File

@ -46,19 +46,6 @@ nserror textplain_init(void);
size_t textplain_size(struct content *c);
/**
* Given a range of byte offsets within a UTF8 textplain content,
* return a box that fully encloses the text
*
* \param[in] c content of type CONTENT_TEXTPLAIN
* \param[in] start byte offset of start of text range
* \param[in] end byte offset of end
* \param[out] r rectangle to be completed
*/
void textplain_coords_from_range(struct content *c,
unsigned start, unsigned end, struct rect *r);
/**
* 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

View File

@ -310,63 +310,6 @@ traverse_tree(struct box *box,
}
/**
* Selection traversal handler for redrawing the screen when the selection
* has been altered.
*
* \param text pointer to text string
* \param length length of text to be appended (bytes)
* \param box pointer to text box being (partially) added
* \param handle unused handle, we don't need one
* \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
redraw_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)
{
struct rdw_info *r = (struct rdw_info*)handle;
int width, height;
int x, y;
if (!box) {
return true;
}
/* \todo - it should be possible to reduce the redrawn area by
* considering the 'text', 'length' and 'space' parameters */
box_coords(box, &x, &y);
width = box->padding[LEFT] + box->width + box->padding[RIGHT];
height = box->padding[TOP] + box->height + box->padding[BOTTOM];
if ((box->type == BOX_TEXT) &&
(box->space != 0)) {
width += box->space;
}
if (r->inited) {
if (x < r->r.x0) r->r.x0 = x;
if (y < r->r.y0) r->r.y0 = y;
if (x + width > r->r.x1) r->r.x1 = x + width;
if (y + height > r->r.y1) r->r.y1 = y + height;
} else {
r->inited = true;
r->r.x0 = x;
r->r.y0 = y;
r->r.x1 = x + width;
r->r.y1 = y + height;
}
return true;
}
/**
@ -376,43 +319,20 @@ redraw_handler(const char *text,
* \param start_idx start offset (bytes) within the textual representation
* \param end_idx end offset (bytes) within the textual representation
*/
static void
static nserror
selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx)
{
struct rdw_info rdw;
nserror res;
assert(end_idx >= start_idx);
rdw.inited = false;
if (s->root) {
if (!traverse_tree(s->root,
&s->len_ctx,
start_idx,
end_idx,
redraw_handler,
&rdw,
NULL,
NULL,
false))
return;
if (s->c->handler->textselection_redraw != NULL) {
res = s->c->handler->textselection_redraw(s->c,
start_idx,
end_idx);
} else {
if ((s->is_html == false) &&
(end_idx > start_idx)) {
textplain_coords_from_range(s->c,
start_idx,
end_idx,
&rdw.r);
rdw.inited = true;
}
res = NSERROR_NOT_IMPLEMENTED;
}
if (rdw.inited) {
content__request_redraw(s->c,
rdw.r.x0,
rdw.r.y0,
rdw.r.x1 - rdw.r.x0,
rdw.r.y1 - rdw.r.y0);
}
return res;
}