From dbfdafdf1820a95995c3b260e147c55125468874 Mon Sep 17 00:00:00 2001 From: Adrian Lees Date: Wed, 15 Feb 2006 23:09:55 +0000 Subject: [project @ 2006-02-15 23:09:53 by adrianl] Extend text selection, copying, saving and searching code to handle textplain contents; modified textplain code to accept other line terminators svn path=/import/netsurf/; revision=2081 --- render/box.c | 27 ++++- render/box.h | 7 ++ render/html.h | 14 +++ render/html_redraw.c | 164 +++++++++++++++++++++------- render/textplain.c | 294 +++++++++++++++++++++++++++++++++++++++++++++------ render/textplain.h | 23 +++- 6 files changed, 456 insertions(+), 73 deletions(-) (limited to 'render') diff --git a/render/box.c b/render/box.c index e527cdde3..5ceb0376c 100644 --- a/render/box.c +++ b/render/box.c @@ -47,8 +47,9 @@ struct box * box_create(struct css_style *style, struct box *box; box = talloc(context, struct box); - if (!box) + if (!box) { return 0; + } box->type = BOX_INLINE; box->style = style; @@ -231,6 +232,27 @@ void box_coords(struct box *box, int *x, int *y) } +/** + * Find the bounds of a box. + * + * \param box the box to calculate bounds of + * \param r receives bounds + */ + +void box_bounds(struct box *box, struct rect *r) +{ + int width, height; + + box_coords(box, &r->x0, &r->y0); + + width = box->padding[LEFT] + box->width + box->padding[RIGHT]; + height = box->padding[TOP] + box->height + box->padding[BOTTOM]; + + r->x1 = r->x0 + width; + r->y1 = r->y0 + height; +} + + /** * Find the boxes at a point. * @@ -244,7 +266,7 @@ void box_coords(struct box *box, int *x, int *y) * \param content updated to content of object that returned box is in, if any * \return box at given point, or 0 if none found * - * To find all the boxes in the heirarchy at a certain point, use code like + * To find all the boxes in the hierarchy at a certain point, use code like * this: * \code * struct box *box = top_of_document_to_search; @@ -469,6 +491,7 @@ void box_dump(struct box *box, unsigned int depth) default: fprintf(stderr, "Unknown box type "); } + fprintf(stderr, "ofst %d", box->byte_offset); if (box->text) fprintf(stderr, "'%.*s' ", (int) box->length, box->text); if (box->space) diff --git a/render/box.h b/render/box.h index 92a48379c..3c62ecffe 100644 --- a/render/box.h +++ b/render/box.h @@ -96,6 +96,12 @@ typedef enum { BOX_INLINE_END } box_type; +struct rect { + int x0, y0; + int x1, y1; +}; + + /** Node in box tree. All dimensions are in pixels. */ struct box { /** Type of box. */ @@ -254,6 +260,7 @@ void box_unlink_and_free(struct box *box); void box_free(struct box *box); void box_free_box(struct box *box); void box_free_object_params(struct object_params *op); +void box_bounds(struct box *box, struct rect *r); void box_coords(struct box *box, int *x, int *y); struct box *box_at_point(struct box *box, int x, int y, int *box_x, int *box_y, diff --git a/render/html.h b/render/html.h index 959624d35..8ad0a7742 100644 --- a/render/html.h +++ b/render/html.h @@ -20,6 +20,7 @@ #include "netsurf/css/css.h" struct box; +struct rect; struct browser_window; struct content; struct form_successful_control; @@ -125,4 +126,17 @@ bool html_redraw(struct content *c, int x, int y, int clip_x0, int clip_y0, int clip_x1, int clip_y1, float scale, unsigned long background_colour); + +/* redraw a short text string, complete with highlighting + (for selection/search) and ghost caret */ + +bool text_redraw(const char *utf8_text, size_t utf8_len, + size_t offset, bool space, + struct css_style *style, + int x, int y, + struct rect *clip, + int height, + float scale, colour current_background_color, + bool excluded); + #endif diff --git a/render/html_redraw.c b/render/html_redraw.c index df7d5bfbb..820a56213 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -18,6 +18,7 @@ #include "netsurf/desktop/gui.h" #include "netsurf/desktop/plotters.h" #include "netsurf/desktop/selection.h" +#include "netsurf/desktop/textinput.h" #include "netsurf/render/box.h" #include "netsurf/render/font.h" #include "netsurf/render/form.h" @@ -34,6 +35,8 @@ static bool html_redraw_box(struct box *box, static bool html_redraw_text_box(struct box *box, int x, int y, int x0, int y0, int x1, int y1, float scale, colour current_background_color); +static bool html_redraw_caret(struct caret *caret, + os_colour current_background_color, float scale); static bool html_redraw_borders(struct box *box, int x_parent, int y_parent, int padding_width, int padding_height, float scale); static bool html_redraw_border_plot(int i, int *p, colour c, @@ -369,26 +372,77 @@ bool html_redraw_box(struct box *box, bool html_redraw_text_box(struct box *box, int x, int y, int x0, int y0, int x1, int y1, float scale, colour current_background_color) +{ + bool excluded = (box->object != NULL); + struct rect clip; + + clip.x0 = x0; + clip.y0 = y0; + clip.x1 = x1; + clip.y1 = y1; + + if (!text_redraw(box->text, box->length, box->byte_offset, + box->space, box->style, x, y, + &clip, box->height, scale, + current_background_color, excluded)) + return false; + + /* does this textbox contain the ghost caret? */ + if (ghost_caret.defined && box == ghost_caret.text_box) { + + if (!html_redraw_caret(&ghost_caret, current_background_color, scale)) + return false; + } + return true; +} + + +/** + * Redraw a short text string, complete with highlighting + * (for selection/search) and ghost caret + * + * \param utf8_text pointer to UTF-8 text string + * \param utf8_len length of string, in bytes + * \param offset byte offset within textual representation + * \param space indicates whether string is followed by a space + * \param style text style to use + * \param x x ordinate at which to plot text + * \param y y ordinate at which to plot text + * \param clip pointer to current clip rectangle + * \param height height of text string + * \param scale current display scale (1.0 = 100%) + * \param current_background_color + * \param excluded exclude this text string from the selection + * \return true iff successful and redraw should proceed + */ + +bool text_redraw(const char *utf8_text, size_t utf8_len, + size_t offset, bool space, struct css_style *style, + int x, int y, struct rect *clip, + int height, + float scale, colour current_background_color, + bool excluded) { bool highlighted = false; /* is this box part of a selection? */ - if (!box->object && current_redraw_browser) { + if (!excluded && current_redraw_browser) { + unsigned len = utf8_len + (space ? 1 : 0); unsigned start_idx; unsigned end_idx; /* first try the browser window's current selection */ if (selection_defined(current_redraw_browser->sel) && selection_highlighted(current_redraw_browser->sel, - box, &start_idx, &end_idx)) { + offset, offset + len, &start_idx, &end_idx)) { highlighted = true; } - /* what about the current search operation, if any */ + /* what about the current search operation, if any? */ if (!highlighted && search_current_window == current_redraw_browser->window && gui_search_term_highlighted(current_redraw_browser->window, - box, &start_idx, &end_idx)) { + offset, offset + len, &start_idx, &end_idx)) { highlighted = true; } @@ -400,23 +454,23 @@ bool html_redraw_text_box(struct box *box, int x, int y, bool text_visible = true; int startx, endx; - if (end_idx > box->length) { - /* adjust for trailing space, not present in box->text */ - assert(end_idx == box->length + 1); - endtxt_idx = box->length; + if (end_idx > utf8_len) { + /* adjust for trailing space, not present in utf8_text */ + assert(end_idx == utf8_len + 1); + endtxt_idx = utf8_len; } - if (!nsfont_width(box->style, box->text, start_idx, &startx)) + if (!nsfont_width(style, utf8_text, start_idx, &startx)) startx = 0; - if (!nsfont_width(box->style, box->text, endtxt_idx, &endx)) + if (!nsfont_width(style, utf8_text, endtxt_idx, &endx)) endx = 0; /* is there a trailing space that should be highlighted as well? */ - if (end_idx > box->length) { + if (end_idx > utf8_len) { int spc_width; /* \todo is there a more elegant/efficient solution? */ - if (nsfont_width(box->style, " ", 1, &spc_width)) + if (nsfont_width(style, " ", 1, &spc_width)) endx += spc_width; } @@ -427,10 +481,10 @@ bool html_redraw_text_box(struct box *box, int x, int y, /* draw any text preceding highlighted portion */ if (start_idx > 0 && - !plot.text(x, y + (int) (box->height * 0.75 * scale), - box->style, box->text, start_idx, + !plot.text(x, y + (int) (height * 0.75 * scale), + style, utf8_text, start_idx, current_background_color, - /*print_text_black ? 0 :*/ box->style->color)) + /*print_text_black ? 0 :*/ style->color)) return false; /* decide whether highlighted portion is to be white-on-black or @@ -442,16 +496,16 @@ bool html_redraw_text_box(struct box *box, int x, int y, hfore_col = hback_col ^ 0xffffff; /* highlighted portion */ - if (!plot.fill(x + startx, y, x + endx, y + box->height * scale, + if (!plot.fill(x + startx, y, x + endx, y + height * scale, hback_col)) return false; if (start_idx > 0) { - int px0 = max(x + startx, x0); - int px1 = min(x + endx, x1); + int px0 = max(x + startx, clip->x0); + int px1 = min(x + endx, clip->x1); if (px0 < px1) { - if (!plot.clip(px0, y0, px1, y1)) + if (!plot.clip(px0, clip->y0, px1, clip->y1)) return false; clip_changed = true; } else @@ -459,46 +513,75 @@ bool html_redraw_text_box(struct box *box, int x, int y, } if (text_visible && - !plot.text(x, y + (int) (box->height * 0.75 * scale), - box->style, box->text, endtxt_idx, + !plot.text(x, y + (int) (height * 0.75 * scale), + style, utf8_text, endtxt_idx, hback_col, hfore_col)) return false; /* draw any text succeeding highlighted portion */ - if (endtxt_idx < box->length) { - int px0 = max(x + endx, x0); - if (px0 < x1) { + if (endtxt_idx < utf8_len) { + int px0 = max(x + endx, clip->x0); + if (px0 < clip->x1) { - if (!plot.clip(px0, y0, x1, y1)) + if (!plot.clip(px0, clip->y0, clip->x1, clip->y1)) return false; clip_changed = true; - if (!plot.text(x, y + (int) (box->height * 0.75 * scale), - box->style, box->text, box->length, + if (!plot.text(x, y + (int) (height * 0.75 * scale), + style, utf8_text, utf8_len, current_background_color, - /*print_text_black ? 0 :*/ box->style->color)) + /*print_text_black ? 0 :*/ style->color)) return false; } } - if (clip_changed && !plot.clip(x0, y0, x1, y1)) + if (clip_changed && + !plot.clip(clip->x0, clip->y0, clip->x1, clip->y1)) return false; } } if (!highlighted) { - if (!plot.text(x, y + (int) (box->height * 0.75 * scale), - box->style, box->text, box->length, + if (!plot.text(x, y + (int) (height * 0.75 * scale), + style, utf8_text, utf8_len, current_background_color, - /*print_text_black ? 0 :*/ box->style->color)) + /*print_text_black ? 0 :*/ style->color)) return false; } - return true; } +/** + * Draw text caret. + * + * \param c structure describing text caret + * \param current_background_color background colour under the caret + * \param scale current scale setting (1.0 = 100%) + * \return true iff successful and redraw should proceed + */ + +bool html_redraw_caret(struct caret *c, os_colour current_background_color, + float scale) +{ + os_colour caret_color = 0x808080; /* todo - choose a proper colour */ + int xc = c->x, y = c->y; + int h = c->height - 1; + int w = (h + 7) / 8; + + return (plot.line(xc * scale, y * scale, + xc * scale, (y + h) * scale, + 0, caret_color, false, false) && + plot.line((xc - w) * scale, y * scale, + (xc + w) * scale, y * scale, + 0, caret_color, false, false) && + plot.line((xc - w) * scale, (y + h) * scale, + (xc + w) * scale, (y + h) * scale, + 0, caret_color, false, false)); +} + + /** * Draw borders for a box. * @@ -514,10 +597,17 @@ bool html_redraw_text_box(struct box *box, int x, int y, bool html_redraw_borders(struct box *box, int x_parent, int y_parent, int padding_width, int padding_height, float scale) { - int top = box->border[TOP] * scale; - int right = box->border[RIGHT] * scale; - int bottom = box->border[BOTTOM] * scale; - int left = box->border[LEFT] * scale; + int top = box->border[TOP]; + int right = box->border[RIGHT]; + int bottom = box->border[BOTTOM]; + int left = box->border[LEFT]; + + if (scale != 1.0) { + top *= scale; + right *= scale; + bottom *= scale; + left *= scale; + } assert(box->style); diff --git a/render/textplain.c b/render/textplain.c index 26f98b671..d7561abd1 100644 --- a/render/textplain.c +++ b/render/textplain.c @@ -3,12 +3,14 @@ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license * Copyright 2006 James Bursa + * Copyright 2006 Adrian Lees */ /** \file * Content for text/plain (implementation). */ +#include #include #include #include @@ -16,6 +18,7 @@ #include "netsurf/css/css.h" #include "netsurf/desktop/gui.h" #include "netsurf/desktop/plotters.h" +#include "netsurf/render/box.h" #include "netsurf/render/font.h" #include "netsurf/render/textplain.h" #include "netsurf/utils/log.h" @@ -76,8 +79,9 @@ bool textplain_create(struct content *c, const char *params[]) c->data.textplain.utf8_data = utf8_data; c->data.textplain.utf8_data_size = 0; c->data.textplain.utf8_data_allocated = CHUNK; - c->data.textplain.physical_line_start = 0; + c->data.textplain.physical_line = 0; c->data.textplain.physical_line_count = 0; + c->data.textplain.formatted_width = 0; return true; @@ -169,11 +173,12 @@ void textplain_reformat(struct content *c, int width, int height) char *utf8_data = c->data.textplain.utf8_data; size_t utf8_data_size = c->data.textplain.utf8_data_size; unsigned long line_count = 0; - size_t *line_start = c->data.textplain.physical_line_start; - size_t *line_start1; + struct textplain_line *line = c->data.textplain.physical_line; + struct textplain_line *line1; size_t i, space, col; size_t columns = 80; int character_width; + size_t line_start; /* compute available columns (assuming monospaced font) - use 8 * characters for better accuracy */ @@ -181,30 +186,49 @@ void textplain_reformat(struct content *c, int width, int height) return; columns = (width - MARGIN - MARGIN) * 8 / character_width; + c->data.textplain.formatted_width = width; + c->data.textplain.physical_line_count = 0; - if (!line_start) { - c->data.textplain.physical_line_start = line_start = - talloc_array(c, size_t, 1024 + 3); - if (!line_start) + if (!line) { + c->data.textplain.physical_line = line = + talloc_array(c, struct textplain_line, 1024 + 3); + if (!line) goto no_memory; } - line_start[line_count++] = 0; + line[line_count++].start = line_start = 0; space = 0; for (i = 0, col = 0; i != utf8_data_size; i++) { - if (utf8_data[i] == '\n' || col + 1 == columns) { + bool term = (utf8_data[i] == '\n' || utf8_data[i] == '\r'); + if (term || col + 1 == columns) { if (line_count % 1024 == 0) { - line_start1 = talloc_realloc(c, line_start, - size_t, line_count + 1024 + 3); - if (!line_start1) + line1 = talloc_realloc(c, line, + struct textplain_line, line_count + 1024 + 3); + if (!line1) goto no_memory; - c->data.textplain.physical_line_start = - line_start = line_start1; - } - if (utf8_data[i] != '\n' && space) - i = space; - line_start[line_count++] = i + 1; + c->data.textplain.physical_line = + line = line1; + } + if (term) { + line[line_count-1].length = i - line_start; + + /* skip second char of CR/LF or LF/CR pair */ + if (i + 1 < utf8_data_size && + utf8_data[i+1] != utf8_data[i] && + (utf8_data[i+1] == '\n' || utf8_data[i+1] == '\r')) + i++; + } + else { + if (space) { + /* break at last space in line */ + i = space; + line[line_count-1].length = (i + 1) - line_start; + } + else + line[line_count-1].length = i - line_start; + } + line[line_count++].start = line_start = i + 1; col = 0; space = 0; } else { @@ -213,7 +237,8 @@ void textplain_reformat(struct content *c, int width, int height) space = i; } } - line_start[line_count] = utf8_data_size; + line[line_count-1].length = i - line[line_count-1].start; + line[line_count].start = utf8_data_size; c->data.textplain.physical_line_count = line_count; c->width = width; @@ -266,14 +291,21 @@ bool textplain_redraw(struct content *c, int x, int y, float scale, unsigned long background_colour) { char *utf8_data = c->data.textplain.utf8_data; - long line; + long lineno; unsigned long line_count = c->data.textplain.physical_line_count; float line_height = css_len2px(&textplain_style.font_size.value.length, - &textplain_style) * 1.2 * scale; - long line0 = clip_y0 / line_height - 1; - long line1 = clip_y1 / line_height + 1; - size_t *line_start = c->data.textplain.physical_line_start; + &textplain_style) * 1.2; + float scaled_line_height = line_height * scale; + long line0 = clip_y0 / scaled_line_height - 1; + long line1 = clip_y1 / scaled_line_height + 1; + struct textplain_line *line = c->data.textplain.physical_line; size_t length; + struct rect clip; + + clip.x0 = clip_x0; + clip.y0 = clip_y0; + clip.x1 = clip_x1; + clip.y1 = clip_y1; if (line0 < 0) line0 = 0; @@ -289,23 +321,219 @@ bool textplain_redraw(struct content *c, int x, int y, if (!plot.clg(0xffffff)) return false; - if (!line_start) + if (!line) return true; x += MARGIN * scale; y += MARGIN * scale; - for (line = line0; line != line1; line++) { - length = line_start[line + 1] - line_start[line]; + for (lineno = line0; lineno != line1; lineno++) { + length = line[lineno].length; if (!length) continue; - if (utf8_data[line_start[line] + length - 1] == '\n') - length--; - if (!plot.text(x, y + (line + 1) * line_height, - &textplain_style, - utf8_data + line_start[line], length, - 0xffffff, 0x000000)) + if (!text_redraw(utf8_data + line[lineno].start, length, + line[lineno].start, false, &textplain_style, + x, y + (lineno * scaled_line_height), + &clip, line_height, scale, + background_colour, false)) return false; } return true; } + + +/** + * Return byte offset within UTF8 textplain content, given the co-ordinates + * of a point within a textplain content. 'dir' specifies the direction in + * which to search (-1 = above-left, +1 = below-right) if the co-ordinates are not + * contained within a line. + * + * \param c content of type CONTENT_TEXTPLAIN + * \param x x ordinate of point + * \param y y ordinate of point + * \param dir direction of search if not within line + * \return ptr to start of line containing (or nearest to) point + */ + +unsigned textplain_offset_from_coords(struct content *c, int x, int y, int dir) +{ + float line_height = css_len2px(&textplain_style.font_size.value.length, + &textplain_style) * 1.2; + struct textplain_line *line; + int pixel_offset; + unsigned nlines; + int idx; + + assert(c->type == CONTENT_TEXTPLAIN); + + y = (int)((float)(y - MARGIN) / line_height); + x -= MARGIN; + + nlines = c->data.textplain.physical_line_count; + if (!nlines) + return 0; + + if (y < 0) y = 0; + else if (y >= nlines) + y = nlines - 1; + line = &c->data.textplain.physical_line[y]; + + if (x < 0) x = 0; + + nsfont_position_in_string(&textplain_style, + c->data.textplain.utf8_data + line->start, + line->length, + x, + &idx, + &pixel_offset); + + return line->start + idx; +} + + +/** + * Given a byte offset within the text, return the line number + * of the line containing that offset (or -1 if offset invalid) + * + * \param c content of type CONTENT_TEXTPLAIN + * \param offset byte offset within textual representation + * \return line number, or -1 if offset invalid (larger than size) + */ + +int textplain_find_line(struct content *c, unsigned offset) +{ + struct textplain_line *line = c->data.textplain.physical_line; + int nlines = c->data.textplain.physical_line_count; + int lineno = 0; + + assert(c->type == CONTENT_TEXTPLAIN); + + if (offset > c->data.textplain.utf8_data_size) + return -1; + +/* \todo - implement binary search here */ + while (lineno < nlines && line[lineno].start < offset) + lineno++; + if (line[lineno].start > offset) + lineno--; + + return lineno; +} + + +/** + * Given a range of byte offsets within a UTF8 textplain content, + * return a box that fully encloses the text + * + * \param c content of type CONTENT_TEXTPLAIN + * \param start byte offset of start of text range + * \param end byte offset of end + * \param r rectangle to be completed + */ + +void textplain_coords_from_range(struct content *c, unsigned start, unsigned end, + struct rect *r) +{ + float line_height = css_len2px(&textplain_style.font_size.value.length, + &textplain_style) * 1.2; + char *utf8_data = c->data.textplain.utf8_data; + struct textplain_line *line; + unsigned lineno = 0; + unsigned nlines; + + assert(c->type == CONTENT_TEXTPLAIN); + assert(start <= end); + assert(end <= c->data.textplain.utf8_data_size); + + nlines = c->data.textplain.physical_line_count; + line = c->data.textplain.physical_line; + + /* find start */ + lineno = textplain_find_line(c, start); + + r->y0 = (int)(MARGIN + lineno * line_height); + + if (lineno + 1 <= nlines || line[lineno + 1].start >= end) { + /* \todo - it may actually be more efficient just to run + forwards most of the time */ + + /* find end */ + lineno = textplain_find_line(c, end); + + r->x0 = 0; + r->x1 = c->data.textplain.formatted_width; + } + else { + /* single line */ + nsfont_width(&textplain_style, + utf8_data + line[lineno].start, + start - line[lineno].start, + &r->x0); + + nsfont_width(&textplain_style, + utf8_data + line[lineno].start, + min(line[lineno].length, end - line[lineno].start), + &r->x1); + } + + r->y1 = (int)(MARGIN + (lineno + 1) * line_height); +} + + +/** + * Return a pointer to the requested line of text. + * + * \param c content of type CONTENT_TEXTPLAIN + * \param lineno line number + * \param poffset receives byte offset of line start within text + * \param plen receives length of returned line + * \return pointer to text, or NULL if invalid line number + */ + +char *textplain_get_line(struct content *c, unsigned lineno, + size_t *poffset, size_t *plen) +{ + struct textplain_line *line; + + assert(c->type == CONTENT_TEXTPLAIN); + + if (lineno >= c->data.textplain.physical_line_count) + return NULL; + line = &c->data.textplain.physical_line[lineno]; + + *poffset = line->start; + *plen = line->length; + return c->data.textplain.utf8_data + line->start; +} + + +/** + * 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 c content of type CONTENT_TEXTPLAIN + * \param start starting byte offset within UTF-8 text + * \param end ending byte offset + * \param 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) +{ + size_t utf8_size = c->data.textplain.utf8_data_size; + + assert(c->type == CONTENT_TEXTPLAIN); + + /* 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 c->data.textplain.utf8_data + start; +} diff --git a/render/textplain.h b/render/textplain.h index 27da768ee..0775381fc 100644 --- a/render/textplain.h +++ b/render/textplain.h @@ -3,6 +3,7 @@ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license * Copyright 2006 James Bursa + * Copyright 2006 Adrian Lees */ /** \file @@ -12,10 +13,16 @@ #ifndef _NETSURF_RENDER_TEXTPLAIN_H_ #define _NETSURF_RENDER_TEXTPLAIN_H_ +#include #include struct content; +struct textplain_line { + size_t start; + size_t length; +}; + struct content_textplain_data { const char *encoding; iconv_t iconv_cd; @@ -24,7 +31,8 @@ struct content_textplain_data { size_t utf8_data_size; size_t utf8_data_allocated; unsigned long physical_line_count; - size_t *physical_line_start; + struct textplain_line *physical_line; + int formatted_width; }; bool textplain_create(struct content *c, const char *params[]); @@ -37,4 +45,17 @@ bool textplain_redraw(struct content *c, int x, int y, int clip_x0, int clip_y0, int clip_x1, int clip_y1, float scale, unsigned long background_colour); +/* access to lines for text selection and searching */ +#define textplain_line_count(c) ((c)->data.textplain.physical_line_count) +#define textplain_size(c) ((c)->data.textplain.utf8_data_size) + +size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir); +void textplain_coords_from_range(struct content *c, + unsigned start, unsigned end, struct rect *r); +char *textplain_get_line(struct content *c, unsigned lineno, + size_t *poffset, size_t *plen); +int textplain_find_line(struct content *c, unsigned offset); +char *textplain_get_raw_data(struct content *c, + unsigned start, unsigned end, size_t *plen); + #endif -- cgit v1.2.3