From 7bd710a71c3233bc44ec66350a9c8d2b7a29c41c Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Sat, 4 Nov 2006 19:53:22 +0000 Subject: Initial work for display: list-item support svn path=/trunk/netsurf/; revision=3028 --- render/box.c | 4 + render/box.h | 13 +++- render/box_construct.c | 59 +++++++++++++- render/box_normalise.c | 153 ++++++++++++++++++++++++++++++++++-- render/layout.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 416 insertions(+), 18 deletions(-) diff --git a/render/box.c b/render/box.c index 506d94215..4113120de 100644 --- a/render/box.c +++ b/render/box.c @@ -495,6 +495,10 @@ void box_dump(struct box *box, unsigned int depth) case BOX_FLOAT_RIGHT: fprintf(stderr, "FLOAT_RIGHT "); break; case BOX_BR: fprintf(stderr, "BR "); break; case BOX_TEXT: fprintf(stderr, "TEXT "); break; + case BOX_LIST: fprintf(stderr, "LIST "); break; + case BOX_LIST_ITEM: fprintf(stderr, "LIST_ITEM "); break; + case BOX_LIST_MARKER: fprintf(stderr, "LIST_MARKER "); break; + case BOX_LIST_PRINCIPAL: fprintf(stderr, "LIST_PRINCIPAL "); break; default: fprintf(stderr, "Unknown box type "); } diff --git a/render/box.h b/render/box.h index a0c43ef1e..369956f76 100644 --- a/render/box.h +++ b/render/box.h @@ -60,15 +60,19 @@ * A box tree is "normalized" if the following is satisfied: * \code * parent permitted child nodes - * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE + * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE, LIST * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT, * INLINE_END * INLINE none * TABLE at least 1 TABLE_ROW_GROUP * TABLE_ROW_GROUP at least 1 TABLE_ROW * TABLE_ROW at least 1 TABLE_CELL - * TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE (same as BLOCK) - * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK or TABLE \endcode + * TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE, LIST (same as BLOCK) + * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK or TABLE + * LIST at least 1 LIST_ITEM + * LIST_ITEM exactly 1 LIST_MARKER and exactly 1 LIST_PRINCIPAL + * LIST_PRINCIPAL BLOCK, INLINE_CONTAINER, TABLE, LIST (same as BLOCK) + * \endcode */ #ifndef _NETSURF_RENDER_BOX_H_ @@ -93,7 +97,8 @@ typedef enum { BOX_TABLE_ROW_GROUP, BOX_FLOAT_LEFT, BOX_FLOAT_RIGHT, BOX_INLINE_BLOCK, BOX_BR, BOX_TEXT, - BOX_INLINE_END + BOX_INLINE_END, + BOX_LIST, BOX_LIST_ITEM, BOX_LIST_MARKER, BOX_LIST_PRINCIPAL } box_type; struct rect { diff --git a/render/box_construct.c b/render/box_construct.c index 69b681760..4c847289a 100644 --- a/render/box_construct.c +++ b/render/box_construct.c @@ -205,7 +205,7 @@ static const box_type box_map[] = { 0, /*CSS_DISPLAY_INHERIT,*/ BOX_INLINE, /*CSS_DISPLAY_INLINE,*/ BOX_BLOCK, /*CSS_DISPLAY_BLOCK,*/ - BOX_BLOCK, /*CSS_DISPLAY_LIST_ITEM,*/ + BOX_LIST_ITEM, /*CSS_DISPLAY_LIST_ITEM,*/ BOX_INLINE, /*CSS_DISPLAY_RUN_IN,*/ BOX_INLINE_BLOCK, /*CSS_DISPLAY_INLINE_BLOCK,*/ BOX_TABLE, /*CSS_DISPLAY_TABLE,*/ @@ -350,6 +350,10 @@ bool box_construct_element(xmlNode *n, struct content *content, return true; } + /* if this is a list item, then reset the box type */ + if (style->display == CSS_DISPLAY_LIST_ITEM) + box->type = BOX_LIST_ITEM; + if (!*inline_container && (box->type == BOX_INLINE || box->type == BOX_BR || @@ -394,6 +398,59 @@ bool box_construct_element(xmlNode *n, struct content *content, &inline_container_c, href, target, title)) return false; + } else if (box->type == BOX_LIST_ITEM) { + /* list item: create marker box and recurse */ + struct box *list_item; + struct box *marker; + + /* create container box */ + list_item = box_create(0, href, target, title, 0, content); + if (!list_item) + return false; + list_item->type = BOX_LIST_ITEM; + + /* create marker - effectively a single INLINE */ + /* marker style information is contained in PRINCIPAL box */ + marker = box_create(box->style, href, target, title, 0, + content); + if (!marker) + return false; + marker->type = BOX_LIST_MARKER; + marker->clone = 1; + + /** \todo marker content (list-style-type) + * need to traverse up the tree to find containing BOX_LIST, + * which contains the counter information */ + marker->text = talloc_strdup(content, "1."); + if (!marker->text) + return false; + marker->space = 1; + marker->length = 2; + + if (style->list_style_image.type == + CSS_LIST_STYLE_IMAGE_URI) { + if (!html_fetch_object(content, + style->list_style_image.uri, marker, + 0, content->available_width, 1000, + false)) + return false; + } + + /* make box into principal block for list */ + box->type = BOX_LIST_PRINCIPAL; + + box_add_child(parent, list_item); + box_add_child(list_item, marker); + box_add_child(list_item, box); + + /* and recurse */ + inline_container_c = 0; + for (c = n->children; convert_children && c; c = c->next) { + if (!convert_xml_to_box(c, content, style, + box, &inline_container_c, + href, target, title)) + return false; + } } else { if (style->float_ == CSS_FLOAT_LEFT || style->float_ == CSS_FLOAT_RIGHT) { diff --git a/render/box_normalise.c b/render/box_normalise.c index e241f447f..703622bb9 100644 --- a/render/box_normalise.c +++ b/render/box_normalise.c @@ -57,26 +57,32 @@ static bool calculate_table_row(struct columns *col_info, unsigned int col_span, unsigned int row_span, unsigned int *start_column); static bool box_normalise_inline_container(struct box *cont, struct content *c); +static bool box_normalise_list(struct box *cont, struct content *c); +static bool box_normalise_list_item(struct box *cont, struct content *c); /** * Ensure the box tree is correctly nested by adding and removing nodes. * - * \param block box of type BLOCK, INLINE_BLOCK, or TABLE_CELL + * \param block box of type BLOCK, INLINE_BLOCK, TABLE_CELL or LIST_PRINCIPAL * \param box_pool pool to allocate new boxes in * \return true on success, false on memory exhaustion * * The tree is modified to satisfy the following: * \code * parent permitted child nodes - * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE + * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE, LIST * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT * INLINE, TEXT none * TABLE at least 1 TABLE_ROW_GROUP * TABLE_ROW_GROUP at least 1 TABLE_ROW * TABLE_ROW at least 1 TABLE_CELL * TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE (same as BLOCK) - * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK or TABLE \endcode + * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK or TABLE + * LIST at least 1 LIST_ITEM + * LIST_ITEM exactly 1 LIST_MARKER and exactly 1 LIST_PRINCIPAL + * LIST_PRINCIPAL BLOCK, INLINE_CONTAINER, TABLE, LIST (same as BLOCK) + * \endcode */ bool box_normalise_block(struct box *block, struct content *c) @@ -84,12 +90,14 @@ bool box_normalise_block(struct box *block, struct content *c) struct box *child; struct box *next_child; struct box *table; + struct box *list; struct css_style *style; assert(block != 0); LOG(("block %p, block->type %u", block, block->type)); assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || - block->type == BOX_TABLE_CELL); + block->type == BOX_TABLE_CELL || + block->type == BOX_LIST_PRINCIPAL); gui_multitask(); for (child = block->children; child != 0; child = next_child) { @@ -158,6 +166,48 @@ bool box_normalise_block(struct box *block, struct content *c) if (!box_normalise_table(table, c)) return false; break; + case BOX_LIST: + if (!box_normalise_list(child, c)) + return false; + break; + case BOX_LIST_ITEM: + /* Insert implied BOX_LIST */ + style = css_duplicate_style(block->style); + if (!style) + return false; + css_cascade(style, &css_blank_style); + list = box_create(style, 0, 0, 0, 0, c); + if (!list) { + css_free_style(style); + return false; + } + list->type = BOX_LIST; + if (child->prev == 0) + block->children = list; + else + child->prev->next = list; + list->prev = child->prev; + while (child != 0 && child->type == BOX_LIST_ITEM) { + box_add_child(list, child); + next_child = child->next; + child->next = 0; + child = next_child; + } + list->last->next = 0; + list->next = next_child = child; + if (list->next) + list->next->prev = list; + else + block->last = list; + list->parent = block; + if (!box_normalise_list(list, c)) + return false; + break; + case BOX_LIST_MARKER: + case BOX_LIST_PRINCIPAL: + /* Should be wrapped in BOX_LIST_ITEM */ + assert(0); + break; default: assert(0); } @@ -205,6 +255,8 @@ bool box_normalise_table(struct box *table, struct content * c) case BOX_TABLE: case BOX_TABLE_ROW: case BOX_TABLE_CELL: + case BOX_LIST: + case BOX_LIST_ITEM: /* insert implied table row group */ assert(table->style != NULL); style = css_duplicate_style(table->style); @@ -233,12 +285,15 @@ bool box_normalise_table(struct box *table, struct content * c) child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || child->type == BOX_TABLE_ROW || - child->type == BOX_TABLE_CELL)) { + child->type == BOX_TABLE_CELL || + child->type == BOX_LIST || + child->type == BOX_LIST_ITEM)) { box_add_child(row_group, child); next_child = child->next; child->next = 0; child = next_child; } + assert(row_group->last != NULL); row_group->last->next = 0; row_group->next = next_child = child; if (row_group->next) @@ -261,6 +316,11 @@ bool box_normalise_table(struct box *table, struct content * c) container by convert_xml_to_box() */ assert(0); break; + case BOX_LIST_MARKER: + case BOX_LIST_PRINCIPAL: + /* Should have been wrapped in BOX_LIST_ITEM */ + assert(0); + break; default: fprintf(stderr, "%i\n", child->type); assert(0); @@ -381,6 +441,8 @@ bool box_normalise_table_row_group(struct box *row_group, case BOX_TABLE: case BOX_TABLE_ROW_GROUP: case BOX_TABLE_CELL: + case BOX_LIST: + case BOX_LIST_ITEM: /* insert implied table row */ assert(row_group->style != NULL); style = css_duplicate_style(row_group->style); @@ -404,12 +466,15 @@ bool box_normalise_table_row_group(struct box *row_group, child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || child->type == BOX_TABLE_ROW_GROUP || - child->type == BOX_TABLE_CELL)) { + child->type == BOX_TABLE_CELL || + child->type == BOX_LIST || + child->type == BOX_LIST_ITEM)) { box_add_child(row, child); next_child = child->next; child->next = 0; child = next_child; } + assert(row->last != NULL); row->last->next = 0; row->next = next_child = child; if (row->next) @@ -430,6 +495,11 @@ bool box_normalise_table_row_group(struct box *row_group, container by convert_xml_to_box() */ assert(0); break; + case BOX_LIST_MARKER: + case BOX_LIST_PRINCIPAL: + /* should have been wrapped in LIST_ITEM */ + assert(0); + break; default: assert(0); } @@ -480,6 +550,8 @@ bool box_normalise_table_row(struct box *row, case BOX_TABLE: case BOX_TABLE_ROW_GROUP: case BOX_TABLE_ROW: + case BOX_LIST: + case BOX_LIST_ITEM: /* insert implied table cell */ assert(row->style != NULL); style = css_duplicate_style(row->style); @@ -503,12 +575,15 @@ bool box_normalise_table_row(struct box *row, child->type == BOX_INLINE_CONTAINER || child->type == BOX_TABLE || child->type == BOX_TABLE_ROW_GROUP || - child->type == BOX_TABLE_ROW)) { + child->type == BOX_TABLE_ROW || + child->type == BOX_LIST || + child->type == BOX_LIST_ITEM)) { box_add_child(cell, child); next_child = child->next; child->next = 0; child = next_child; } + assert(cell->last != NULL); cell->last->next = 0; cell->next = next_child = child; if (cell->next) @@ -528,6 +603,11 @@ bool box_normalise_table_row(struct box *row, container by convert_xml_to_box() */ assert(0); break; + case BOX_LIST_MARKER: + case BOX_LIST_PRINCIPAL: + /* should have been wrapped in a BOX_LIST_ITEM */ + assert(0); + break; default: assert(0); } @@ -703,6 +783,10 @@ bool box_normalise_inline_container(struct box *cont, struct content * c) case BOX_TABLE_ROW_GROUP: case BOX_TABLE_ROW: case BOX_TABLE_CELL: + case BOX_LIST: + case BOX_LIST_ITEM: + case BOX_LIST_MARKER: + case BOX_LIST_PRINCIPAL: default: assert(0); } @@ -712,3 +796,58 @@ bool box_normalise_inline_container(struct box *cont, struct content * c) return true; } + +bool box_normalise_list(struct box *cont, struct content *c) +{ + struct box *child; + struct box *next_child; + + assert(cont != 0); + assert(cont->type == BOX_LIST); + + for (child = cont->children; child; child = next_child) { + next_child = child->next; + switch (child->type) { + case BOX_LIST_ITEM: + /* ok */ + if (!box_normalise_list_item(child, c)) + return false; + break; + case BOX_LIST_MARKER: + case BOX_LIST_PRINCIPAL: + /** \todo imply LIST_ITEM here? */ + default: + assert(0); + } + } + + return true; +} + + +bool box_normalise_list_item(struct box *cont, struct content *c) +{ + struct box *child; + struct box *next_child; + + assert(cont != 0); + assert(cont->type == BOX_LIST_ITEM); + + for (child = cont->children; child; child = next_child) { + next_child = child->next; + switch (child->type) { + case BOX_LIST_MARKER: + /* ok */ + break; + case BOX_LIST_PRINCIPAL: + /* ok */ + if (!box_normalise_block(child, c)) + return false; + break; + default: + assert(0); + } + } + + return true; +} diff --git a/render/layout.c b/render/layout.c index 58693e254..2822c5239 100644 --- a/render/layout.c +++ b/render/layout.c @@ -48,6 +48,7 @@ static void layout_minmax_block(struct box *block); static bool layout_block_object(struct box *block); static void layout_block_find_dimensions(int available_width, struct box *box); +static void layout_list_find_dimensions(int available_width, struct box *box); static int layout_solve_width(int available_width, int width, int margin[4], int padding[4], int border[4]); static void layout_float_find_dimensions(int available_width, @@ -59,6 +60,9 @@ static void layout_find_dimensions(int available_width, static int layout_clear(struct box *fl, css_clear clear); static void find_sides(struct box *fl, int y0, int y1, int *x0, int *x1, struct box **left, struct box **right); +static void layout_minmax_list(struct box *box); +static void layout_minmax_list_item(struct box *box); +static bool layout_list(struct box *box, struct content *content); static void layout_minmax_inline_container(struct box *inline_container); static int line_height(struct css_style *style); static bool layout_line(struct box *first, int *width, int *y, @@ -149,7 +153,7 @@ bool layout_document(struct content *content, int width, int height) /** * Layout a block formatting context. * - * \param block BLOCK, INLINE_BLOCK, or TABLE_CELL to layout + * \param block BLOCK, INLINE_BLOCK, TABLE_CELL or LIST_PRINCIPAL to layout * \param content memory pool for any new boxes * \return true on success, false on memory exhaustion * @@ -168,7 +172,8 @@ bool layout_block_context(struct box *block, struct content *content) assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || - block->type == BOX_TABLE_CELL); + block->type == BOX_TABLE_CELL || + block->type == BOX_LIST_PRINCIPAL); gui_multitask(); @@ -210,7 +215,8 @@ bool layout_block_context(struct box *block, struct content *content) * to each in the order shown. */ while (box) { assert(box->type == BOX_BLOCK || box->type == BOX_TABLE || - box->type == BOX_INLINE_CONTAINER); + box->type == BOX_INLINE_CONTAINER || + box->type == BOX_LIST); assert(margin_box); /* Tables are laid out before being positioned, because the @@ -235,7 +241,8 @@ bool layout_block_context(struct box *block, struct content *content) return false; layout_solve_width(box->parent->width, box->width, box->margin, box->padding, box->border); - } + } else if (box->type == BOX_LIST) + layout_list_find_dimensions(box->parent->width, box); /* Position box: horizontal. */ box->x = box->parent->padding[LEFT] + box->margin[LEFT] + @@ -308,6 +315,9 @@ bool layout_block_context(struct box *block, struct content *content) cx = x0; box->y += y - cy; cy = y; + } else if (box->type == BOX_LIST) { + if (!layout_list(box, content)) + return false; } /* Advance to next box. */ @@ -386,7 +396,7 @@ bool layout_block_context(struct box *block, struct content *content) /** * Calculate minimum and maximum width of a block. * - * \param block box of type BLOCK, INLINE_BLOCK, or TABLE_CELL + * \param block box of type BLOCK, INLINE_BLOCK, TABLE_CELL, or LIST_PRINCIPAL * \post block->min_width and block->max_width filled in, * 0 <= block->min_width <= block->max_width */ @@ -400,7 +410,8 @@ void layout_minmax_block(struct box *block) assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || - block->type == BOX_TABLE_CELL); + block->type == BOX_TABLE_CELL || + block->type == BOX_LIST_PRINCIPAL); /* check if the widths have already been calculated */ if (block->max_width != UNKNOWN_MAX_WIDTH) @@ -427,6 +438,9 @@ void layout_minmax_block(struct box *block) case BOX_TABLE: layout_minmax_table(child); break; + case BOX_LIST: + layout_minmax_list(child); + break; default: assert(0); } @@ -562,6 +576,64 @@ void layout_block_find_dimensions(int available_width, struct box *box) } +/** + * Compute dimensions of box, margins, paddings, and borders for a list. + * + * Similar to layout_block_find_dimensions() + */ + +void layout_list_find_dimensions(int available_width, struct box *box) +{ + int width, height; + int *margin = box->margin; + int *padding = box->padding; + int *border = box->border; + struct css_style *style = box->style; + struct box *c; + + layout_find_dimensions(available_width, style, + &width, &height, margin, padding, border); + + for (c = box->children; c; c = c->next) { + struct box *marker, *principal; + + marker = c->children; + principal = marker->next; + + /** \todo handle marker */ + marker->width = marker->height = 0; + + layout_block_find_dimensions(available_width, principal); + + if (width < principal->x + principal->width + + principal->padding[RIGHT] + + principal->border[RIGHT] + + principal->margin[RIGHT]) + width = principal->x + principal->width + + principal->padding[RIGHT] + + principal->border[RIGHT] + + principal->margin[RIGHT]; + + if (height < principal->y + principal->height + + principal->padding[BOTTOM] + + principal->border[BOTTOM] + + principal->margin[BOTTOM]) + height = principal->y + principal->height + + principal->padding[BOTTOM] + + principal->border[BOTTOM] + + principal->margin[BOTTOM]; + } + + box->width = layout_solve_width(available_width, width, margin, + padding, border); + box->height = height; + + if (margin[TOP] == AUTO) + margin[TOP] = 0; + if (margin[BOTTOM] == AUTO) + margin[BOTTOM] = 0; +} + /** * Solve the width constraint as given in CSS 2.1 section 10.3.3. */ @@ -817,6 +889,127 @@ void find_sides(struct box *fl, int y0, int y1, } +/** + * Calculate minimum and maximum widths of a list + */ + +void layout_minmax_list(struct box *box) +{ + int min = 0, max = 0; + struct box *c; + + assert(box->type == BOX_LIST); + + /* check if we've already calculated the width */ + if (box->max_width != UNKNOWN_MAX_WIDTH) + return; + + for (c = box->children; c; c = c->next) { + layout_minmax_list_item(c); + if (min < c->min_width) + min = c->min_width; + if (max < c->max_width) + max = c->max_width; + } + + box->min_width = min; + box->max_width = max; +} + + +/** + * Calculate minimum and maximum widths of a list item + */ + +void layout_minmax_list_item(struct box *box) +{ + struct box *marker, *principal; + + assert(box->type == BOX_LIST_ITEM); + + /* check if we've already calculated the width */ + if (box->max_width != UNKNOWN_MAX_WIDTH) + return; + + marker = box->children; + principal = marker->next; + + layout_minmax_block(principal); + + /** \todo what on earth are we expected to do with the marker? + * AFAICS, none of Opera, Firefox nor Konqueror support + * display: list-item with list-style-position: outside + * In Opera/Firefox's case, they don't appear to bother to + * generate the marker box (probably following the "optional" + * wording of the spec - 12.5). Konqueror turns it into + * list-style-position: inside. */ + marker->min_width = marker->max_width = 0; + + box->min_width = principal->min_width + marker->min_width; + box->max_width = principal->max_width + marker->max_width; +} + + +/** + * Layout a list + * + * \param box list box + * \param content memory pool for any new boxes + * \return true on success, false on memory exhaustion + */ + +bool layout_list(struct box *box, struct content *content) +{ + struct box *c; + int cy = 0; + + assert(box->type == BOX_LIST); + + for (c = box->children; c; c = c->next) { + struct box *marker, *principal; + + assert(c->type == BOX_LIST_ITEM); + + marker = c->children; + principal = marker->next; + + c->x = 0; + c->y = cy; + + /** \todo handle marker */ + marker->x = marker->y = 0; + + principal->x = 0; + principal->y = 0; + /* manipulate principal box's parent pointer so + * layout_block_context has a parent style to play with */ + /** \todo is this sane? */ + principal->parent = box; + layout_block_context(principal, content); + principal->parent = c; + + if (c->width == UNKNOWN_WIDTH) + c->width = principal->x + principal->width + + principal->padding[RIGHT] + + principal->border[RIGHT] + + principal->margin[RIGHT]; + +// if (c->height == AUTO) + c->height = principal->y + principal->height + + principal->padding[BOTTOM] + + principal->border[BOTTOM] + + principal->margin[BOTTOM]; + + cy += c->height; + } + +// if (box->height == AUTO) + box->height = cy - box->padding[TOP]; + + return true; +} + + /** * Layout lines of text or inline boxes with floats. * -- cgit v1.2.3