From 96841ae2ff8d6a08a1ea453a7fcc14e9a8729401 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Wed, 2 Jan 2013 18:02:15 +0000 Subject: Remove forward declaration. --- render/html_redraw.c | 4121 +++++++++++++++++++++++++------------------------- 1 file changed, 2042 insertions(+), 2079 deletions(-) (limited to 'render') diff --git a/render/html_redraw.c b/render/html_redraw.c index e7474f8c4..26bc46c98 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -53,125 +53,9 @@ #include "utils/utils.h" -static bool html_redraw_box(const html_content *html, struct box *box, - int x, int y, const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx); -static bool html_redraw_box_children(const html_content *html, struct box *box, - int x_parent, int y_parent, const struct rect *clip, - float scale, colour current_background_color, - const struct redraw_context *ctx); -static bool html_redraw_text_box(const html_content *html, struct box *box, - int x, int y, const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx); -static bool html_redraw_borders(struct box *box, int x_parent, int y_parent, - int p_width, int p_height, const struct rect *clip, - float scale, const struct redraw_context *ctx); -static bool html_redraw_inline_borders(struct box *box, struct rect b, - const struct rect *clip, float scale, bool first, bool last, - const struct redraw_context *ctx); -static bool html_redraw_border_plot(const int side, const int *p, colour c, - enum css_border_style_e style, int thickness, bool rectangular, - const struct rect *clip, const struct redraw_context *ctx); -static bool html_redraw_checkbox(int x, int y, int width, int height, - bool selected, const struct redraw_context *ctx); -static bool html_redraw_radio(int x, int y, int width, int height, - bool selected, const struct redraw_context *ctx); -static bool html_redraw_file(int x, int y, int width, int height, - struct box *box, float scale, colour background_colour, - const struct redraw_context *ctx); -static bool html_redraw_background(int x, int y, struct box *box, float scale, - const struct rect *clip, colour *background_colour, - struct box *background, const struct redraw_context *ctx); -static bool html_redraw_inline_background(int x, int y, struct box *box, - float scale, const struct rect *clip, struct rect b, - bool first, bool last, colour *background_colour, - const struct redraw_context *ctx); -static bool html_redraw_text_decoration(struct box *box, - int x_parent, int y_parent, float scale, - colour background_colour, const struct redraw_context *ctx); -static bool html_redraw_text_decoration_inline(struct box *box, int x, int y, - float scale, colour colour, float ratio, - const struct redraw_context *ctx); -static bool html_redraw_text_decoration_block(struct box *box, int x, int y, - float scale, colour colour, float ratio, - const struct redraw_context *ctx); - -bool html_redraw_debug = false; - -/** - * Draw a CONTENT_HTML using the current set of plotters (plot). - * - * \param c content of type CONTENT_HTML - * \param data redraw data for this content redraw - * \param clip current clip region - * \param ctx current redraw context - * \return true if successful, false otherwise - * - * x, y, clip_[xy][01] are in target coordinates. - */ - -bool html_redraw(struct content *c, struct content_redraw_data *data, - const struct rect *clip, const struct redraw_context *ctx) -{ - html_content *html = (html_content *) c; - struct box *box; - bool result = true; - bool select, select_only; - plot_style_t pstyle_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = data->background_colour, - }; - - box = html->layout; - assert(box); - - /* The select menu needs special treating because, when opened, it - * reaches beyond its layout box. - */ - select = false; - select_only = false; - if (ctx->interactive && html->visible_select_menu != NULL) { - struct form_control *control = html->visible_select_menu; - select = true; - /* check if the redraw rectangle is completely inside of the - select menu */ - select_only = form_clip_inside_select_menu(control, - data->scale, clip); - } - - if (!select_only) { - /* clear to background colour */ - result = ctx->plot->clip(clip); - - if (html->background_colour != NS_TRANSPARENT) - pstyle_fill_bg.fill_colour = html->background_colour; - - result &= ctx->plot->rectangle(clip->x0, clip->y0, - clip->x1, clip->y1, - &pstyle_fill_bg); - - result &= html_redraw_box(html, box, data->x, data->y, clip, - data->scale, pstyle_fill_bg.fill_colour, ctx); - } - - if (select) { - int menu_x, menu_y; - box = html->visible_select_menu->box; - box_coords(box, &menu_x, &menu_y); - - menu_x -= box->border[LEFT].width; - menu_y += box->height + box->border[BOTTOM].width + - box->padding[BOTTOM] + box->padding[TOP]; - result &= form_redraw_select_menu(html->visible_select_menu, - data->x + menu_x, data->y + menu_y, - data->scale, clip, ctx); - } - return result; -} +bool html_redraw_debug = false; /** * Determine if a box has a background that needs drawing @@ -239,895 +123,829 @@ static struct box *html_redraw_find_bg_box(struct box *box) } /** - * Recursively draw a box. - * - * \param html html content - * \param box box to draw - * \param x_parent coordinate of parent box - * \param y_parent coordinate of parent box - * \param clip clip rectangle - * \param scale scale for redraw - * \param current_background_color background colour under this box - * \param ctx current redraw context - * \return true if successful, false otherwise + * Redraw a short text string, complete with highlighting + * (for selection/search) * - * x, y, clip_[xy][01] are in target coordinates. + * \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 width of space that follows string (0 = no space) + * \param fstyle text style to use (pass text size unscaled) + * \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 excluded exclude this text string from the selection + * \param ctx current redraw context + * \return true iff successful and redraw should proceed */ -bool html_redraw_box(const html_content *html, struct box *box, - int x_parent, int y_parent, - const struct rect *clip, float scale, - colour current_background_color, +bool text_redraw(const char *utf8_text, size_t utf8_len, + size_t offset, int space, const plot_font_style_t *fstyle, + int x, int y, const struct rect *clip, int height, + float scale, bool excluded, struct content *c, + const struct selection *sel, struct search_context *search, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; - int x, y; - int width, height; - int padding_left, padding_top, padding_width, padding_height; - int border_left, border_top, border_right, border_bottom; - struct rect r; - int x_scrolled, y_scrolled; - struct box *bg_box = NULL; - bool has_x_scroll, has_y_scroll; - css_computed_clip_rect css_rect; + bool highlighted = false; + plot_font_style_t plot_fstyle = *fstyle; - if (html_redraw_printing && (box->flags & PRINTED)) - return true; + /* Need scaled text size to pass to plotters */ + plot_fstyle.size *= scale; - /* avoid trivial FP maths */ - if (scale == 1.0) { - x = x_parent + box->x; - y = y_parent + box->y; - width = box->width; - height = box->height; - padding_left = box->padding[LEFT]; - padding_top = box->padding[TOP]; - padding_width = padding_left + box->width + box->padding[RIGHT]; - padding_height = padding_top + box->height + - box->padding[BOTTOM]; - border_left = box->border[LEFT].width; - border_top = box->border[TOP].width; - border_right = box->border[RIGHT].width; - border_bottom = box->border[BOTTOM].width; - } else { - x = (x_parent + box->x) * scale; - y = (y_parent + box->y) * scale; - width = box->width * scale; - height = box->height * scale; - /* left and top padding values are normally zero, - * so avoid trivial FP maths */ - padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale - : 0; - padding_top = box->padding[TOP] ? box->padding[TOP] * scale - : 0; - padding_width = (box->padding[LEFT] + box->width + - box->padding[RIGHT]) * scale; - padding_height = (box->padding[TOP] + box->height + - box->padding[BOTTOM]) * scale; - border_left = box->border[LEFT].width * scale; - border_top = box->border[TOP].width * scale; - border_right = box->border[RIGHT].width * scale; - border_bottom = box->border[BOTTOM].width * scale; - } + /* is this box part of a selection? */ + if (!excluded && ctx->interactive == true) { + unsigned len = utf8_len + (space ? 1 : 0); + unsigned start_idx; + unsigned end_idx; - /* calculate rectangle covering this box and descendants */ - if (box->style && css_computed_overflow(box->style) != - CSS_OVERFLOW_VISIBLE) { - /* box contents clipped to box size */ - r.x0 = x - border_left; - r.y0 = y - border_top; - r.x1 = x + padding_width + border_right; - r.y1 = y + padding_height + border_bottom; - } else { - /* box contents can hang out of the box; use descendant box */ - if (scale == 1.0) { - r.x0 = x + box->descendant_x0; - r.y0 = y + box->descendant_y0; - r.x1 = x + box->descendant_x1 + 1; - r.y1 = y + box->descendant_y1 + 1; - } else { - r.x0 = x + box->descendant_x0 * scale; - r.y0 = y + box->descendant_y0 * scale; - r.x1 = x + box->descendant_x1 * scale + 1; - r.y1 = y + box->descendant_y1 * scale + 1; - } - if (!box->parent) { - /* root element */ - int margin_left, margin_right; - int margin_top, margin_bottom; - if (scale == 1.0) { - margin_left = box->margin[LEFT]; - margin_top = box->margin[TOP]; - margin_right = box->margin[RIGHT]; - margin_bottom = box->margin[BOTTOM]; - } else { - margin_left = box->margin[LEFT] * scale; - margin_top = box->margin[TOP] * scale; - margin_right = box->margin[RIGHT] * scale; - margin_bottom = box->margin[BOTTOM] * scale; - } - r.x0 = x - border_left - margin_left < r.x0 ? - x - border_left - margin_left : r.x0; - r.y0 = y - border_top - margin_top < r.y0 ? - y - border_top - margin_top : r.y0; - r.x1 = x + padding_width + border_right + - margin_right > r.x1 ? - x + padding_width + border_right + - margin_right : r.x1; - r.y1 = y + padding_height + border_bottom + - margin_bottom > r.y1 ? - y + padding_height + border_bottom + - margin_bottom : r.y1; + /* first try the browser window's current selection */ + if (selection_defined(sel) && selection_highlighted(sel, + offset, offset + len, + &start_idx, &end_idx)) { + highlighted = true; } - } - /* return if the rectangle is completely outside the clip rectangle */ - if (clip->y1 < r.y0 || r.y1 < clip->y0 || - clip->x1 < r.x0 || r.x1 < clip->x0) - return true; + /* what about the current search operation, if any? */ + if (!highlighted && (search != NULL) && + search_term_highlighted(c, + offset, offset + len, + &start_idx, &end_idx, + search)) { + highlighted = true; + } - /*if the rectangle is under the page bottom but it can fit in a page, - don't print it now*/ - if (html_redraw_printing) { - if (r.y1 > html_redraw_printing_border) { - if (r.y1 - r.y0 <= html_redraw_printing_border && - (box->type == BOX_TEXT || - box->type == BOX_TABLE_CELL - || box->object || box->gadget)) { - /*remember the highest of all points from the - not printed elements*/ - if (r.y0 < html_redraw_printing_top_cropped) - html_redraw_printing_top_cropped = r.y0; - return true; + /* \todo make search terms visible within selected text */ + if (highlighted) { + struct rect r; + unsigned endtxt_idx = end_idx; + bool clip_changed = false; + bool text_visible = true; + int startx, endx; + plot_style_t *pstyle_fill_hback = plot_style_fill_white; + plot_font_style_t fstyle_hback = plot_fstyle; + + if (end_idx > utf8_len) { + /* adjust for trailing space, not present in + * utf8_text */ + assert(end_idx == utf8_len + 1); + endtxt_idx = utf8_len; } - } - else box->flags |= PRINTED; /*it won't be printed anymore*/ - } - /* if visibility is hidden render children only */ - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) { - if ((plot->group_start) && (!plot->group_start("hidden box"))) - return false; - if (!html_redraw_box_children(html, box, x_parent, y_parent, - &r, scale, current_background_color, ctx)) - return false; - return ((!plot->group_end) || (plot->group_end())); - } + if (!nsfont.font_width(fstyle, utf8_text, start_idx, + &startx)) + startx = 0; - if ((plot->group_start) && (!plot->group_start("vis box"))) - return false; + if (!nsfont.font_width(fstyle, utf8_text, endtxt_idx, + &endx)) + endx = 0; + /* is there a trailing space that should be highlighted + * as well? */ + if (end_idx > utf8_len) { + endx += space; + } - if (box->style != NULL && - css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE && - css_computed_clip(box->style, &css_rect) == - CSS_CLIP_RECT) { - /* We have an absolutly positioned box with a clip rect */ - if (css_rect.left_auto == false) - r.x0 = x - border_left + FIXTOINT(nscss_len2px( - css_rect.left, css_rect.lunit, - box->style)); + if (scale != 1.0) { + startx *= scale; + endx *= scale; + } - if (css_rect.top_auto == false) - r.y0 = y - border_top + FIXTOINT(nscss_len2px( - css_rect.top, css_rect.tunit, - box->style)); + /* draw any text preceding highlighted portion */ + if (start_idx > 0 && + !plot->text(x, y + (int)(height * 0.75 * scale), + utf8_text, start_idx, + &plot_fstyle)) + return false; - if (css_rect.right_auto == false) - r.x1 = x - border_left + FIXTOINT(nscss_len2px( - css_rect.right, css_rect.runit, - box->style)); + /* decide whether highlighted portion is to be + * white-on-black or black-on-white */ + if ((fstyle->background & 0x808080) == 0x808080) + pstyle_fill_hback = plot_style_fill_black; - if (css_rect.bottom_auto == false) - r.y1 = y - border_top + FIXTOINT(nscss_len2px( - css_rect.bottom, css_rect.bunit, - box->style)); + /* highlighted portion */ + if (!plot->rectangle(x + startx, y, x + endx, + y + height * scale, + pstyle_fill_hback)) + return false; - /* find intersection of clip rectangle and box */ - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - /* no point trying to draw 0-width/height boxes */ - if (r.x0 == r.x1 || r.y0 == r.y1) - /* not an error */ - return ((!plot->group_end) || (plot->group_end())); - /* clip to it */ - if (!plot->clip(&r)) - return false; + if (start_idx > 0) { + int px0 = max(x + startx, clip->x0); + int px1 = min(x + endx, clip->x1); - } else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->object) { - /* find intersection of clip rectangle and box */ - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - /* no point trying to draw 0-width/height boxes */ - if (r.x0 == r.x1 || r.y0 == r.y1) - /* not an error */ - return ((!plot->group_end) || (plot->group_end())); - /* clip to it */ - if (!plot->clip(&r)) - return false; - } else { - /* clip box is fine, clip to it */ - r = *clip; - if (!plot->clip(&r)) - return false; - } + if (px0 < px1) { + r.x0 = px0; + r.y0 = clip->y0; + r.x1 = px1; + r.y1 = clip->y1; + if (!plot->clip(&r)) + return false; + clip_changed = true; + } else { + text_visible = false; + } + } - /* background colour and image for block level content and replaced - * inlines */ + fstyle_hback.background = + pstyle_fill_hback->fill_colour; + fstyle_hback.foreground = + pstyle_fill_hback->fill_colour ^ 0xffffff; - bg_box = html_redraw_find_bg_box(box); + if (text_visible && + !plot->text(x, y + (int)(height * 0.75 * scale), + utf8_text, endtxt_idx, + &fstyle_hback)) + return false; - /* bg_box == NULL implies that this box should not have - * its background rendered. Otherwise filter out linebreaks, - * optimize away non-differing inlines, only plot background - * for BOX_TEXT it's in an inline */ - if (bg_box && bg_box->type != BOX_BR && - bg_box->type != BOX_TEXT && - bg_box->type != BOX_INLINE_END && - (bg_box->type != BOX_INLINE || bg_box->object || - bg_box->flags & IFRAME || box->flags & REPLACE_DIM)) { - /* find intersection of clip box and border edge */ - struct rect p; - p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left; - p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top; - p.x1 = x + padding_width + border_right < r.x1 ? - x + padding_width + border_right : r.x1; - p.y1 = y + padding_height + border_bottom < r.y1 ? - y + padding_height + border_bottom : r.y1; - if (!box->parent) { - /* Root element, special case: - * background covers margins too */ - int m_left, m_top, m_right, m_bottom; - if (scale == 1.0) { - m_left = box->margin[LEFT]; - m_top = box->margin[TOP]; - m_right = box->margin[RIGHT]; - m_bottom = box->margin[BOTTOM]; - } else { - m_left = box->margin[LEFT] * scale; - m_top = box->margin[TOP] * scale; - m_right = box->margin[RIGHT] * scale; - m_bottom = box->margin[BOTTOM] * scale; + /* draw any text succeeding highlighted portion */ + if (endtxt_idx < utf8_len) { + int px0 = max(x + endx, clip->x0); + if (px0 < clip->x1) { + + r.x0 = px0; + r.y0 = clip->y0; + r.x1 = clip->x1; + r.y1 = clip->y1; + if (!plot->clip(&r)) + return false; + + clip_changed = true; + + if (!plot->text(x, y + (int) + (height * 0.75 * scale), + utf8_text, utf8_len, + &plot_fstyle)) + return false; + } } - p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left; - p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top; - p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1; - p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1; - } - /* valid clipping rectangles only */ - if ((p.x0 < p.x1) && (p.y0 < p.y1)) { - /* plot background */ - if (!html_redraw_background(x, y, box, scale, &p, - ¤t_background_color, bg_box, ctx)) - return false; - /* restore previous graphics window */ - if (!plot->clip(&r)) + + if (clip_changed && + !plot->clip(clip)) return false; } } - /* borders for block level content and replaced inlines */ - if (box->style && box->type != BOX_TEXT && - box->type != BOX_INLINE_END && - (box->type != BOX_INLINE || box->object || - box->flags & IFRAME || box->flags & REPLACE_DIM) && - (border_top || border_right || - border_bottom || border_left)) { - if (!html_redraw_borders(box, x_parent, y_parent, - padding_width, padding_height, &r, - scale, ctx)) + if (!highlighted) { + if (!plot->text(x, y + (int) (height * 0.75 * scale), + utf8_text, utf8_len, + &plot_fstyle)) return false; } + return true; +} - /* backgrounds and borders for non-replaced inlines */ - if (box->style && box->type == BOX_INLINE && box->inline_end && - (html_redraw_box_has_background(box) || - border_top || border_right || - border_bottom || border_left)) { - /* inline backgrounds and borders span other boxes and may - * wrap onto separate lines */ - struct box *ib; - struct rect b; /* border edge rectangle */ - struct rect p; /* clipped rect */ - bool first = true; - int ib_x; - int ib_y = y; - int ib_p_width; - int ib_b_left, ib_b_right; - - b.x0 = x - border_left; - b.x1 = x + padding_width + border_right; - b.y0 = y - border_top; - b.y1 = y + padding_height + border_bottom; - - p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; - p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; - p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; - p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; - for (ib = box; ib; ib = ib->next) { - /* to get extents of rectangle(s) associated with - * inline, cycle though all boxes in inline, skipping - * over floats */ - if (ib->type == BOX_FLOAT_LEFT || - ib->type == BOX_FLOAT_RIGHT) - continue; - if (scale == 1.0) { - ib_x = x_parent + ib->x; - ib_y = y_parent + ib->y; - ib_p_width = ib->padding[LEFT] + ib->width + - ib->padding[RIGHT]; - ib_b_left = ib->border[LEFT].width; - ib_b_right = ib->border[RIGHT].width; - } else { - ib_x = (x_parent + ib->x) * scale; - ib_y = (y_parent + ib->y) * scale; - ib_p_width = (ib->padding[LEFT] + ib->width + - ib->padding[RIGHT]) * scale; - ib_b_left = ib->border[LEFT].width * scale; - ib_b_right = ib->border[RIGHT].width * scale; - } +static plot_style_t plot_style_bdr = { + .stroke_type = PLOT_OP_TYPE_DASH, +}; +static plot_style_t plot_style_fillbdr = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_dark = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_light = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_ddark = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_dlight = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; - if ((ib->flags & NEW_LINE) && ib != box) { - /* inline element has wrapped, plot background - * and borders */ - if (!html_redraw_inline_background( - x, y, box, scale, &p, b, - first, false, - ¤t_background_color, ctx)) - return false; - /* restore previous graphics window */ - if (!plot->clip(&r)) - return false; - if (!html_redraw_inline_borders(box, b, &r, - scale, first, false, ctx)) - return false; - /* reset coords */ - b.x0 = ib_x - ib_b_left; - b.y0 = ib_y - border_top - padding_top; - b.y1 = ib_y + padding_height - padding_top + - border_bottom; +/** + * Draw one border. + * + * \param side index of border side (TOP, RIGHT, BOTTOM, LEFT) + * \param p array of precomputed border vertices + * \param c colour for border + * \param style border line style + * \param thickness border thickness + * \param rectangular whether border is rectangular + * \param ctx current redraw context + * \return true if successful, false otherwise + */ - p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; - p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; - p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; +static bool html_redraw_border_plot(const int side, const int *p, colour c, + enum css_border_style_e style, int thickness, bool rectangular, + const struct rect *clip, const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + int z[8]; /* Vertices of border part */ + unsigned int light = side; + plot_style_t *plot_style_bdr_in; + plot_style_t *plot_style_bdr_out; - first = false; - } + if (c == NS_TRANSPARENT) + return true; - /* increase width for current box */ - b.x1 = ib_x + ib_p_width + ib_b_right; - p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; + plot_style_bdr.stroke_type = PLOT_OP_TYPE_DASH; + plot_style_bdr.stroke_colour = c; + plot_style_bdr.stroke_width = thickness; + plot_style_fillbdr.fill_colour = c; + plot_style_fillbdr_dark.fill_colour = darken_colour(c); + plot_style_fillbdr_light.fill_colour = lighten_colour(c); + plot_style_fillbdr_ddark.fill_colour = double_darken_colour(c); + plot_style_fillbdr_dlight.fill_colour = double_lighten_colour(c); - if (ib == box->inline_end) - /* reached end of BOX_INLINE span */ - break; - } - /* plot background and borders for last rectangle of - * the inline */ - if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b, - first, true, ¤t_background_color, ctx)) - return false; - /* restore previous graphics window */ - if (!plot->clip(&r)) - return false; - if (!html_redraw_inline_borders(box, b, &r, scale, first, true, - ctx)) + switch (style) { + case CSS_BORDER_STYLE_DOTTED: + plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT; + /* fall through */ + case CSS_BORDER_STYLE_DASHED: + if (!plot->line((p[0] + p[2]) / 2, + (p[1] + p[3]) / 2, + (p[4] + p[6]) / 2, + (p[5] + p[7]) / 2, + &plot_style_bdr)) return false; + break; - } - - /* Debug outlines */ - if (html_redraw_debug) { - int margin_left, margin_right; - int margin_top, margin_bottom; - if (scale == 1.0) { - /* avoid trivial fp maths */ - margin_left = box->margin[LEFT]; - margin_top = box->margin[TOP]; - margin_right = box->margin[RIGHT]; - margin_bottom = box->margin[BOTTOM]; + case CSS_BORDER_STYLE_SOLID: + /* fall through to default */ + default: + if (rectangular || thickness == 1) { + int x0, y0, x1, y1; + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = p[6]; y1 = p[7]; + x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? + x1 + p[4] - p[6] : x1; + } else { + x0 = p[6]; y0 = p[7]; + x1 = p[2]; y1 = p[3]; + y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? + y1 + p[1] - p[3] : y1; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + &plot_style_fillbdr)) + return false; + } } else { - margin_left = box->margin[LEFT] * scale; - margin_top = box->margin[TOP] * scale; - margin_right = box->margin[RIGHT] * scale; - margin_bottom = box->margin[BOTTOM] * scale; - } - /* Content edge -- blue */ - if (!plot->rectangle(x + padding_left, - y + padding_top, - x + padding_left + width, - y + padding_top + height, - plot_style_content_edge)) - return false; - /* Padding edge -- red */ - if (!plot->rectangle(x, y, - x + padding_width, y + padding_height, - plot_style_padding_edge)) - return false; - /* Margin edge -- yellow */ - if (!plot->rectangle( - x - border_left - margin_left, - y - border_top - margin_top, - x + padding_width + border_right + - margin_right, - y + padding_height + border_bottom + - margin_bottom, - plot_style_margin_edge)) - return false; - } - - /* clip to the padding edge for objects, or boxes with overflow hidden - * or scroll */ - if ((box->style && css_computed_overflow(box->style) != - CSS_OVERFLOW_VISIBLE) || box->object || - box->flags & IFRAME) { - r.x0 = x; - r.y0 = y; - r.x1 = x + padding_width; - r.y1 = y + padding_height; - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - if (r.x1 <= r.x0 || r.y1 <= r.y0) - return ((!plot->group_end) || (plot->group_end())); - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->object) { - if (!plot->clip(&r)) + if (!plot->polygon(p, 4, &plot_style_fillbdr)) return false; } - } + break; - /* text decoration */ - if (box->type != BOX_TEXT && box->style && - css_computed_text_decoration(box->style) != - CSS_TEXT_DECORATION_NONE) - if (!html_redraw_text_decoration(box, x_parent, y_parent, - scale, current_background_color, ctx)) + case CSS_BORDER_STYLE_DOUBLE: + z[0] = p[0]; + z[1] = p[1]; + z[2] = (p[0] * 2 + p[2]) / 3; + z[3] = (p[1] * 2 + p[3]) / 3; + z[4] = (p[6] * 2 + p[4]) / 3; + z[5] = (p[7] * 2 + p[5]) / 3; + z[6] = p[6]; + z[7] = p[7]; + if (!plot->polygon(z, 4, &plot_style_fillbdr)) return false; + z[0] = p[2]; + z[1] = p[3]; + z[2] = (p[2] * 2 + p[0]) / 3; + z[3] = (p[3] * 2 + p[1]) / 3; + z[4] = (p[4] * 2 + p[6]) / 3; + z[5] = (p[5] * 2 + p[7]) / 3; + z[6] = p[4]; + z[7] = p[5]; + if (!plot->polygon(z, 4, &plot_style_fillbdr)) + return false; + break; - if (box->object && width != 0 && height != 0) { - struct content_redraw_data obj_data; - - x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale; - y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale; + case CSS_BORDER_STYLE_GROOVE: + light = 3 - light; + /* fall through */ + case CSS_BORDER_STYLE_RIDGE: + /* choose correct colours for each part of the border line */ + if (light <= 1) { + plot_style_bdr_in = &plot_style_fillbdr_dark; + plot_style_bdr_out = &plot_style_fillbdr_light; + } else { + plot_style_bdr_in = &plot_style_fillbdr_light; + plot_style_bdr_out = &plot_style_fillbdr_dark; + } - obj_data.x = x_scrolled + padding_left; - obj_data.y = y_scrolled + padding_top; - obj_data.width = width; - obj_data.height = height; - obj_data.background_colour = current_background_color; - obj_data.scale = scale; - obj_data.repeat_x = false; - obj_data.repeat_y = false; + /* Render border */ + if ((rectangular || thickness == 2) && thickness != 1) { + /* Border made up from two parts and can be plotted + * with rectangles */ + int x0, y0, x1, y1; - if (content_get_type(box->object) == CONTENT_HTML) { - obj_data.x /= scale; - obj_data.y /= scale; - } + /* First part */ + if (side == TOP || side == RIGHT) { + x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; + x1 = p[6]; y1 = p[7]; + } else { + x0 = p[6]; y0 = p[7]; + x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } - if (!content_redraw(box->object, &obj_data, &r, ctx)) { - /* Show image fail */ - /* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */ - const char *obj = "\xef\xbf\xbc"; - int obj_width; - int obj_x = x + padding_left; - if (!plot->rectangle(x + padding_left, - y + padding_top, - x + padding_left + width - 1, - y + padding_top + height - 1, - plot_style_broken_object)) + /* Second part */ + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; + } else { + x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; + x1 = p[2]; y1 = p[3]; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } else if (thickness == 1) { + /* Border made up from one part which can be plotted + * as a rectangle */ + int x0, y0, x1, y1; + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = p[6]; y1 = p[7]; + x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? + x1 + p[4] - p[6] : x1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } + } else { + x0 = p[6]; y0 = p[7]; + x1 = p[2]; y1 = p[3]; + y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? + y1 + p[1] - p[3] : y1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } + } else { + /* Border made up from two parts and can't be plotted + * with rectangles */ + z[0] = p[0]; + z[1] = p[1]; + z[2] = (p[0] + p[2]) / 2; + z[3] = (p[1] + p[3]) / 2; + z[4] = (p[6] + p[4]) / 2; + z[5] = (p[7] + p[5]) / 2; + z[6] = p[6]; + z[7] = p[7]; + if (!plot->polygon(z, 4, plot_style_bdr_in)) return false; - if (!nsfont.font_width(plot_fstyle_broken_object, obj, - sizeof(obj) - 1, &obj_width)) - obj_x += 1; - else - obj_x += width / 2 - obj_width / 2; - - if (!plot->text(obj_x, y + padding_top + (int) - (height * 0.75), - obj, sizeof(obj) - 1, - plot_fstyle_broken_object)) + z[0] = p[2]; + z[1] = p[3]; + z[6] = p[4]; + z[7] = p[5]; + if (!plot->polygon(z, 4, plot_style_bdr_out)) return false; } - - - } else if (box->iframe) { - /* Offset is passed to browser window redraw unscaled */ - browser_window_redraw(box->iframe, - (x + padding_left) / scale, - (y + padding_top) / scale, &r, ctx); - - } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) { - if (!html_redraw_checkbox(x + padding_left, y + padding_top, - width, height, box->gadget->selected, ctx)) - return false; - - } else if (box->gadget && box->gadget->type == GADGET_RADIO) { - if (!html_redraw_radio(x + padding_left, y + padding_top, - width, height, box->gadget->selected, ctx)) - return false; - - } else if (box->gadget && box->gadget->type == GADGET_FILE) { - if (!html_redraw_file(x + padding_left, y + padding_top, - width, height, box, scale, - current_background_color, ctx)) - return false; - - } else if (box->text) { - if (!html_redraw_text_box(html, box, x, y, &r, scale, - current_background_color, ctx)) - return false; - - } else { - if (!html_redraw_box_children(html, box, x_parent, y_parent, &r, - scale, current_background_color, ctx)) - return false; - } - - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) - if (!plot->clip(clip)) - return false; - - /* list marker */ - if (box->list_marker) - if (!html_redraw_box(html, box->list_marker, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, ctx)) - return false; - - /* scrollbars */ - if (((box->style && box->type != BOX_BR && - box->type != BOX_TABLE && box->type != BOX_INLINE && - (css_computed_overflow(box->style) == - CSS_OVERFLOW_SCROLL || - css_computed_overflow(box->style) == - CSS_OVERFLOW_AUTO)) || (box->object && - content_get_type(box->object) == CONTENT_HTML)) && - box->parent != NULL) { - - has_x_scroll = box_hscrollbar_present(box); - has_y_scroll = box_vscrollbar_present(box); - - if (!box_handle_scrollbars((struct content *)html, - box, has_x_scroll, has_y_scroll)) - return false; - - if (box->scroll_x != NULL) - scrollbar_redraw(box->scroll_x, - x_parent + box->x, - y_parent + box->y + box->padding[TOP] + - box->height + box->padding[BOTTOM] - - SCROLLBAR_WIDTH, clip, scale, ctx); - if (box->scroll_y != NULL) - scrollbar_redraw(box->scroll_y, - x_parent + box->x + box->padding[LEFT] + - box->width + box->padding[RIGHT] - - SCROLLBAR_WIDTH, - y_parent + box->y, clip, scale, ctx); - } - - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) - if (!plot->clip(clip)) - return false; - - return ((!plot->group_end) || (plot->group_end())); -} - + break; -/** - * Draw the various children of a box. - * - * \param html html content - * \param box box to draw children of - * \param x_parent coordinate of parent box - * \param y_parent coordinate of parent box - * \param clip clip rectangle - * \param scale scale for redraw - * \param current_background_color background colour under this box - * \param ctx current redraw context - * \return true if successful, false otherwise - */ + case CSS_BORDER_STYLE_INSET: + light = (light + 2) % 4; + /* fall through */ + case CSS_BORDER_STYLE_OUTSET: + /* choose correct colours for each part of the border line */ + switch (light) { + case 0: + plot_style_bdr_in = &plot_style_fillbdr_light; + plot_style_bdr_out = &plot_style_fillbdr_dlight; + break; + case 1: + plot_style_bdr_in = &plot_style_fillbdr_ddark; + plot_style_bdr_out = &plot_style_fillbdr_dark; + break; + case 2: + plot_style_bdr_in = &plot_style_fillbdr_dark; + plot_style_bdr_out = &plot_style_fillbdr_ddark; + break; + case 3: + plot_style_bdr_in = &plot_style_fillbdr_dlight; + plot_style_bdr_out = &plot_style_fillbdr_light; + break; + default: + plot_style_bdr_in = &plot_style_fillbdr; + plot_style_bdr_out = &plot_style_fillbdr; + break; + } -bool html_redraw_box_children(const html_content *html, struct box *box, - int x_parent, int y_parent, - const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx) -{ - struct box *c; + /* Render border */ + if ((rectangular || thickness == 2) && thickness != 1) { + /* Border made up from two parts and can be plotted + * with rectangles */ + int x0, y0, x1, y1; - for (c = box->children; c; c = c->next) { + /* First part */ + if (side == TOP || side == RIGHT) { + x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; + x1 = p[6]; y1 = p[7]; + } else { + x0 = p[6]; y0 = p[7]; + x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } - if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) - if (!html_redraw_box(html, c, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, - ctx)) + /* Second part */ + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; + } else { + x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; + x1 = p[2]; y1 = p[3]; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } else if (thickness == 1) { + /* Border made up from one part which can be plotted + * as a rectangle */ + int x0, y0, x1, y1; + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = p[6]; y1 = p[7]; + x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? + x1 + p[4] - p[6] : x1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } + } else { + x0 = p[6]; y0 = p[7]; + x1 = p[2]; y1 = p[3]; + y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? + y1 + p[1] - p[3] : y1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } + } else { + /* Border made up from two parts and can't be plotted + * with rectangles */ + z[0] = p[0]; + z[1] = p[1]; + z[2] = (p[0] + p[2]) / 2; + z[3] = (p[1] + p[3]) / 2; + z[4] = (p[6] + p[4]) / 2; + z[5] = (p[7] + p[5]) / 2; + z[6] = p[6]; + z[7] = p[7]; + if (!plot->polygon(z, 4, plot_style_bdr_in)) + return false; + z[0] = p[2]; + z[1] = p[3]; + z[6] = p[4]; + z[7] = p[5]; + if (!plot->polygon(z, 4, plot_style_bdr_out)) return false; + } + break; } - for (c = box->float_children; c; c = c->next_float) - if (!html_redraw_box(html, c, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, - ctx)) - return false; return true; } /** - * Redraw the text content of a box, possibly partially highlighted - * because the text has been selected, or matches a search operation. + * Draw borders for a box. * - * \param box box with text content - * \param x x co-ord of box - * \param y y co-ord of box - * \param clip current clip rectangle - * \param scale current scale setting (1.0 = 100%) - * \param current_background_color - * \param ctx current redraw context - * \return true iff successful and redraw should proceed + * \param box box to draw + * \param x_parent coordinate of left padding edge of parent of box + * \param y_parent coordinate of top padding edge of parent of box + * \param p_width width of padding box + * \param p_height height of padding box + * \param scale scale for redraw + * \param ctx current redraw context + * \return true if successful, false otherwise */ -bool html_redraw_text_box(const html_content *html, struct box *box, - int x, int y, const struct rect *clip, float scale, - colour current_background_color, +static bool html_redraw_borders(struct box *box, int x_parent, int y_parent, + int p_width, int p_height, const struct rect *clip, float scale, const struct redraw_context *ctx) { - bool excluded = (box->object != NULL); - plot_font_style_t fstyle; - - font_plot_style_from_css(box->style, &fstyle); - fstyle.background = current_background_color; - - if (!text_redraw(box->text, box->length, box->byte_offset, - box->space, &fstyle, x, y, - clip, box->height, scale, excluded, - (struct content *)html, &html->sel, - html->search, ctx)) - return false; - - return true; -} + unsigned int sides[] = { LEFT, RIGHT, TOP, BOTTOM }; + int top = box->border[TOP].width; + int right = box->border[RIGHT].width; + int bottom = box->border[BOTTOM].width; + int left = box->border[LEFT].width; + int x, y; + unsigned int i, side; + int p[8]; /* Box border vertices */ + int z[8]; /* Border vertices */ + bool square_end_1 = false; + bool square_end_2 = false; -/** - * Redraw a short text string, complete with highlighting - * (for selection/search) - * - * \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 width of space that follows string (0 = no space) - * \param fstyle text style to use (pass text size unscaled) - * \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 excluded exclude this text string from the selection - * \param ctx current redraw context - * \return true iff successful and redraw should proceed - */ + x = x_parent + box->x; + y = y_parent + box->y; -bool text_redraw(const char *utf8_text, size_t utf8_len, - size_t offset, int space, const plot_font_style_t *fstyle, - int x, int y, const struct rect *clip, int height, - float scale, bool excluded, struct content *c, - const struct selection *sel, struct search_context *search, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - bool highlighted = false; - plot_font_style_t plot_fstyle = *fstyle; + if (scale != 1.0) { + top *= scale; + right *= scale; + bottom *= scale; + left *= scale; + x *= scale; + y *= scale; + } - /* Need scaled text size to pass to plotters */ - plot_fstyle.size *= scale; + assert(box->style); - /* is this box part of a selection? */ - if (!excluded && ctx->interactive == true) { - unsigned len = utf8_len + (space ? 1 : 0); - unsigned start_idx; - unsigned end_idx; + /* Calculate border vertices + * + * A----------------------+ + * | \ / | + * | B--------------+ | + * | | | | + * | +--------------C | + * | / \ | + * +----------------------D + */ + p[0] = x - left; p[1] = y - top; /* A */ + p[2] = x; p[3] = y; /* B */ + p[4] = x + p_width; p[5] = y + p_height; /* C */ + p[6] = x + p_width + right; p[7] = y + p_height + bottom; /* D */ - /* first try the browser window's current selection */ - if (selection_defined(sel) && selection_highlighted(sel, - offset, offset + len, - &start_idx, &end_idx)) { - highlighted = true; - } + for (i = 0; i != 4; i++) { + colour col = 0; + side = sides[i]; /* plot order */ - /* what about the current search operation, if any? */ - if (!highlighted && (search != NULL) && - search_term_highlighted(c, - offset, offset + len, - &start_idx, &end_idx, - search)) { - highlighted = true; - } + if (box->border[side].width == 0 || + nscss_color_is_transparent(box->border[side].c)) + continue; - /* \todo make search terms visible within selected text */ - if (highlighted) { - struct rect r; - unsigned endtxt_idx = end_idx; - bool clip_changed = false; - bool text_visible = true; - int startx, endx; - plot_style_t *pstyle_fill_hback = plot_style_fill_white; - plot_font_style_t fstyle_hback = plot_fstyle; + switch (side) { + case LEFT: + square_end_1 = (top == 0); + square_end_2 = (bottom == 0); - if (end_idx > utf8_len) { - /* adjust for trailing space, not present in - * utf8_text */ - assert(end_idx == utf8_len + 1); - endtxt_idx = utf8_len; + z[0] = p[0]; z[1] = p[7]; + z[2] = p[2]; z[3] = p[5]; + z[4] = p[2]; z[5] = p[3]; + z[6] = p[0]; z[7] = p[1]; + + if (nscss_color_is_transparent(box->border[TOP].c) == + false && + box->border[TOP].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang top corner fully, + * if top border is opaque */ + z[5] -= top; + square_end_1 = true; + } + if (nscss_color_is_transparent(box->border[BOTTOM].c) == + false && + box->border[BOTTOM].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang bottom corner fully, + * if bottom border is opaque */ + z[3] += bottom; + square_end_2 = true; } - if (!nsfont.font_width(fstyle, utf8_text, start_idx, - &startx)) - startx = 0; + col = nscss_color_to_ns(box->border[side].c); - if (!nsfont.font_width(fstyle, utf8_text, endtxt_idx, - &endx)) - endx = 0; + if (!html_redraw_border_plot(side, z, col, + box->border[side].style, + box->border[side].width * scale, + square_end_1 && square_end_2, + clip, ctx)) + return false; + break; + case RIGHT: + square_end_1 = (top == 0); + square_end_2 = (bottom == 0); - /* is there a trailing space that should be highlighted - * as well? */ - if (end_idx > utf8_len) { - endx += space; - } + z[0] = p[6]; z[1] = p[1]; + z[2] = p[4]; z[3] = p[3]; + z[4] = p[4]; z[5] = p[5]; + z[6] = p[6]; z[7] = p[7]; - if (scale != 1.0) { - startx *= scale; - endx *= scale; + if (nscss_color_is_transparent(box->border[TOP].c) == + false && + box->border[TOP].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang top corner fully, + * if top border is opaque */ + z[3] -= top; + square_end_1 = true; + } + if (nscss_color_is_transparent(box->border[BOTTOM].c) == + false && + box->border[BOTTOM].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang bottom corner fully, + * if bottom border is opaque */ + z[5] += bottom; + square_end_2 = true; } - /* draw any text preceding highlighted portion */ - if (start_idx > 0 && - !plot->text(x, y + (int)(height * 0.75 * scale), - utf8_text, start_idx, - &plot_fstyle)) - return false; - - /* decide whether highlighted portion is to be - * white-on-black or black-on-white */ - if ((fstyle->background & 0x808080) == 0x808080) - pstyle_fill_hback = plot_style_fill_black; + col = nscss_color_to_ns(box->border[side].c); - /* highlighted portion */ - if (!plot->rectangle(x + startx, y, x + endx, - y + height * scale, - pstyle_fill_hback)) + if (!html_redraw_border_plot(side, z, col, + box->border[side].style, + box->border[side].width * scale, + square_end_1 && square_end_2, + clip, ctx)) return false; + break; + case TOP: + if (clip->y0 > p[3]) + /* clip rectangle is below border; nothing to + * plot */ + continue; - if (start_idx > 0) { - int px0 = max(x + startx, clip->x0); - int px1 = min(x + endx, clip->x1); + square_end_1 = (left == 0); + square_end_2 = (right == 0); - if (px0 < px1) { - r.x0 = px0; - r.y0 = clip->y0; - r.x1 = px1; - r.y1 = clip->y1; - if (!plot->clip(&r)) - return false; - clip_changed = true; - } else { - text_visible = false; - } + z[0] = p[2]; z[1] = p[3]; + z[2] = p[0]; z[3] = p[1]; + z[4] = p[6]; z[5] = p[1]; + z[6] = p[4]; z[7] = p[3]; + + if (box->border[TOP].style == + CSS_BORDER_STYLE_SOLID && + box->border[TOP].c == + box->border[LEFT].c) { + /* don't bother overlapping left corner if + * it's the same colour anyway */ + z[2] += left; + square_end_1 = true; + } + if (box->border[TOP].style == + CSS_BORDER_STYLE_SOLID && + box->border[TOP].c == + box->border[RIGHT].c) { + /* don't bother overlapping right corner if + * it's the same colour anyway */ + z[4] -= right; + square_end_2 = true; } - fstyle_hback.background = - pstyle_fill_hback->fill_colour; - fstyle_hback.foreground = - pstyle_fill_hback->fill_colour ^ 0xffffff; + col = nscss_color_to_ns(box->border[side].c); - if (text_visible && - !plot->text(x, y + (int)(height * 0.75 * scale), - utf8_text, endtxt_idx, - &fstyle_hback)) + if (!html_redraw_border_plot(side, z, col, + box->border[side].style, + box->border[side].width * scale, + square_end_1 && square_end_2, + clip, ctx)) return false; + break; + case BOTTOM: + if (clip->y1 < p[5]) + /* clip rectangle is above border; nothing to + * plot */ + continue; - /* draw any text succeeding highlighted portion */ - if (endtxt_idx < utf8_len) { - int px0 = max(x + endx, clip->x0); - if (px0 < clip->x1) { - - r.x0 = px0; - r.y0 = clip->y0; - r.x1 = clip->x1; - r.y1 = clip->y1; - if (!plot->clip(&r)) - return false; + square_end_1 = (left == 0); + square_end_2 = (right == 0); - clip_changed = true; + z[0] = p[4]; z[1] = p[5]; + z[2] = p[6]; z[3] = p[7]; + z[4] = p[0]; z[5] = p[7]; + z[6] = p[2]; z[7] = p[5]; - if (!plot->text(x, y + (int) - (height * 0.75 * scale), - utf8_text, utf8_len, - &plot_fstyle)) - return false; - } + if (box->border[BOTTOM].style == + CSS_BORDER_STYLE_SOLID && + box->border[BOTTOM].c == + box->border[LEFT].c) { + /* don't bother overlapping left corner if + * it's the same colour anyway */ + z[4] += left; + square_end_1 = true; + } + if (box->border[BOTTOM].style == + CSS_BORDER_STYLE_SOLID && + box->border[BOTTOM].c == + box->border[RIGHT].c) { + /* don't bother overlapping right corner if + * it's the same colour anyway */ + z[2] -= right; + square_end_2 = true; } - if (clip_changed && - !plot->clip(clip)) - return false; - } - } + col = nscss_color_to_ns(box->border[side].c); - if (!highlighted) { - if (!plot->text(x, y + (int) (height * 0.75 * scale), - utf8_text, utf8_len, - &plot_fstyle)) - return false; + if (!html_redraw_border_plot(side, z, col, + box->border[side].style, + box->border[side].width * scale, + square_end_1 && square_end_2, + clip, ctx)) + return false; + break; + default: + assert(side == TOP || side == BOTTOM || + side == LEFT || side == RIGHT); + break; + } } + return true; } /** - * Draw borders for a box. + * Draw an inline's borders. * - * \param box box to draw - * \param x_parent coordinate of left padding edge of parent of box - * \param y_parent coordinate of top padding edge of parent of box - * \param p_width width of padding box - * \param p_height height of padding box - * \param scale scale for redraw - * \param ctx current redraw context + * \param box BOX_INLINE which created the border + * \param b coordinates of border edge rectangle + * \param scale scale for redraw + * \param first true if this is the first rectangle associated with the inline + * \param last true if this is the last rectangle associated with the inline + * \param ctx current redraw context * \return true if successful, false otherwise */ -bool html_redraw_borders(struct box *box, int x_parent, int y_parent, - int p_width, int p_height, const struct rect *clip, float scale, +static bool html_redraw_inline_borders(struct box *box, struct rect b, + const struct rect *clip, float scale, bool first, bool last, const struct redraw_context *ctx) { - unsigned int sides[] = { LEFT, RIGHT, TOP, BOTTOM }; int top = box->border[TOP].width; int right = box->border[RIGHT].width; int bottom = box->border[BOTTOM].width; int left = box->border[LEFT].width; - int x, y; - unsigned int i, side; + colour col; int p[8]; /* Box border vertices */ int z[8]; /* Border vertices */ - bool square_end_1 = false; - bool square_end_2 = false; - - x = x_parent + box->x; - y = y_parent + box->y; + bool square_end_1; + bool square_end_2; if (scale != 1.0) { top *= scale; right *= scale; bottom *= scale; left *= scale; - x *= scale; - y *= scale; } - assert(box->style); - /* Calculate border vertices * * A----------------------+ @@ -1138,771 +956,689 @@ bool html_redraw_borders(struct box *box, int x_parent, int y_parent, * | / \ | * +----------------------D */ - p[0] = x - left; p[1] = y - top; /* A */ - p[2] = x; p[3] = y; /* B */ - p[4] = x + p_width; p[5] = y + p_height; /* C */ - p[6] = x + p_width + right; p[7] = y + p_height + bottom; /* D */ + p[0] = b.x0; p[1] = b.y0; /* A */ + p[2] = first ? b.x0 + left : b.x0; p[3] = b.y0 + top; /* B */ + p[4] = last ? b.x1 - right : b.x1; p[5] = b.y1 - bottom; /* C */ + p[6] = b.x1; p[7] = b.y1; /* D */ - for (i = 0; i != 4; i++) { - colour col = 0; - side = sides[i]; /* plot order */ + assert(box->style); - if (box->border[side].width == 0 || - nscss_color_is_transparent(box->border[side].c)) - continue; + /* Left */ + square_end_1 = (top == 0); + square_end_2 = (bottom == 0); + if (left != 0 && first && nscss_color_is_transparent( + box->border[LEFT].c) == false) { + col = nscss_color_to_ns(box->border[LEFT].c); - switch (side) { - case LEFT: - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); + z[0] = p[0]; z[1] = p[7]; + z[2] = p[2]; z[3] = p[5]; + z[4] = p[2]; z[5] = p[3]; + z[6] = p[0]; z[7] = p[1]; - z[0] = p[0]; z[1] = p[7]; - z[2] = p[2]; z[3] = p[5]; - z[4] = p[2]; z[5] = p[3]; - z[6] = p[0]; z[7] = p[1]; + if (nscss_color_is_transparent(box->border[TOP].c) == false && + box->border[TOP].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang top corner fully, + * if top border is opaque */ + z[5] -= top; + square_end_1 = true; + } - if (nscss_color_is_transparent(box->border[TOP].c) == - false && - box->border[TOP].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque */ - z[5] -= top; - square_end_1 = true; - } - if (nscss_color_is_transparent(box->border[BOTTOM].c) == - false && - box->border[BOTTOM].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque */ - z[3] += bottom; - square_end_2 = true; - } + if (nscss_color_is_transparent(box->border[BOTTOM].c) == + false && + box->border[BOTTOM].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang bottom corner fully, + * if bottom border is opaque */ + z[3] += bottom; + square_end_2 = true; + } - col = nscss_color_to_ns(box->border[side].c); + if (!html_redraw_border_plot(LEFT, z, col, + box->border[LEFT].style, + left, square_end_1 && square_end_2, + clip, ctx)) + return false; + } - if (!html_redraw_border_plot(side, z, col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, ctx)) - return false; - break; - case RIGHT: - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); + /* Right */ + square_end_1 = (top == 0); + square_end_2 = (bottom == 0); + if (right != 0 && last && nscss_color_is_transparent( + box->border[RIGHT].c) == false) { + col = nscss_color_to_ns(box->border[RIGHT].c); - z[0] = p[6]; z[1] = p[1]; - z[2] = p[4]; z[3] = p[3]; - z[4] = p[4]; z[5] = p[5]; - z[6] = p[6]; z[7] = p[7]; + z[0] = p[6]; z[1] = p[1]; + z[2] = p[4]; z[3] = p[3]; + z[4] = p[4]; z[5] = p[5]; + z[6] = p[6]; z[7] = p[7]; - if (nscss_color_is_transparent(box->border[TOP].c) == - false && - box->border[TOP].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque */ - z[3] -= top; - square_end_1 = true; - } - if (nscss_color_is_transparent(box->border[BOTTOM].c) == - false && - box->border[BOTTOM].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque */ - z[5] += bottom; - square_end_2 = true; - } + if (nscss_color_is_transparent(box->border[TOP].c) == false && + box->border[TOP].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang top corner fully, + * if top border is opaque */ + z[3] -= top; + square_end_1 = true; + } - col = nscss_color_to_ns(box->border[side].c); + if (nscss_color_is_transparent(box->border[BOTTOM].c) == + false && + box->border[BOTTOM].style != + CSS_BORDER_STYLE_DOUBLE) { + /* make border overhang bottom corner fully, + * if bottom border is opaque */ + z[5] += bottom; + square_end_2 = true; + } - if (!html_redraw_border_plot(side, z, col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, ctx)) - return false; - break; - case TOP: - if (clip->y0 > p[3]) - /* clip rectangle is below border; nothing to - * plot */ - continue; + if (!html_redraw_border_plot(RIGHT, z, col, + box->border[RIGHT].style, + right, square_end_1 && square_end_2, + clip, ctx)) + return false; + } - square_end_1 = (left == 0); - square_end_2 = (right == 0); + /* Top */ + square_end_1 = (left == 0); + square_end_2 = (right == 0); + if (top != 0 && nscss_color_is_transparent( + box->border[TOP].c) == false) { + col = nscss_color_to_ns(box->border[TOP].c); - z[0] = p[2]; z[1] = p[3]; - z[2] = p[0]; z[3] = p[1]; - z[4] = p[6]; z[5] = p[1]; - z[6] = p[4]; z[7] = p[3]; + z[0] = p[2]; z[1] = p[3]; + z[2] = p[0]; z[3] = p[1]; + z[4] = p[6]; z[5] = p[1]; + z[6] = p[4]; z[7] = p[3]; + + if (first && box->border[TOP].style == + CSS_BORDER_STYLE_SOLID && + box->border[TOP].c == + box->border[LEFT].c) { + /* don't bother overlapping left corner if + * it's the same colour anyway */ + z[2] += left; + square_end_1 = true; + } + + if (last && box->border[TOP].style == + CSS_BORDER_STYLE_SOLID && + box->border[TOP].c == + box->border[RIGHT].c) { + /* don't bother overlapping right corner if + * it's the same colour anyway */ + z[4] -= right; + square_end_2 = true; + } + + if (!html_redraw_border_plot(TOP, z, col, + box->border[TOP].style, + top, square_end_1 && square_end_2, + clip, ctx)) + return false; + } + + /* Bottom */ + square_end_1 = (left == 0); + square_end_2 = (right == 0); + if (bottom != 0 && nscss_color_is_transparent( + box->border[BOTTOM].c) == false) { + col = nscss_color_to_ns(box->border[BOTTOM].c); + + z[0] = p[4]; z[1] = p[5]; + z[2] = p[6]; z[3] = p[7]; + z[4] = p[0]; z[5] = p[7]; + z[6] = p[2]; z[7] = p[5]; + + if (first && box->border[BOTTOM].style == + CSS_BORDER_STYLE_SOLID && + box->border[BOTTOM].c == + box->border[LEFT].c) { + /* don't bother overlapping left corner if + * it's the same colour anyway */ + z[4] += left; + square_end_1 = true; + } + + if (last && box->border[BOTTOM].style == + CSS_BORDER_STYLE_SOLID && + box->border[BOTTOM].c == + box->border[RIGHT].c) { + /* don't bother overlapping right corner if + * it's the same colour anyway */ + z[2] -= right; + square_end_2 = true; + } - if (box->border[TOP].style == - CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == - box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway */ - z[2] += left; - square_end_1 = true; - } - if (box->border[TOP].style == - CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == - box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway */ - z[4] -= right; - square_end_2 = true; - } + if (!html_redraw_border_plot(BOTTOM, z, col, + box->border[BOTTOM].style, + bottom, square_end_1 && square_end_2, + clip, ctx)) + return false; + } - col = nscss_color_to_ns(box->border[side].c); + return true; +} - if (!html_redraw_border_plot(side, z, col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, ctx)) - return false; - break; - case BOTTOM: - if (clip->y1 < p[5]) - /* clip rectangle is above border; nothing to - * plot */ - continue; - square_end_1 = (left == 0); - square_end_2 = (right == 0); +/** + * Plot a checkbox. + * + * \param x left coordinate + * \param y top coordinate + * \param width dimensions of checkbox + * \param height dimensions of checkbox + * \param selected the checkbox is selected + * \param ctx current redraw context + * \return true if successful, false otherwise + */ - z[0] = p[4]; z[1] = p[5]; - z[2] = p[6]; z[3] = p[7]; - z[4] = p[0]; z[5] = p[7]; - z[6] = p[2]; z[7] = p[5]; +static bool html_redraw_checkbox(int x, int y, int width, int height, + bool selected, const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + double z = width * 0.15; + if (z == 0) + z = 1; - if (box->border[BOTTOM].style == - CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == - box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway */ - z[4] += left; - square_end_1 = true; - } - if (box->border[BOTTOM].style == - CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == - box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway */ - z[2] -= right; - square_end_2 = true; - } + if (!(plot->rectangle(x, y, x + width, y + height, + plot_style_fill_wbasec) && + plot->line(x, y, x + width, y, plot_style_stroke_darkwbasec) && + plot->line(x, y, x, y + height, plot_style_stroke_darkwbasec) && + plot->line(x + width, y, x + width, y + height, + plot_style_stroke_lightwbasec) && + plot->line(x, y + height, x + width, y + height, + plot_style_stroke_lightwbasec))) + return false; - col = nscss_color_to_ns(box->border[side].c); + if (selected) { + if (width < 12 || height < 12) { + /* render a solid box instead of a tick */ + if (!plot->rectangle(x + z + z, y + z + z, + x + width - z, y + height - z, + plot_style_fill_wblobc)) + return false; + } else { + /* render a tick, as it'll fit comfortably */ + if (!(plot->line(x + width - z, + y + z, + x + (z * 3), + y + height - z, + plot_style_stroke_wblobc) && - if (!html_redraw_border_plot(side, z, col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, ctx)) + plot->line(x + (z * 3), + y + height - z, + x + z + z, + y + (height / 2), + plot_style_stroke_wblobc))) return false; - break; - default: - assert(side == TOP || side == BOTTOM || - side == LEFT || side == RIGHT); - break; } } - return true; } /** - * Draw an inline's borders. + * Plot a radio icon. * - * \param box BOX_INLINE which created the border - * \param b coordinates of border edge rectangle - * \param scale scale for redraw - * \param first true if this is the first rectangle associated with the inline - * \param last true if this is the last rectangle associated with the inline - * \param ctx current redraw context + * \param x left coordinate + * \param y top coordinate + * \param width dimensions of radio icon + * \param height dimensions of radio icon + * \param selected the radio icon is selected + * \param ctx current redraw context * \return true if successful, false otherwise */ - -bool html_redraw_inline_borders(struct box *box, struct rect b, - const struct rect *clip, float scale, bool first, bool last, - const struct redraw_context *ctx) +static bool html_redraw_radio(int x, int y, int width, int height, + bool selected, const struct redraw_context *ctx) { - int top = box->border[TOP].width; - int right = box->border[RIGHT].width; - int bottom = box->border[BOTTOM].width; - int left = box->border[LEFT].width; - colour col; - int p[8]; /* Box border vertices */ - int z[8]; /* Border vertices */ - bool square_end_1; - bool square_end_2; + const struct plotter_table *plot = ctx->plot; - if (scale != 1.0) { - top *= scale; - right *= scale; - bottom *= scale; - left *= scale; - } + /* plot background of radio button */ + if (!plot->disc(x + width * 0.5, + y + height * 0.5, + width * 0.5 - 1, + plot_style_fill_wbasec)) + return false; - /* Calculate border vertices - * - * A----------------------+ - * | \ / | - * | B--------------+ | - * | | | | - * | +--------------C | - * | / \ | - * +----------------------D - */ - p[0] = b.x0; p[1] = b.y0; /* A */ - p[2] = first ? b.x0 + left : b.x0; p[3] = b.y0 + top; /* B */ - p[4] = last ? b.x1 - right : b.x1; p[5] = b.y1 - bottom; /* C */ - p[6] = b.x1; p[7] = b.y1; /* D */ + /* plot dark arc */ + if (!plot->arc(x + width * 0.5, + y + height * 0.5, + width * 0.5 - 1, + 45, + 225, + plot_style_fill_darkwbasec)) + return false; - assert(box->style); + /* plot light arc */ + if (!plot->arc(x + width * 0.5, + y + height * 0.5, + width * 0.5 - 1, + 225, + 45, + plot_style_fill_lightwbasec)) + return false; - /* Left */ - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); - if (left != 0 && first && nscss_color_is_transparent( - box->border[LEFT].c) == false) { - col = nscss_color_to_ns(box->border[LEFT].c); + if (selected) { + /* plot selection blob */ + if (!plot->disc(x + width * 0.5, + y + height * 0.5, + width * 0.3 - 1, + plot_style_fill_wblobc)) + return false; + } - z[0] = p[0]; z[1] = p[7]; - z[2] = p[2]; z[3] = p[5]; - z[4] = p[2]; z[5] = p[3]; - z[6] = p[0]; z[7] = p[1]; + return true; +} - if (nscss_color_is_transparent(box->border[TOP].c) == false && - box->border[TOP].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque */ - z[5] -= top; - square_end_1 = true; - } - if (nscss_color_is_transparent(box->border[BOTTOM].c) == - false && - box->border[BOTTOM].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque */ - z[3] += bottom; - square_end_2 = true; - } +/** + * Plot a file upload input. + * + * \param x left coordinate + * \param y top coordinate + * \param width dimensions of input + * \param height dimensions of input + * \param box box of input + * \param scale scale for redraw + * \param background_colour current background colour + * \param ctx current redraw context + * \return true if successful, false otherwise + */ + +static bool html_redraw_file(int x, int y, int width, int height, + struct box *box, float scale, colour background_colour, + const struct redraw_context *ctx) +{ + int text_width; + const char *text; + size_t length; + plot_font_style_t fstyle; - if (!html_redraw_border_plot(LEFT, z, col, - box->border[LEFT].style, - left, square_end_1 && square_end_2, - clip, ctx)) - return false; - } + font_plot_style_from_css(box->style, &fstyle); + fstyle.background = background_colour; - /* Right */ - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); - if (right != 0 && last && nscss_color_is_transparent( - box->border[RIGHT].c) == false) { - col = nscss_color_to_ns(box->border[RIGHT].c); + if (box->gadget->value) + text = box->gadget->value; + else + text = messages_get("Form_Drop"); + length = strlen(text); - z[0] = p[6]; z[1] = p[1]; - z[2] = p[4]; z[3] = p[3]; - z[4] = p[4]; z[5] = p[5]; - z[6] = p[6]; z[7] = p[7]; + if (!nsfont.font_width(&fstyle, text, length, &text_width)) + return false; + text_width *= scale; + if (width < text_width + 8) + x = x + width - text_width - 4; + else + x = x + 4; - if (nscss_color_is_transparent(box->border[TOP].c) == false && - box->border[TOP].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque */ - z[3] -= top; - square_end_1 = true; - } + return ctx->plot->text(x, y + height * 0.75, text, length, &fstyle); +} - if (nscss_color_is_transparent(box->border[BOTTOM].c) == - false && - box->border[BOTTOM].style != - CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque */ - z[5] += bottom; - square_end_2 = true; - } - if (!html_redraw_border_plot(RIGHT, z, col, - box->border[RIGHT].style, - right, square_end_1 && square_end_2, - clip, ctx)) - return false; - } +/** + * Plot background images. + * + * \param x coordinate of box + * \param y coordinate of box + * \param box box to draw background image of + * \param scale scale for redraw + * \param clip current clip rectangle + * \param background_colour current background colour + * \param background box containing background details (usually ::box) + * \param ctx current redraw context + * \return true if successful, false otherwise + * + * The reason for the presence of ::background is the backwards compatibility + * mess that is backgrounds on . The background will be drawn relative + * to ::box, using the background information contained within ::background. + */ - /* Top */ - square_end_1 = (left == 0); - square_end_2 = (right == 0); - if (top != 0 && nscss_color_is_transparent( - box->border[TOP].c) == false) { - col = nscss_color_to_ns(box->border[TOP].c); +static bool html_redraw_background(int x, int y, struct box *box, float scale, + const struct rect *clip, colour *background_colour, + struct box *background, const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + bool repeat_x = false; + bool repeat_y = false; + bool plot_colour = true; + bool plot_content; + bool clip_to_children = false; + struct box *clip_box = box; + int ox = x, oy = y; + int width, height; + css_fixed hpos = 0, vpos = 0; + css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; + struct box *parent; + struct rect r = *clip; + css_color bgcol; + plot_style_t pstyle_fill_bg = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = *background_colour, + }; - z[0] = p[2]; z[1] = p[3]; - z[2] = p[0]; z[3] = p[1]; - z[4] = p[6]; z[5] = p[1]; - z[6] = p[4]; z[7] = p[3]; + if (ctx->background_images == false) + return true; - if (first && box->border[TOP].style == - CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == - box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway */ - z[2] += left; - square_end_1 = true; - } + plot_content = (background->background != NULL); - if (last && box->border[TOP].style == - CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == - box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway */ - z[4] -= right; - square_end_2 = true; + if (plot_content) { + if (!box->parent) { + /* Root element, special case: + * background origin calc. is based on margin box */ + x -= box->margin[LEFT] * scale; + y -= box->margin[TOP] * scale; + width = box->margin[LEFT] + box->padding[LEFT] + + box->width + box->padding[RIGHT] + + box->margin[RIGHT]; + height = box->margin[TOP] + box->padding[TOP] + + box->height + box->padding[BOTTOM] + + box->margin[BOTTOM]; + } else { + width = box->padding[LEFT] + box->width + + box->padding[RIGHT]; + height = box->padding[TOP] + box->height + + box->padding[BOTTOM]; } + /* handle background-repeat */ + switch (css_computed_background_repeat(background->style)) { + case CSS_BACKGROUND_REPEAT_REPEAT: + repeat_x = repeat_y = true; + /* optimisation: only plot the colour if + * bitmap is not opaque */ + plot_colour = !content_get_opaque(background->background); + break; - if (!html_redraw_border_plot(TOP, z, col, - box->border[TOP].style, - top, square_end_1 && square_end_2, - clip, ctx)) - return false; - } + case CSS_BACKGROUND_REPEAT_REPEAT_X: + repeat_x = true; + break; - /* Bottom */ - square_end_1 = (left == 0); - square_end_2 = (right == 0); - if (bottom != 0 && nscss_color_is_transparent( - box->border[BOTTOM].c) == false) { - col = nscss_color_to_ns(box->border[BOTTOM].c); + case CSS_BACKGROUND_REPEAT_REPEAT_Y: + repeat_y = true; + break; - z[0] = p[4]; z[1] = p[5]; - z[2] = p[6]; z[3] = p[7]; - z[4] = p[0]; z[5] = p[7]; - z[6] = p[2]; z[7] = p[5]; + case CSS_BACKGROUND_REPEAT_NO_REPEAT: + break; - if (first && box->border[BOTTOM].style == - CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == - box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway */ - z[4] += left; - square_end_1 = true; + default: + break; } - if (last && box->border[BOTTOM].style == - CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == - box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway */ - z[2] -= right; - square_end_2 = true; + /* handle background-position */ + css_computed_background_position(background->style, + &hpos, &hunit, &vpos, &vunit); + if (hunit == CSS_UNIT_PCT) { + x += (width - + content_get_width(background->background)) * + scale * FIXTOFLT(hpos) / 100.; + } else { + x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, + background->style)) * scale); } - if (!html_redraw_border_plot(BOTTOM, z, col, - box->border[BOTTOM].style, - bottom, square_end_1 && square_end_2, - clip, ctx)) - return false; + if (vunit == CSS_UNIT_PCT) { + y += (height - + content_get_height(background->background)) * + scale * FIXTOFLT(vpos) / 100.; + } else { + y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, + background->style)) * scale); + } } - return true; -} + /* special case for table rows as their background needs + * to be clipped to all the cells */ + if (box->type == BOX_TABLE_ROW) { + css_fixed h = 0, v = 0; + css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; -static plot_style_t plot_style_bdr = { - .stroke_type = PLOT_OP_TYPE_DASH, -}; -static plot_style_t plot_style_fillbdr = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_dark = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_light = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_ddark = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_dlight = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; + for (parent = box->parent; + ((parent) && (parent->type != BOX_TABLE)); + parent = parent->parent); + assert(parent && (parent->style)); -/** - * Draw one border. - * - * \param side index of border side (TOP, RIGHT, BOTTOM, LEFT) - * \param p array of precomputed border vertices - * \param c colour for border - * \param style border line style - * \param thickness border thickness - * \param rectangular whether border is rectangular - * \param ctx current redraw context - * \return true if successful, false otherwise - */ + css_computed_border_spacing(parent->style, &h, &hu, &v, &vu); -bool html_redraw_border_plot(const int side, const int *p, colour c, - enum css_border_style_e style, int thickness, bool rectangular, - const struct rect *clip, const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - int z[8]; /* Vertices of border part */ - unsigned int light = side; - plot_style_t *plot_style_bdr_in; - plot_style_t *plot_style_bdr_out; + clip_to_children = (h > 0) || (v > 0); - if (c == NS_TRANSPARENT) - return true; + if (clip_to_children) + clip_box = box->children; + } - plot_style_bdr.stroke_type = PLOT_OP_TYPE_DASH; - plot_style_bdr.stroke_colour = c; - plot_style_bdr.stroke_width = thickness; - plot_style_fillbdr.fill_colour = c; - plot_style_fillbdr_dark.fill_colour = darken_colour(c); - plot_style_fillbdr_light.fill_colour = lighten_colour(c); - plot_style_fillbdr_ddark.fill_colour = double_darken_colour(c); - plot_style_fillbdr_dlight.fill_colour = double_lighten_colour(c); + for (; clip_box; clip_box = clip_box->next) { + /* clip to child boxes if needed */ + if (clip_to_children) { + assert(clip_box->type == BOX_TABLE_CELL); - switch (style) { - case CSS_BORDER_STYLE_DOTTED: - plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT; - /* fall through */ - case CSS_BORDER_STYLE_DASHED: - if (!plot->line((p[0] + p[2]) / 2, - (p[1] + p[3]) / 2, - (p[4] + p[6]) / 2, - (p[5] + p[7]) / 2, - &plot_style_bdr)) - return false; - break; + /* update clip.* to the child cell */ + r.x0 = ox + (clip_box->x * scale); + r.y0 = oy + (clip_box->y * scale); + r.x1 = r.x0 + (clip_box->padding[LEFT] + + clip_box->width + + clip_box->padding[RIGHT]) * scale; + r.y1 = r.y0 + (clip_box->padding[TOP] + + clip_box->height + + clip_box->padding[BOTTOM]) * scale; - case CSS_BORDER_STYLE_SOLID: - /* fall through to default */ - default: - if (rectangular || thickness == 1) { - int x0, y0, x1, y1; - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = p[6]; y1 = p[7]; - x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - x1 + p[4] - p[6] : x1; - } else { - x0 = p[6]; y0 = p[7]; - x1 = p[2]; y1 = p[3]; - y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - y1 + p[1] - p[3] : y1; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - &plot_style_fillbdr)) - return false; - } - } else { - if (!plot->polygon(p, 4, &plot_style_fillbdr)) - return false; - } - break; + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (r.x1 > clip->x1) r.x1 = clip->x1; + if (r.y1 > clip->y1) r.y1 = clip->y1; - case CSS_BORDER_STYLE_DOUBLE: - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] * 2 + p[2]) / 3; - z[3] = (p[1] * 2 + p[3]) / 3; - z[4] = (p[6] * 2 + p[4]) / 3; - z[5] = (p[7] * 2 + p[5]) / 3; - z[6] = p[6]; - z[7] = p[7]; - if (!plot->polygon(z, 4, &plot_style_fillbdr)) - return false; - z[0] = p[2]; - z[1] = p[3]; - z[2] = (p[2] * 2 + p[0]) / 3; - z[3] = (p[3] * 2 + p[1]) / 3; - z[4] = (p[4] * 2 + p[6]) / 3; - z[5] = (p[5] * 2 + p[7]) / 3; - z[6] = p[4]; - z[7] = p[5]; - if (!plot->polygon(z, 4, &plot_style_fillbdr)) - return false; - break; + css_computed_background_color(clip_box->style, &bgcol); - case CSS_BORDER_STYLE_GROOVE: - light = 3 - light; - /* fall through */ - case CSS_BORDER_STYLE_RIDGE: - /* choose correct colours for each part of the border line */ - if (light <= 1) { - plot_style_bdr_in = &plot_style_fillbdr_dark; - plot_style_bdr_out = &plot_style_fillbdr_light; - } else { - plot_style_bdr_in = &plot_style_fillbdr_light; - plot_style_bdr_out = &plot_style_fillbdr_dark; + /* attributes override */ + /* if the background content is opaque there + * is no need to plot underneath it. + */ + if ((r.x0 >= r.x1) || + (r.y0 >= r.y1) || + (nscss_color_is_transparent(bgcol) == false) || + ((clip_box->background != NULL) && + content_get_opaque(clip_box->background))) + continue; } - /* Render border */ - if ((rectangular || thickness == 2) && thickness != 1) { - /* Border made up from two parts and can be plotted - * with rectangles */ - int x0, y0, x1, y1; + /* plot the background colour */ + css_computed_background_color(background->style, &bgcol); - /* First part */ - if (side == TOP || side == RIGHT) { - x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; - x1 = p[6]; y1 = p[7]; - } else { - x0 = p[6]; y0 = p[7]; - x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) + if (nscss_color_is_transparent(bgcol) == false) { + *background_colour = nscss_color_to_ns(bgcol); + pstyle_fill_bg.fill_colour = *background_colour; + if (plot_colour) + if (!plot->rectangle(r.x0, r.y0, r.x1, r.y1, + &pstyle_fill_bg)) return false; - } + } + /* and plot the image */ + if (plot_content) { + width = content_get_width(background->background); + height = content_get_height(background->background); - /* Second part */ - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; - } else { - x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; - x1 = p[2]; y1 = p[3]; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; + /* ensure clip area only as large as required */ + if (!repeat_x) { + if (r.x0 < x) + r.x0 = x; + if (r.x1 > x + width * scale) + r.x1 = x + width * scale; } - } else if (thickness == 1) { - /* Border made up from one part which can be plotted - * as a rectangle */ - int x0, y0, x1, y1; - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = p[6]; y1 = p[7]; - x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - x1 + p[4] - p[6] : x1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) - return false; - } - } else { - x0 = p[6]; y0 = p[7]; - x1 = p[2]; y1 = p[3]; - y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - y1 + p[1] - p[3] : y1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; - } + if (!repeat_y) { + if (r.y0 < y) + r.y0 = y; + if (r.y1 > y + height * scale) + r.y1 = y + height * scale; + } + /* valid clipping rectangles only */ + if ((r.x0 < r.x1) && (r.y0 < r.y1)) { + struct content_redraw_data bg_data; + + if (!plot->clip(&r)) + return false; + + bg_data.x = x; + bg_data.y = y; + bg_data.width = ceilf(width * scale); + bg_data.height = ceilf(height * scale); + bg_data.background_colour = *background_colour; + bg_data.scale = scale; + bg_data.repeat_x = repeat_x; + bg_data.repeat_y = repeat_y; + + if (!content_redraw(background->background, + &bg_data, &r, ctx)) + return false; } - } else { - /* Border made up from two parts and can't be plotted - * with rectangles */ - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] + p[2]) / 2; - z[3] = (p[1] + p[3]) / 2; - z[4] = (p[6] + p[4]) / 2; - z[5] = (p[7] + p[5]) / 2; - z[6] = p[6]; - z[7] = p[7]; - if (!plot->polygon(z, 4, plot_style_bdr_in)) - return false; - z[0] = p[2]; - z[1] = p[3]; - z[6] = p[4]; - z[7] = p[5]; - if (!plot->polygon(z, 4, plot_style_bdr_out)) - return false; } - break; - case CSS_BORDER_STYLE_INSET: - light = (light + 2) % 4; - /* fall through */ - case CSS_BORDER_STYLE_OUTSET: - /* choose correct colours for each part of the border line */ - switch (light) { - case 0: - plot_style_bdr_in = &plot_style_fillbdr_light; - plot_style_bdr_out = &plot_style_fillbdr_dlight; + /* only rows being clipped to child boxes loop */ + if (!clip_to_children) + return true; + } + return true; +} + + +/** + * Plot an inline's background and/or background image. + * + * \param x coordinate of box + * \param y coordinate of box + * \param box BOX_INLINE which created the background + * \param scale scale for redraw + * \param clip coordinates of clip rectangle + * \param b coordinates of border edge rectangle + * \param first true if this is the first rectangle associated with the inline + * \param last true if this is the last rectangle associated with the inline + * \param background_colour updated to current background colour if plotted + * \param ctx current redraw context + * \return true if successful, false otherwise + */ + +static bool html_redraw_inline_background(int x, int y, struct box *box, + float scale, const struct rect *clip, struct rect b, + bool first, bool last, colour *background_colour, + const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + struct rect r = *clip; + bool repeat_x = false; + bool repeat_y = false; + bool plot_colour = true; + bool plot_content; + css_fixed hpos = 0, vpos = 0; + css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; + css_color bgcol; + plot_style_t pstyle_fill_bg = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = *background_colour, + }; + + plot_content = (box->background != NULL); + + if (html_redraw_printing && nsoption_bool(remove_backgrounds)) + return true; + + if (plot_content) { + /* handle background-repeat */ + switch (css_computed_background_repeat(box->style)) { + case CSS_BACKGROUND_REPEAT_REPEAT: + repeat_x = repeat_y = true; + /* optimisation: only plot the colour if + * bitmap is not opaque + */ + plot_colour = !content_get_opaque(box->background); break; - case 1: - plot_style_bdr_in = &plot_style_fillbdr_ddark; - plot_style_bdr_out = &plot_style_fillbdr_dark; + + case CSS_BACKGROUND_REPEAT_REPEAT_X: + repeat_x = true; break; - case 2: - plot_style_bdr_in = &plot_style_fillbdr_dark; - plot_style_bdr_out = &plot_style_fillbdr_ddark; + + case CSS_BACKGROUND_REPEAT_REPEAT_Y: + repeat_y = true; break; - case 3: - plot_style_bdr_in = &plot_style_fillbdr_dlight; - plot_style_bdr_out = &plot_style_fillbdr_light; + + case CSS_BACKGROUND_REPEAT_NO_REPEAT: break; + default: - plot_style_bdr_in = &plot_style_fillbdr; - plot_style_bdr_out = &plot_style_fillbdr; break; } - /* Render border */ - if ((rectangular || thickness == 2) && thickness != 1) { - /* Border made up from two parts and can be plotted - * with rectangles */ - int x0, y0, x1, y1; + /* handle background-position */ + css_computed_background_position(box->style, + &hpos, &hunit, &vpos, &vunit); + if (hunit == CSS_UNIT_PCT) { + x += (b.x1 - b.x0 - + content_get_width(box->background) * + scale) * FIXTOFLT(hpos) / 100.; - /* First part */ - if (side == TOP || side == RIGHT) { - x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; - x1 = p[6]; y1 = p[7]; - } else { - x0 = p[6]; y0 = p[7]; - x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) - return false; + if (!repeat_x && ((hpos < 2 && !first) || + (hpos > 98 && !last))){ + plot_content = false; } + } else { + x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, + box->style)) * scale); + } - /* Second part */ - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; - } else { - x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; - x1 = p[2]; y1 = p[3]; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; - } - } else if (thickness == 1) { - /* Border made up from one part which can be plotted - * as a rectangle */ - int x0, y0, x1, y1; - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = p[6]; y1 = p[7]; - x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - x1 + p[4] - p[6] : x1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) - return false; - } - } else { - x0 = p[6]; y0 = p[7]; - x1 = p[2]; y1 = p[3]; - y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - y1 + p[1] - p[3] : y1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; - } - } + if (vunit == CSS_UNIT_PCT) { + y += (b.y1 - b.y0 - + content_get_height(box->background) * + scale) * FIXTOFLT(vpos) / 100.; } else { - /* Border made up from two parts and can't be plotted - * with rectangles */ - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] + p[2]) / 2; - z[3] = (p[1] + p[3]) / 2; - z[4] = (p[6] + p[4]) / 2; - z[5] = (p[7] + p[5]) / 2; - z[6] = p[6]; - z[7] = p[7]; - if (!plot->polygon(z, 4, plot_style_bdr_in)) + y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, + box->style)) * scale); + } + } + + /* plot the background colour */ + css_computed_background_color(box->style, &bgcol); + + if (nscss_color_is_transparent(bgcol) == false) { + *background_colour = nscss_color_to_ns(bgcol); + pstyle_fill_bg.fill_colour = *background_colour; + + if (plot_colour) + if (!plot->rectangle(r.x0, r.y0, r.x1, r.y1, + &pstyle_fill_bg)) return false; - z[0] = p[2]; - z[1] = p[3]; - z[6] = p[4]; - z[7] = p[5]; - if (!plot->polygon(z, 4, plot_style_bdr_out)) + } + /* and plot the image */ + if (plot_content) { + int width = content_get_width(box->background); + int height = content_get_height(box->background); + + if (!repeat_x) { + if (r.x0 < x) + r.x0 = x; + if (r.x1 > x + width * scale) + r.x1 = x + width * scale; + } + if (!repeat_y) { + if (r.y0 < y) + r.y0 = y; + if (r.y1 > y + height * scale) + r.y1 = y + height * scale; + } + /* valid clipping rectangles only */ + if ((r.x0 < r.x1) && (r.y0 < r.y1)) { + struct content_redraw_data bg_data; + + if (!plot->clip(&r)) + return false; + + bg_data.x = x; + bg_data.y = y; + bg_data.width = ceilf(width * scale); + bg_data.height = ceilf(height * scale); + bg_data.background_colour = *background_colour; + bg_data.scale = scale; + bg_data.repeat_x = repeat_x; + bg_data.repeat_y = repeat_y; + + if (!content_redraw(box->background, &bg_data, &r, ctx)) return false; } - break; } return true; @@ -1910,55 +1646,83 @@ bool html_redraw_border_plot(const int side, const int *p, colour c, /** - * Plot a checkbox. + * Plot text decoration for an inline box. * - * \param x left coordinate - * \param y top coordinate - * \param width dimensions of checkbox - * \param height dimensions of checkbox - * \param selected the checkbox is selected - * \param ctx current redraw context + * \param box box to plot decorations for, of type BOX_INLINE + * \param x x coordinate of parent of box + * \param y y coordinate of parent of box + * \param scale scale for redraw + * \param colour colour for decorations + * \param ratio position of line as a ratio of line height + * \param ctx current redraw context * \return true if successful, false otherwise */ -bool html_redraw_checkbox(int x, int y, int width, int height, bool selected, +static bool html_redraw_text_decoration_inline(struct box *box, int x, int y, + float scale, colour colour, float ratio, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; - double z = width * 0.15; - if (z == 0) - z = 1; + struct box *c; + plot_style_t plot_style_box = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_colour = colour, + }; - if (!(plot->rectangle(x, y, x + width, y + height, - plot_style_fill_wbasec) && - plot->line(x, y, x + width, y, plot_style_stroke_darkwbasec) && - plot->line(x, y, x, y + height, plot_style_stroke_darkwbasec) && - plot->line(x + width, y, x + width, y + height, - plot_style_stroke_lightwbasec) && - plot->line(x, y + height, x + width, y + height, - plot_style_stroke_lightwbasec))) - return false; + for (c = box->next; + c && c != box->inline_end; + c = c->next) { + if (c->type != BOX_TEXT) + continue; + if (!plot->line((x + c->x) * scale, + (y + c->y + c->height * ratio) * scale, + (x + c->x + c->width) * scale, + (y + c->y + c->height * ratio) * scale, + &plot_style_box)) + return false; + } + return true; +} - if (selected) { - if (width < 12 || height < 12) { - /* render a solid box instead of a tick */ - if (!plot->rectangle(x + z + z, y + z + z, - x + width - z, y + height - z, - plot_style_fill_wblobc)) - return false; - } else { - /* render a tick, as it'll fit comfortably */ - if (!(plot->line(x + width - z, - y + z, - x + (z * 3), - y + height - z, - plot_style_stroke_wblobc) && - plot->line(x + (z * 3), - y + height - z, - x + z + z, - y + (height / 2), - plot_style_stroke_wblobc))) +/** + * Plot text decoration for an non-inline box. + * + * \param box box to plot decorations for, of type other than BOX_INLINE + * \param x x coordinate of box + * \param y y coordinate of box + * \param scale scale for redraw + * \param colour colour for decorations + * \param ratio position of line as a ratio of line height + * \param ctx current redraw context + * \return true if successful, false otherwise + */ + +static bool html_redraw_text_decoration_block(struct box *box, int x, int y, + float scale, colour colour, float ratio, + const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + struct box *c; + plot_style_t plot_style_box = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_colour = colour, + }; + + /* draw through text descendants */ + for (c = box->children; c; c = c->next) { + if (c->type == BOX_TEXT) { + if (!plot->line((x + c->x) * scale, + (y + c->y + c->height * ratio) * scale, + (x + c->x + c->width) * scale, + (y + c->y + c->height * ratio) * scale, + &plot_style_box)) + return false; + } else if (c->type == BOX_INLINE_CONTAINER || + c->type == BOX_BLOCK) { + if (!html_redraw_text_decoration_block(c, + x + c->x, y + c->y, + scale, colour, ratio, ctx)) return false; } } @@ -1967,53 +1731,56 @@ bool html_redraw_checkbox(int x, int y, int width, int height, bool selected, /** - * Plot a radio icon. + * Plot text decoration for a box. * - * \param x left coordinate - * \param y top coordinate - * \param width dimensions of radio icon - * \param height dimensions of radio icon - * \param selected the radio icon is selected + * \param box box to plot decorations for + * \param x_parent x coordinate of parent of box + * \param y_parent y coordinate of parent of box + * \param scale scale for redraw + * \param background_colour current background colour * \param ctx current redraw context * \return true if successful, false otherwise */ -bool html_redraw_radio(int x, int y, int width, int height, bool selected, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - /* plot background of radio button */ - if (!plot->disc(x + width * 0.5, - y + height * 0.5, - width * 0.5 - 1, - plot_style_fill_wbasec)) - return false; +static bool html_redraw_text_decoration(struct box *box, + int x_parent, int y_parent, float scale, + colour background_colour, const struct redraw_context *ctx) +{ + static const enum css_text_decoration_e decoration[] = { + CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE, + CSS_TEXT_DECORATION_LINE_THROUGH }; + static const float line_ratio[] = { 0.9, 0.1, 0.5 }; + colour fgcol; + unsigned int i; + css_color col; - /* plot dark arc */ - if (!plot->arc(x + width * 0.5, - y + height * 0.5, - width * 0.5 - 1, - 45, - 225, - plot_style_fill_darkwbasec)) - return false; + css_computed_color(box->style, &col); + fgcol = nscss_color_to_ns(col); - /* plot light arc */ - if (!plot->arc(x + width * 0.5, - y + height * 0.5, - width * 0.5 - 1, - 225, - 45, - plot_style_fill_lightwbasec)) - return false; + /* antialias colour for under/overline */ + if (html_redraw_printing == false) + fgcol = blend_colour(background_colour, fgcol); - if (selected) { - /* plot selection blob */ - if (!plot->disc(x + width * 0.5, - y + height * 0.5, - width * 0.3 - 1, - plot_style_fill_wblobc)) - return false; + if (box->type == BOX_INLINE) { + if (!box->inline_end) + return true; + for (i = 0; i != NOF_ELEMENTS(decoration); i++) + if (css_computed_text_decoration(box->style) & + decoration[i]) + if (!html_redraw_text_decoration_inline(box, + x_parent, y_parent, scale, + fgcol, line_ratio[i], ctx)) + return false; + } else { + for (i = 0; i != NOF_ELEMENTS(decoration); i++) + if (css_computed_text_decoration(box->style) & + decoration[i]) + if (!html_redraw_text_decoration_block(box, + x_parent + box->x, + y_parent + box->y, + scale, + fgcol, line_ratio[i], ctx)) + return false; } return true; @@ -2021,554 +1788,750 @@ bool html_redraw_radio(int x, int y, int width, int height, bool selected, /** - * Plot a file upload input. + * Redraw the text content of a box, possibly partially highlighted + * because the text has been selected, or matches a search operation. * - * \param x left coordinate - * \param y top coordinate - * \param width dimensions of input - * \param height dimensions of input - * \param box box of input + * \param box box with text content + * \param x x co-ord of box + * \param y y co-ord of box + * \param clip current clip rectangle + * \param scale current scale setting (1.0 = 100%) + * \param current_background_color + * \param ctx current redraw context + * \return true iff successful and redraw should proceed + */ + +static bool html_redraw_text_box(const html_content *html, struct box *box, + int x, int y, const struct rect *clip, float scale, + colour current_background_color, + const struct redraw_context *ctx) +{ + bool excluded = (box->object != NULL); + plot_font_style_t fstyle; + + font_plot_style_from_css(box->style, &fstyle); + fstyle.background = current_background_color; + + if (!text_redraw(box->text, box->length, box->byte_offset, + box->space, &fstyle, x, y, + clip, box->height, scale, excluded, + (struct content *)html, &html->sel, + html->search, ctx)) + return false; + + return true; +} + +bool html_redraw_box(const html_content *html, struct box *box, + int x_parent, int y_parent, + const struct rect *clip, float scale, + colour current_background_color, + const struct redraw_context *ctx); + +/** + * Draw the various children of a box. + * + * \param html html content + * \param box box to draw children of + * \param x_parent coordinate of parent box + * \param y_parent coordinate of parent box + * \param clip clip rectangle * \param scale scale for redraw - * \param background_colour current background colour + * \param current_background_color background colour under this box * \param ctx current redraw context * \return true if successful, false otherwise */ -bool html_redraw_file(int x, int y, int width, int height, - struct box *box, float scale, colour background_colour, +static bool html_redraw_box_children(const html_content *html, struct box *box, + int x_parent, int y_parent, + const struct rect *clip, float scale, + colour current_background_color, const struct redraw_context *ctx) { - int text_width; - const char *text; - size_t length; - plot_font_style_t fstyle; - - font_plot_style_from_css(box->style, &fstyle); - fstyle.background = background_colour; + struct box *c; - if (box->gadget->value) - text = box->gadget->value; - else - text = messages_get("Form_Drop"); - length = strlen(text); + for (c = box->children; c; c = c->next) { - if (!nsfont.font_width(&fstyle, text, length, &text_width)) - return false; - text_width *= scale; - if (width < text_width + 8) - x = x + width - text_width - 4; - else - x = x + 4; + if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) + if (!html_redraw_box(html, c, + x_parent + box->x - + scrollbar_get_offset(box->scroll_x), + y_parent + box->y - + scrollbar_get_offset(box->scroll_y), + clip, scale, current_background_color, + ctx)) + return false; + } + for (c = box->float_children; c; c = c->next_float) + if (!html_redraw_box(html, c, + x_parent + box->x - + scrollbar_get_offset(box->scroll_x), + y_parent + box->y - + scrollbar_get_offset(box->scroll_y), + clip, scale, current_background_color, + ctx)) + return false; - return ctx->plot->text(x, y + height * 0.75, text, length, &fstyle); + return true; } - /** - * Plot background images. + * Recursively draw a box. * - * \param x coordinate of box - * \param y coordinate of box - * \param box box to draw background image of - * \param scale scale for redraw - * \param clip current clip rectangle - * \param background_colour current background colour - * \param background box containing background details (usually ::box) - * \param ctx current redraw context + * \param html html content + * \param box box to draw + * \param x_parent coordinate of parent box + * \param y_parent coordinate of parent box + * \param clip clip rectangle + * \param scale scale for redraw + * \param current_background_color background colour under this box + * \param ctx current redraw context * \return true if successful, false otherwise * - * The reason for the presence of ::background is the backwards compatibility - * mess that is backgrounds on . The background will be drawn relative - * to ::box, using the background information contained within ::background. + * x, y, clip_[xy][01] are in target coordinates. */ -bool html_redraw_background(int x, int y, struct box *box, float scale, - const struct rect *clip, colour *background_colour, - struct box *background, const struct redraw_context *ctx) +bool html_redraw_box(const html_content *html, struct box *box, + int x_parent, int y_parent, + const struct rect *clip, float scale, + colour current_background_color, + const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; - bool repeat_x = false; - bool repeat_y = false; - bool plot_colour = true; - bool plot_content; - bool clip_to_children = false; - struct box *clip_box = box; - int ox = x, oy = y; + int x, y; int width, height; - css_fixed hpos = 0, vpos = 0; - css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; - struct box *parent; - struct rect r = *clip; - css_color bgcol; - plot_style_t pstyle_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = *background_colour, - }; + int padding_left, padding_top, padding_width, padding_height; + int border_left, border_top, border_right, border_bottom; + struct rect r; + int x_scrolled, y_scrolled; + struct box *bg_box = NULL; + bool has_x_scroll, has_y_scroll; + css_computed_clip_rect css_rect; - if (ctx->background_images == false) + if (html_redraw_printing && (box->flags & PRINTED)) return true; - plot_content = (background->background != NULL); + /* avoid trivial FP maths */ + if (scale == 1.0) { + x = x_parent + box->x; + y = y_parent + box->y; + width = box->width; + height = box->height; + padding_left = box->padding[LEFT]; + padding_top = box->padding[TOP]; + padding_width = padding_left + box->width + box->padding[RIGHT]; + padding_height = padding_top + box->height + + box->padding[BOTTOM]; + border_left = box->border[LEFT].width; + border_top = box->border[TOP].width; + border_right = box->border[RIGHT].width; + border_bottom = box->border[BOTTOM].width; + } else { + x = (x_parent + box->x) * scale; + y = (y_parent + box->y) * scale; + width = box->width * scale; + height = box->height * scale; + /* left and top padding values are normally zero, + * so avoid trivial FP maths */ + padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale + : 0; + padding_top = box->padding[TOP] ? box->padding[TOP] * scale + : 0; + padding_width = (box->padding[LEFT] + box->width + + box->padding[RIGHT]) * scale; + padding_height = (box->padding[TOP] + box->height + + box->padding[BOTTOM]) * scale; + border_left = box->border[LEFT].width * scale; + border_top = box->border[TOP].width * scale; + border_right = box->border[RIGHT].width * scale; + border_bottom = box->border[BOTTOM].width * scale; + } - if (plot_content) { - if (!box->parent) { - /* Root element, special case: - * background origin calc. is based on margin box */ - x -= box->margin[LEFT] * scale; - y -= box->margin[TOP] * scale; - width = box->margin[LEFT] + box->padding[LEFT] + - box->width + box->padding[RIGHT] + - box->margin[RIGHT]; - height = box->margin[TOP] + box->padding[TOP] + - box->height + box->padding[BOTTOM] + - box->margin[BOTTOM]; + /* calculate rectangle covering this box and descendants */ + if (box->style && css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) { + /* box contents clipped to box size */ + r.x0 = x - border_left; + r.y0 = y - border_top; + r.x1 = x + padding_width + border_right; + r.y1 = y + padding_height + border_bottom; + } else { + /* box contents can hang out of the box; use descendant box */ + if (scale == 1.0) { + r.x0 = x + box->descendant_x0; + r.y0 = y + box->descendant_y0; + r.x1 = x + box->descendant_x1 + 1; + r.y1 = y + box->descendant_y1 + 1; } else { - width = box->padding[LEFT] + box->width + - box->padding[RIGHT]; - height = box->padding[TOP] + box->height + - box->padding[BOTTOM]; + r.x0 = x + box->descendant_x0 * scale; + r.y0 = y + box->descendant_y0 * scale; + r.x1 = x + box->descendant_x1 * scale + 1; + r.y1 = y + box->descendant_y1 * scale + 1; } - /* handle background-repeat */ - switch (css_computed_background_repeat(background->style)) { - case CSS_BACKGROUND_REPEAT_REPEAT: - repeat_x = repeat_y = true; - /* optimisation: only plot the colour if - * bitmap is not opaque */ - plot_colour = !content_get_opaque(background->background); - break; - - case CSS_BACKGROUND_REPEAT_REPEAT_X: - repeat_x = true; - break; - - case CSS_BACKGROUND_REPEAT_REPEAT_Y: - repeat_y = true; - break; - - case CSS_BACKGROUND_REPEAT_NO_REPEAT: - break; - - default: - break; + if (!box->parent) { + /* root element */ + int margin_left, margin_right; + int margin_top, margin_bottom; + if (scale == 1.0) { + margin_left = box->margin[LEFT]; + margin_top = box->margin[TOP]; + margin_right = box->margin[RIGHT]; + margin_bottom = box->margin[BOTTOM]; + } else { + margin_left = box->margin[LEFT] * scale; + margin_top = box->margin[TOP] * scale; + margin_right = box->margin[RIGHT] * scale; + margin_bottom = box->margin[BOTTOM] * scale; + } + r.x0 = x - border_left - margin_left < r.x0 ? + x - border_left - margin_left : r.x0; + r.y0 = y - border_top - margin_top < r.y0 ? + y - border_top - margin_top : r.y0; + r.x1 = x + padding_width + border_right + + margin_right > r.x1 ? + x + padding_width + border_right + + margin_right : r.x1; + r.y1 = y + padding_height + border_bottom + + margin_bottom > r.y1 ? + y + padding_height + border_bottom + + margin_bottom : r.y1; } + } - /* handle background-position */ - css_computed_background_position(background->style, - &hpos, &hunit, &vpos, &vunit); - if (hunit == CSS_UNIT_PCT) { - x += (width - - content_get_width(background->background)) * - scale * FIXTOFLT(hpos) / 100.; - } else { - x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, - background->style)) * scale); - } + /* return if the rectangle is completely outside the clip rectangle */ + if (clip->y1 < r.y0 || r.y1 < clip->y0 || + clip->x1 < r.x0 || r.x1 < clip->x0) + return true; - if (vunit == CSS_UNIT_PCT) { - y += (height - - content_get_height(background->background)) * - scale * FIXTOFLT(vpos) / 100.; - } else { - y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, - background->style)) * scale); + /*if the rectangle is under the page bottom but it can fit in a page, + don't print it now*/ + if (html_redraw_printing) { + if (r.y1 > html_redraw_printing_border) { + if (r.y1 - r.y0 <= html_redraw_printing_border && + (box->type == BOX_TEXT || + box->type == BOX_TABLE_CELL + || box->object || box->gadget)) { + /*remember the highest of all points from the + not printed elements*/ + if (r.y0 < html_redraw_printing_top_cropped) + html_redraw_printing_top_cropped = r.y0; + return true; + } } + else box->flags |= PRINTED; /*it won't be printed anymore*/ } - /* special case for table rows as their background needs - * to be clipped to all the cells */ - if (box->type == BOX_TABLE_ROW) { - css_fixed h = 0, v = 0; - css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; - - for (parent = box->parent; - ((parent) && (parent->type != BOX_TABLE)); - parent = parent->parent); - assert(parent && (parent->style)); - - css_computed_border_spacing(parent->style, &h, &hu, &v, &vu); - - clip_to_children = (h > 0) || (v > 0); - - if (clip_to_children) - clip_box = box->children; + /* if visibility is hidden render children only */ + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) { + if ((plot->group_start) && (!plot->group_start("hidden box"))) + return false; + if (!html_redraw_box_children(html, box, x_parent, y_parent, + &r, scale, current_background_color, ctx)) + return false; + return ((!plot->group_end) || (plot->group_end())); } - for (; clip_box; clip_box = clip_box->next) { - /* clip to child boxes if needed */ - if (clip_to_children) { - assert(clip_box->type == BOX_TABLE_CELL); + if ((plot->group_start) && (!plot->group_start("vis box"))) + return false; - /* update clip.* to the child cell */ - r.x0 = ox + (clip_box->x * scale); - r.y0 = oy + (clip_box->y * scale); - r.x1 = r.x0 + (clip_box->padding[LEFT] + - clip_box->width + - clip_box->padding[RIGHT]) * scale; - r.y1 = r.y0 + (clip_box->padding[TOP] + - clip_box->height + - clip_box->padding[BOTTOM]) * scale; - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (r.x1 > clip->x1) r.x1 = clip->x1; - if (r.y1 > clip->y1) r.y1 = clip->y1; + if (box->style != NULL && + css_computed_position(box->style) == + CSS_POSITION_ABSOLUTE && + css_computed_clip(box->style, &css_rect) == + CSS_CLIP_RECT) { + /* We have an absolutly positioned box with a clip rect */ + if (css_rect.left_auto == false) + r.x0 = x - border_left + FIXTOINT(nscss_len2px( + css_rect.left, css_rect.lunit, + box->style)); - css_computed_background_color(clip_box->style, &bgcol); + if (css_rect.top_auto == false) + r.y0 = y - border_top + FIXTOINT(nscss_len2px( + css_rect.top, css_rect.tunit, + box->style)); - /* attributes override */ - /* if the background content is opaque there - * is no need to plot underneath it. - */ - if ((r.x0 >= r.x1) || - (r.y0 >= r.y1) || - (nscss_color_is_transparent(bgcol) == false) || - ((clip_box->background != NULL) && - content_get_opaque(clip_box->background))) - continue; - } + if (css_rect.right_auto == false) + r.x1 = x - border_left + FIXTOINT(nscss_len2px( + css_rect.right, css_rect.runit, + box->style)); - /* plot the background colour */ - css_computed_background_color(background->style, &bgcol); + if (css_rect.bottom_auto == false) + r.y1 = y - border_top + FIXTOINT(nscss_len2px( + css_rect.bottom, css_rect.bunit, + box->style)); - if (nscss_color_is_transparent(bgcol) == false) { - *background_colour = nscss_color_to_ns(bgcol); - pstyle_fill_bg.fill_colour = *background_colour; - if (plot_colour) - if (!plot->rectangle(r.x0, r.y0, r.x1, r.y1, - &pstyle_fill_bg)) - return false; - } - /* and plot the image */ - if (plot_content) { - width = content_get_width(background->background); - height = content_get_height(background->background); + /* find intersection of clip rectangle and box */ + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (clip->x1 < r.x1) r.x1 = clip->x1; + if (clip->y1 < r.y1) r.y1 = clip->y1; + /* no point trying to draw 0-width/height boxes */ + if (r.x0 == r.x1 || r.y0 == r.y1) + /* not an error */ + return ((!plot->group_end) || (plot->group_end())); + /* clip to it */ + if (!plot->clip(&r)) + return false; - /* ensure clip area only as large as required */ - if (!repeat_x) { - if (r.x0 < x) - r.x0 = x; - if (r.x1 > x + width * scale) - r.x1 = x + width * scale; - } - if (!repeat_y) { - if (r.y0 < y) - r.y0 = y; - if (r.y1 > y + height * scale) - r.y1 = y + height * scale; - } - /* valid clipping rectangles only */ - if ((r.x0 < r.x1) && (r.y0 < r.y1)) { - struct content_redraw_data bg_data; + } else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->object) { + /* find intersection of clip rectangle and box */ + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (clip->x1 < r.x1) r.x1 = clip->x1; + if (clip->y1 < r.y1) r.y1 = clip->y1; + /* no point trying to draw 0-width/height boxes */ + if (r.x0 == r.x1 || r.y0 == r.y1) + /* not an error */ + return ((!plot->group_end) || (plot->group_end())); + /* clip to it */ + if (!plot->clip(&r)) + return false; + } else { + /* clip box is fine, clip to it */ + r = *clip; + if (!plot->clip(&r)) + return false; + } - if (!plot->clip(&r)) - return false; + /* background colour and image for block level content and replaced + * inlines */ - bg_data.x = x; - bg_data.y = y; - bg_data.width = ceilf(width * scale); - bg_data.height = ceilf(height * scale); - bg_data.background_colour = *background_colour; - bg_data.scale = scale; - bg_data.repeat_x = repeat_x; - bg_data.repeat_y = repeat_y; + bg_box = html_redraw_find_bg_box(box); - if (!content_redraw(background->background, - &bg_data, &r, ctx)) - return false; + /* bg_box == NULL implies that this box should not have + * its background rendered. Otherwise filter out linebreaks, + * optimize away non-differing inlines, only plot background + * for BOX_TEXT it's in an inline */ + if (bg_box && bg_box->type != BOX_BR && + bg_box->type != BOX_TEXT && + bg_box->type != BOX_INLINE_END && + (bg_box->type != BOX_INLINE || bg_box->object || + bg_box->flags & IFRAME || box->flags & REPLACE_DIM)) { + /* find intersection of clip box and border edge */ + struct rect p; + p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left; + p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top; + p.x1 = x + padding_width + border_right < r.x1 ? + x + padding_width + border_right : r.x1; + p.y1 = y + padding_height + border_bottom < r.y1 ? + y + padding_height + border_bottom : r.y1; + if (!box->parent) { + /* Root element, special case: + * background covers margins too */ + int m_left, m_top, m_right, m_bottom; + if (scale == 1.0) { + m_left = box->margin[LEFT]; + m_top = box->margin[TOP]; + m_right = box->margin[RIGHT]; + m_bottom = box->margin[BOTTOM]; + } else { + m_left = box->margin[LEFT] * scale; + m_top = box->margin[TOP] * scale; + m_right = box->margin[RIGHT] * scale; + m_bottom = box->margin[BOTTOM] * scale; } + p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left; + p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top; + p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1; + p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1; + } + /* valid clipping rectangles only */ + if ((p.x0 < p.x1) && (p.y0 < p.y1)) { + /* plot background */ + if (!html_redraw_background(x, y, box, scale, &p, + ¤t_background_color, bg_box, ctx)) + return false; + /* restore previous graphics window */ + if (!plot->clip(&r)) + return false; } - - /* only rows being clipped to child boxes loop */ - if (!clip_to_children) - return true; } - return true; -} + /* borders for block level content and replaced inlines */ + if (box->style && box->type != BOX_TEXT && + box->type != BOX_INLINE_END && + (box->type != BOX_INLINE || box->object || + box->flags & IFRAME || box->flags & REPLACE_DIM) && + (border_top || border_right || + border_bottom || border_left)) { + if (!html_redraw_borders(box, x_parent, y_parent, + padding_width, padding_height, &r, + scale, ctx)) + return false; + } -/** - * Plot an inline's background and/or background image. - * - * \param x coordinate of box - * \param y coordinate of box - * \param box BOX_INLINE which created the background - * \param scale scale for redraw - * \param clip coordinates of clip rectangle - * \param b coordinates of border edge rectangle - * \param first true if this is the first rectangle associated with the inline - * \param last true if this is the last rectangle associated with the inline - * \param background_colour updated to current background colour if plotted - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -bool html_redraw_inline_background(int x, int y, struct box *box, float scale, - const struct rect *clip, struct rect b, bool first, bool last, - colour *background_colour, const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - struct rect r = *clip; - bool repeat_x = false; - bool repeat_y = false; - bool plot_colour = true; - bool plot_content; - css_fixed hpos = 0, vpos = 0; - css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; - css_color bgcol; - plot_style_t pstyle_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = *background_colour, - }; + /* backgrounds and borders for non-replaced inlines */ + if (box->style && box->type == BOX_INLINE && box->inline_end && + (html_redraw_box_has_background(box) || + border_top || border_right || + border_bottom || border_left)) { + /* inline backgrounds and borders span other boxes and may + * wrap onto separate lines */ + struct box *ib; + struct rect b; /* border edge rectangle */ + struct rect p; /* clipped rect */ + bool first = true; + int ib_x; + int ib_y = y; + int ib_p_width; + int ib_b_left, ib_b_right; - plot_content = (box->background != NULL); + b.x0 = x - border_left; + b.x1 = x + padding_width + border_right; + b.y0 = y - border_top; + b.y1 = y + padding_height + border_bottom; - if (html_redraw_printing && nsoption_bool(remove_backgrounds)) - return true; + p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; + p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; + p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; + p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; + for (ib = box; ib; ib = ib->next) { + /* to get extents of rectangle(s) associated with + * inline, cycle though all boxes in inline, skipping + * over floats */ + if (ib->type == BOX_FLOAT_LEFT || + ib->type == BOX_FLOAT_RIGHT) + continue; + if (scale == 1.0) { + ib_x = x_parent + ib->x; + ib_y = y_parent + ib->y; + ib_p_width = ib->padding[LEFT] + ib->width + + ib->padding[RIGHT]; + ib_b_left = ib->border[LEFT].width; + ib_b_right = ib->border[RIGHT].width; + } else { + ib_x = (x_parent + ib->x) * scale; + ib_y = (y_parent + ib->y) * scale; + ib_p_width = (ib->padding[LEFT] + ib->width + + ib->padding[RIGHT]) * scale; + ib_b_left = ib->border[LEFT].width * scale; + ib_b_right = ib->border[RIGHT].width * scale; + } - if (plot_content) { - /* handle background-repeat */ - switch (css_computed_background_repeat(box->style)) { - case CSS_BACKGROUND_REPEAT_REPEAT: - repeat_x = repeat_y = true; - /* optimisation: only plot the colour if - * bitmap is not opaque - */ - plot_colour = !content_get_opaque(box->background); - break; + if ((ib->flags & NEW_LINE) && ib != box) { + /* inline element has wrapped, plot background + * and borders */ + if (!html_redraw_inline_background( + x, y, box, scale, &p, b, + first, false, + ¤t_background_color, ctx)) + return false; + /* restore previous graphics window */ + if (!plot->clip(&r)) + return false; + if (!html_redraw_inline_borders(box, b, &r, + scale, first, false, ctx)) + return false; + /* reset coords */ + b.x0 = ib_x - ib_b_left; + b.y0 = ib_y - border_top - padding_top; + b.y1 = ib_y + padding_height - padding_top + + border_bottom; - case CSS_BACKGROUND_REPEAT_REPEAT_X: - repeat_x = true; - break; + p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; + p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; + p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; - case CSS_BACKGROUND_REPEAT_REPEAT_Y: - repeat_y = true; - break; + first = false; + } - case CSS_BACKGROUND_REPEAT_NO_REPEAT: - break; + /* increase width for current box */ + b.x1 = ib_x + ib_p_width + ib_b_right; + p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; - default: - break; + if (ib == box->inline_end) + /* reached end of BOX_INLINE span */ + break; } + /* plot background and borders for last rectangle of + * the inline */ + if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b, + first, true, ¤t_background_color, ctx)) + return false; + /* restore previous graphics window */ + if (!plot->clip(&r)) + return false; + if (!html_redraw_inline_borders(box, b, &r, scale, first, true, + ctx)) + return false; - /* handle background-position */ - css_computed_background_position(box->style, - &hpos, &hunit, &vpos, &vunit); - if (hunit == CSS_UNIT_PCT) { - x += (b.x1 - b.x0 - - content_get_width(box->background) * - scale) * FIXTOFLT(hpos) / 100.; + } - if (!repeat_x && ((hpos < 2 && !first) || - (hpos > 98 && !last))){ - plot_content = false; - } + /* Debug outlines */ + if (html_redraw_debug) { + int margin_left, margin_right; + int margin_top, margin_bottom; + if (scale == 1.0) { + /* avoid trivial fp maths */ + margin_left = box->margin[LEFT]; + margin_top = box->margin[TOP]; + margin_right = box->margin[RIGHT]; + margin_bottom = box->margin[BOTTOM]; } else { - x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, - box->style)) * scale); + margin_left = box->margin[LEFT] * scale; + margin_top = box->margin[TOP] * scale; + margin_right = box->margin[RIGHT] * scale; + margin_bottom = box->margin[BOTTOM] * scale; } + /* Content edge -- blue */ + if (!plot->rectangle(x + padding_left, + y + padding_top, + x + padding_left + width, + y + padding_top + height, + plot_style_content_edge)) + return false; + /* Padding edge -- red */ + if (!plot->rectangle(x, y, + x + padding_width, y + padding_height, + plot_style_padding_edge)) + return false; + /* Margin edge -- yellow */ + if (!plot->rectangle( + x - border_left - margin_left, + y - border_top - margin_top, + x + padding_width + border_right + + margin_right, + y + padding_height + border_bottom + + margin_bottom, + plot_style_margin_edge)) + return false; + } - if (vunit == CSS_UNIT_PCT) { - y += (b.y1 - b.y0 - - content_get_height(box->background) * - scale) * FIXTOFLT(vpos) / 100.; - } else { - y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, - box->style)) * scale); + /* clip to the padding edge for objects, or boxes with overflow hidden + * or scroll */ + if ((box->style && css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) || box->object || + box->flags & IFRAME) { + r.x0 = x; + r.y0 = y; + r.x1 = x + padding_width; + r.y1 = y + padding_height; + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (clip->x1 < r.x1) r.x1 = clip->x1; + if (clip->y1 < r.y1) r.y1 = clip->y1; + if (r.x1 <= r.x0 || r.y1 <= r.y0) + return ((!plot->group_end) || (plot->group_end())); + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->object) { + if (!plot->clip(&r)) + return false; } } - /* plot the background colour */ - css_computed_background_color(box->style, &bgcol); + /* text decoration */ + if (box->type != BOX_TEXT && box->style && + css_computed_text_decoration(box->style) != + CSS_TEXT_DECORATION_NONE) + if (!html_redraw_text_decoration(box, x_parent, y_parent, + scale, current_background_color, ctx)) + return false; - if (nscss_color_is_transparent(bgcol) == false) { - *background_colour = nscss_color_to_ns(bgcol); - pstyle_fill_bg.fill_colour = *background_colour; + if (box->object && width != 0 && height != 0) { + struct content_redraw_data obj_data; - if (plot_colour) - if (!plot->rectangle(r.x0, r.y0, r.x1, r.y1, - &pstyle_fill_bg)) - return false; - } - /* and plot the image */ - if (plot_content) { - int width = content_get_width(box->background); - int height = content_get_height(box->background); + x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale; + y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale; - if (!repeat_x) { - if (r.x0 < x) - r.x0 = x; - if (r.x1 > x + width * scale) - r.x1 = x + width * scale; - } - if (!repeat_y) { - if (r.y0 < y) - r.y0 = y; - if (r.y1 > y + height * scale) - r.y1 = y + height * scale; + obj_data.x = x_scrolled + padding_left; + obj_data.y = y_scrolled + padding_top; + obj_data.width = width; + obj_data.height = height; + obj_data.background_colour = current_background_color; + obj_data.scale = scale; + obj_data.repeat_x = false; + obj_data.repeat_y = false; + + if (content_get_type(box->object) == CONTENT_HTML) { + obj_data.x /= scale; + obj_data.y /= scale; } - /* valid clipping rectangles only */ - if ((r.x0 < r.x1) && (r.y0 < r.y1)) { - struct content_redraw_data bg_data; - if (!plot->clip(&r)) + if (!content_redraw(box->object, &obj_data, &r, ctx)) { + /* Show image fail */ + /* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */ + const char *obj = "\xef\xbf\xbc"; + int obj_width; + int obj_x = x + padding_left; + if (!plot->rectangle(x + padding_left, + y + padding_top, + x + padding_left + width - 1, + y + padding_top + height - 1, + plot_style_broken_object)) return false; + if (!nsfont.font_width(plot_fstyle_broken_object, obj, + sizeof(obj) - 1, &obj_width)) + obj_x += 1; + else + obj_x += width / 2 - obj_width / 2; - bg_data.x = x; - bg_data.y = y; - bg_data.width = ceilf(width * scale); - bg_data.height = ceilf(height * scale); - bg_data.background_colour = *background_colour; - bg_data.scale = scale; - bg_data.repeat_x = repeat_x; - bg_data.repeat_y = repeat_y; - - if (!content_redraw(box->background, &bg_data, &r, ctx)) + if (!plot->text(obj_x, y + padding_top + (int) + (height * 0.75), + obj, sizeof(obj) - 1, + plot_fstyle_broken_object)) return false; } - } - - return true; -} + + } else if (box->iframe) { + /* Offset is passed to browser window redraw unscaled */ + browser_window_redraw(box->iframe, + (x + padding_left) / scale, + (y + padding_top) / scale, &r, ctx); -/** - * Plot text decoration for a box. - * - * \param box box to plot decorations for - * \param x_parent x coordinate of parent of box - * \param y_parent y coordinate of parent of box - * \param scale scale for redraw - * \param background_colour current background colour - * \param ctx current redraw context - * \return true if successful, false otherwise - */ + } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) { + if (!html_redraw_checkbox(x + padding_left, y + padding_top, + width, height, box->gadget->selected, ctx)) + return false; -bool html_redraw_text_decoration(struct box *box, - int x_parent, int y_parent, float scale, - colour background_colour, const struct redraw_context *ctx) -{ - static const enum css_text_decoration_e decoration[] = { - CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE, - CSS_TEXT_DECORATION_LINE_THROUGH }; - static const float line_ratio[] = { 0.9, 0.1, 0.5 }; - colour fgcol; - unsigned int i; - css_color col; + } else if (box->gadget && box->gadget->type == GADGET_RADIO) { + if (!html_redraw_radio(x + padding_left, y + padding_top, + width, height, box->gadget->selected, ctx)) + return false; - css_computed_color(box->style, &col); - fgcol = nscss_color_to_ns(col); + } else if (box->gadget && box->gadget->type == GADGET_FILE) { + if (!html_redraw_file(x + padding_left, y + padding_top, + width, height, box, scale, + current_background_color, ctx)) + return false; - /* antialias colour for under/overline */ - if (html_redraw_printing == false) - fgcol = blend_colour(background_colour, fgcol); + } else if (box->text) { + if (!html_redraw_text_box(html, box, x, y, &r, scale, + current_background_color, ctx)) + return false; - if (box->type == BOX_INLINE) { - if (!box->inline_end) - return true; - for (i = 0; i != NOF_ELEMENTS(decoration); i++) - if (css_computed_text_decoration(box->style) & - decoration[i]) - if (!html_redraw_text_decoration_inline(box, - x_parent, y_parent, scale, - fgcol, line_ratio[i], ctx)) - return false; } else { - for (i = 0; i != NOF_ELEMENTS(decoration); i++) - if (css_computed_text_decoration(box->style) & - decoration[i]) - if (!html_redraw_text_decoration_block(box, - x_parent + box->x, - y_parent + box->y, - scale, - fgcol, line_ratio[i], ctx)) - return false; + if (!html_redraw_box_children(html, box, x_parent, y_parent, &r, + scale, current_background_color, ctx)) + return false; } - return true; -} + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) + if (!plot->clip(clip)) + return false; + /* list marker */ + if (box->list_marker) + if (!html_redraw_box(html, box->list_marker, + x_parent + box->x - + scrollbar_get_offset(box->scroll_x), + y_parent + box->y - + scrollbar_get_offset(box->scroll_y), + clip, scale, current_background_color, ctx)) + return false; -/** - * Plot text decoration for an inline box. - * - * \param box box to plot decorations for, of type BOX_INLINE - * \param x x coordinate of parent of box - * \param y y coordinate of parent of box - * \param scale scale for redraw - * \param colour colour for decorations - * \param ratio position of line as a ratio of line height - * \param ctx current redraw context - * \return true if successful, false otherwise - */ + /* scrollbars */ + if (((box->style && box->type != BOX_BR && + box->type != BOX_TABLE && box->type != BOX_INLINE && + (css_computed_overflow(box->style) == + CSS_OVERFLOW_SCROLL || + css_computed_overflow(box->style) == + CSS_OVERFLOW_AUTO)) || (box->object && + content_get_type(box->object) == CONTENT_HTML)) && + box->parent != NULL) { -bool html_redraw_text_decoration_inline(struct box *box, int x, int y, - float scale, colour colour, float ratio, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - struct box *c; - plot_style_t plot_style_box = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_colour = colour, - }; + has_x_scroll = box_hscrollbar_present(box); + has_y_scroll = box_vscrollbar_present(box); - for (c = box->next; - c && c != box->inline_end; - c = c->next) { - if (c->type != BOX_TEXT) - continue; - if (!plot->line((x + c->x) * scale, - (y + c->y + c->height * ratio) * scale, - (x + c->x + c->width) * scale, - (y + c->y + c->height * ratio) * scale, - &plot_style_box)) + if (!box_handle_scrollbars((struct content *)html, + box, has_x_scroll, has_y_scroll)) return false; + + if (box->scroll_x != NULL) + scrollbar_redraw(box->scroll_x, + x_parent + box->x, + y_parent + box->y + box->padding[TOP] + + box->height + box->padding[BOTTOM] - + SCROLLBAR_WIDTH, clip, scale, ctx); + if (box->scroll_y != NULL) + scrollbar_redraw(box->scroll_y, + x_parent + box->x + box->padding[LEFT] + + box->width + box->padding[RIGHT] - + SCROLLBAR_WIDTH, + y_parent + box->y, clip, scale, ctx); } - return true; -} + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) + if (!plot->clip(clip)) + return false; + + return ((!plot->group_end) || (plot->group_end())); +} /** - * Plot text decoration for an non-inline box. + * Draw a CONTENT_HTML using the current set of plotters (plot). * - * \param box box to plot decorations for, of type other than BOX_INLINE - * \param x x coordinate of box - * \param y y coordinate of box - * \param scale scale for redraw - * \param colour colour for decorations - * \param ratio position of line as a ratio of line height - * \param ctx current redraw context + * \param c content of type CONTENT_HTML + * \param data redraw data for this content redraw + * \param clip current clip region + * \param ctx current redraw context * \return true if successful, false otherwise + * + * x, y, clip_[xy][01] are in target coordinates. */ -bool html_redraw_text_decoration_block(struct box *box, int x, int y, - float scale, colour colour, float ratio, - const struct redraw_context *ctx) +bool html_redraw(struct content *c, struct content_redraw_data *data, + const struct rect *clip, const struct redraw_context *ctx) { - const struct plotter_table *plot = ctx->plot; - struct box *c; - plot_style_t plot_style_box = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_colour = colour, + html_content *html = (html_content *) c; + struct box *box; + bool result = true; + bool select, select_only; + plot_style_t pstyle_fill_bg = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = data->background_colour, }; - /* draw through text descendants */ - for (c = box->children; c; c = c->next) { - if (c->type == BOX_TEXT) { - if (!plot->line((x + c->x) * scale, - (y + c->y + c->height * ratio) * scale, - (x + c->x + c->width) * scale, - (y + c->y + c->height * ratio) * scale, - &plot_style_box)) - return false; - } else if (c->type == BOX_INLINE_CONTAINER || - c->type == BOX_BLOCK) { - if (!html_redraw_text_decoration_block(c, - x + c->x, y + c->y, - scale, colour, ratio, ctx)) - return false; - } + box = html->layout; + assert(box); + + /* The select menu needs special treating because, when opened, it + * reaches beyond its layout box. + */ + select = false; + select_only = false; + if (ctx->interactive && html->visible_select_menu != NULL) { + struct form_control *control = html->visible_select_menu; + select = true; + /* check if the redraw rectangle is completely inside of the + select menu */ + select_only = form_clip_inside_select_menu(control, + data->scale, clip); } - return true; + + if (!select_only) { + /* clear to background colour */ + result = ctx->plot->clip(clip); + + if (html->background_colour != NS_TRANSPARENT) + pstyle_fill_bg.fill_colour = html->background_colour; + + result &= ctx->plot->rectangle(clip->x0, clip->y0, + clip->x1, clip->y1, + &pstyle_fill_bg); + + result &= html_redraw_box(html, box, data->x, data->y, clip, + data->scale, pstyle_fill_bg.fill_colour, ctx); + } + + if (select) { + int menu_x, menu_y; + box = html->visible_select_menu->box; + box_coords(box, &menu_x, &menu_y); + + menu_x -= box->border[LEFT].width; + menu_y += box->height + box->border[BOTTOM].width + + box->padding[BOTTOM] + box->padding[TOP]; + result &= form_redraw_select_menu(html->visible_select_menu, + data->x + menu_x, data->y + menu_y, + data->scale, clip, ctx); + } + + return result; + } -- cgit v1.2.3