/* layout.c: Layout routines for libRUIN * Copyright (C) 2007 Julian Graham * * libRUIN is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * libRUIN is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with libRUIN; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "css.h" #include "layout.h" #include "render.h" #include "scheme.h" #include "util.h" #include "xhtml.h" #include "xul.h" static const int ruin_layout_block_mask = RUIN_LAYOUT_DISPLAY_BLOCK | RUIN_LAYOUT_DISPLAY_TABLE_CELL | RUIN_LAYOUT_DISPLAY_LIST_ITEM; /* Since the meaning of "containing block" can be a bit context-specific, we use the display_type_mask to indicate what type of nodes we're counting as containing blocks for resolving relative widths in this lookup... */ static void ruin_layout_normalize_length(ruin_window_t *w, ruin_length_t *l, ruin_util_list *inheritance, int is_height, int round_to_zero_allowed, int display_type_mask) { int cb_dimension = 0; int d = is_height ? w->font_height : w->font_width; if ((l == NULL) || (inheritance == NULL)) return; if (ruin_util_list_length(inheritance) > 0) { ruin_element_t *containing_block = ruin_layout_find_containing_block (inheritance, display_type_mask); cb_dimension = is_height ? containing_block->height.used : containing_block->width.used; } /* Can't normalize a value if it's not set... */ if ((l->computed == RUIN_LAYOUT_VALUE_AUTO) || (l->computed == RUIN_LAYOUT_VALUE_NONE)) return; switch(l->units) { case RUIN_LAYOUT_UNITS_PIXELS: l->used = floorf(l->computed / d); break; case RUIN_LAYOUT_UNITS_IN: l->used = floorf((l->computed * w->dpi) / d); break; case RUIN_LAYOUT_UNITS_CM: l->used = floorf((l->computed * w->dpi) / (d * 2.54)); break; case RUIN_LAYOUT_UNITS_MM: l->used = floorf((l->computed * w->dpi) / (d * 25.4)); break; case RUIN_LAYOUT_UNITS_PT: l->used = floorf((l->computed * w->dpi) / (72 * d)); break; case RUIN_LAYOUT_UNITS_PC: l->used = floorf(((l->computed * w->dpi) / 6) / d); break; case RUIN_LAYOUT_UNITS_PERCENT: l->used = floorf((l->computed * cb_dimension) / 100); break; default: l->used = l->computed; } if (!round_to_zero_allowed && l->computed != 0 && l->used == 0) { l->used = 1; } return; } void ruin_layout_normalize_lengths(ruin_element_t *t, ruin_util_list *inh, int mask) { ruin_window_t *w = t->parent_window; /* First need to determine maximums and minimums. */ if (t->max_width.computed == RUIN_LAYOUT_VALUE_NONE) t->max_width.used = SHRT_MAX; else ruin_layout_normalize_length(w, &t->max_width, inh, FALSE, TRUE, mask); if (t->max_height.computed == RUIN_LAYOUT_VALUE_NONE) t->max_height.used = SHRT_MAX; else ruin_layout_normalize_length(w, &t->max_height, inh, TRUE, TRUE, mask); ruin_layout_normalize_length(w, &t->min_width, inh, FALSE, TRUE, mask); ruin_layout_normalize_length(w, &t->min_height, inh, TRUE, TRUE, mask); /* Next, we normalize the computed values against the maximums we just obtained. */ ruin_layout_normalize_length(w, &t->width, inh, FALSE, TRUE, mask); ruin_layout_normalize_length(w, &t->margin_left, inh, FALSE, TRUE, mask); ruin_layout_normalize_length(w, &t->margin_right, inh, FALSE, TRUE, mask); ruin_layout_normalize_length(w, &t->padding_left, inh, FALSE, TRUE, mask); ruin_layout_normalize_length(w, &t->padding_right, inh, FALSE, TRUE, mask); ruin_layout_normalize_length (w, &t->border_left_width, inh, FALSE, FALSE, mask); ruin_layout_normalize_length (w, &t->border_right_width, inh, FALSE, FALSE, mask); ruin_layout_normalize_length(w, &t->height, inh, TRUE, TRUE, mask); ruin_layout_normalize_length(w, &t->margin_top, inh, TRUE, TRUE, mask); ruin_layout_normalize_length(w, &t->margin_bottom, inh, TRUE, TRUE, mask); ruin_layout_normalize_length(w, &t->padding_top, inh, TRUE, TRUE, mask); ruin_layout_normalize_length(w, &t->padding_bottom, inh, TRUE, TRUE, mask); ruin_layout_normalize_length (w, &t->border_top_width, inh, TRUE, FALSE, mask); ruin_layout_normalize_length (w, &t->border_bottom_width, inh, TRUE, FALSE, mask); ruin_layout_normalize_length(w, &t->text_indent, inh, FALSE, TRUE, mask); } static void _ruin_layout_parse_size(ruin_element_t *t, ruin_util_list *inh, ruin_length_t *l, char *property, int allow_negative) { char *v = ruin_css_lookup(t, property, inh); if (strcmp(v, "auto") == 0) l->computed = RUIN_LAYOUT_VALUE_AUTO; else if ((strcmp(v, "thin") == 0) || (strcmp(v, "medium") == 0)) { l->computed = 1; l->units = RUIN_LAYOUT_UNITS_CHARS; } else if ((strcmp(v, "thick") == 0)) { l->computed = 2; l->units = RUIN_LAYOUT_UNITS_CHARS; } else { float d = 0; int matches = 0; char units[3]; units[0] = 0; units[1] = 0; units[2] = 0; if (v[0] == '+') v++; matches = sscanf(v, "%f%c%c", &d, &units[0], &units[1]); if (matches == 3) { if ((d >= 0) || ((d < 0) && allow_negative)) l->computed = d; if ((strcmp(units, "em") == 0) || (strcmp(units, "ex") == 0)) l->units = RUIN_LAYOUT_UNITS_CHARS; else if (strcmp(units, "px") == 0) l->units = RUIN_LAYOUT_UNITS_PIXELS; else if (strcmp(units, "pt") == 0) l->units = RUIN_LAYOUT_UNITS_PT; else if (strcmp(units, "pc") == 0) l->units = RUIN_LAYOUT_UNITS_PC; else if (strcmp(units, "in") == 0) l->units = RUIN_LAYOUT_UNITS_IN; else if (strcmp(units, "cm") == 0) l->units = RUIN_LAYOUT_UNITS_CM; else if (strcmp(units, "mm") == 0) l->units = RUIN_LAYOUT_UNITS_MM; } else if (matches == 2) { if ((d >= 0) || ((d < 0) && allow_negative)) l->computed = d; if (strcmp(units, "%") == 0) l->units = RUIN_LAYOUT_UNITS_PERCENT; } else if (matches == 1) { if ((d >= 0) || ((d < 0) && allow_negative)) l->computed = d; l->units = RUIN_LAYOUT_UNITS_CHARS; } } } static void _ruin_layout_parse_sizes(ruin_element_t *t, ruin_util_list *inh) { /* Do we have to do this every time? */ _ruin_layout_parse_size(t, inh, &(t->width), "width", FALSE); _ruin_layout_parse_size(t, inh, &(t->height), "height", FALSE); _ruin_layout_parse_size(t, inh, &(t->min_width), "min-width", FALSE); _ruin_layout_parse_size(t, inh, &(t->max_width), "max-width", FALSE); _ruin_layout_parse_size(t, inh, &(t->min_height), "min-height", FALSE); _ruin_layout_parse_size(t, inh, &(t->max_height), "max-height", FALSE); _ruin_layout_parse_size(t, inh, &(t->padding_top), "padding-top", FALSE); _ruin_layout_parse_size(t, inh, &(t->padding_left), "padding-left", FALSE); _ruin_layout_parse_size (t, inh, &(t->padding_bottom), "padding-bottom", FALSE); _ruin_layout_parse_size(t, inh, &(t->padding_right), "padding-right", FALSE); _ruin_layout_parse_size(t, inh, &(t->text_indent), "text-indent", FALSE); _ruin_layout_parse_size(t, inh, &(t->margin_top), "margin-top", TRUE); _ruin_layout_parse_size(t, inh, &(t->margin_left), "margin-left", TRUE); _ruin_layout_parse_size(t, inh, &(t->margin_bottom), "margin-bottom", TRUE); _ruin_layout_parse_size(t, inh, &(t->margin_right), "margin-right", TRUE); if (strcmp(ruin_css_lookup(t, "border-top-style", inh), "none") != 0) _ruin_layout_parse_size(t, inh, &(t->border_top_width), "border-top-width", FALSE); if (strcmp(ruin_css_lookup(t, "border-left-style", inh), "none") != 0) _ruin_layout_parse_size (t, inh, &(t->border_left_width), "border-left-width", FALSE); if (strcmp(ruin_css_lookup(t, "border-bottom-style", inh), "none") != 0) _ruin_layout_parse_size (t, inh, &(t->border_bottom_width), "border-bottom-width", FALSE); if (strcmp(ruin_css_lookup(t, "border-right-style", inh), "none") != 0) _ruin_layout_parse_size (t, inh, &(t->border_right_width), "border-right-width", FALSE); if (strcmp(ruin_css_lookup(t, "letter-spacing", inh), "normal") != 0) _ruin_layout_parse_size (t, inh, &(t->letter_spacing), "letter-spacing", FALSE); if (strcmp(ruin_css_lookup(t, "word-spacing", inh), "normal") == 0) { t->word_spacing.units = RUIN_LAYOUT_UNITS_CHARS; t->word_spacing.computed = 1; t->word_spacing.used = 1; } else _ruin_layout_parse_size (t, inh, &(t->word_spacing), "word-spacing", FALSE); } ruin_element_t *ruin_element_new() { ruin_element_t *t = calloc(1, sizeof(ruin_element_t)); t->internal_id = ruin_util_generate_id(); t->visible = TRUE; t->id = strdup(""); t->style_cache = ruin_util_hash_new(); t->inherent_attribute_style = scm_list_n(SCM_UNDEFINED); t->additional_attribute_style = scm_list_n(SCM_UNDEFINED); scm_gc_protect_object(t->inherent_attribute_style); scm_gc_protect_object(t->additional_attribute_style); t->capture_events = ruin_util_hash_new(); t->target_events = ruin_util_hash_new(); t->bubble_events = ruin_util_hash_new(); t->max_width.computed = RUIN_LAYOUT_VALUE_NONE; t->max_height.computed = RUIN_LAYOUT_VALUE_NONE; t->width.computed = RUIN_LAYOUT_VALUE_AUTO; t->height.computed = RUIN_LAYOUT_VALUE_AUTO; t->margin_top.computed = RUIN_LAYOUT_VALUE_AUTO; t->margin_left.computed = RUIN_LAYOUT_VALUE_AUTO; t->margin_bottom.computed = RUIN_LAYOUT_VALUE_AUTO; t->margin_right.computed = RUIN_LAYOUT_VALUE_AUTO; t->dirty = TRUE; return t; } void ruin_element_free(ruin_element_t *t) { ruin_util_hash_free(t->style_cache); ruin_util_hash_free(t->capture_events); ruin_util_hash_free(t->target_events); ruin_util_hash_free(t->bubble_events); scm_gc_unprotect_object(t->inherent_attribute_style); scm_gc_unprotect_object(t->additional_attribute_style); free(t->id); free(t); return; } ruin_element_t *ruin_layout_find_containing_block(ruin_util_list *i, int dtm) { int j = 0, len = ruin_util_list_length(i); ruin_element_t *elt_ptr = NULL; for (j = 0; j < len; j++) { char *d = NULL; elt_ptr = i->data; d = ruin_css_lookup(elt_ptr, "display", i); if (((RUIN_LAYOUT_DISPLAY_BLOCK & dtm) && (strcmp(d, "block") == 0)) || ((RUIN_LAYOUT_DISPLAY_INLINE_BLOCK & dtm) && (strcmp(d, "inline-block") == 0)) || ((RUIN_LAYOUT_DISPLAY_INLINE & dtm) && (strcmp(d, "inline") == 0)) || ((RUIN_LAYOUT_DISPLAY_LIST_ITEM & dtm) && (strcmp(d, "list-item") == 0)) || ((RUIN_LAYOUT_DISPLAY_NONE & dtm) && (strcmp(d, "none") == 0)) || ((RUIN_LAYOUT_DISPLAY_RUN_IN & dtm) && (strcmp(d, "run-in") == 0)) || ((RUIN_LAYOUT_DISPLAY_TABLE & dtm) && (strcmp(d, "table") == 0)) || ((RUIN_LAYOUT_DISPLAY_INLINE_TABLE & dtm) && (strcmp(d, "inline-table") == 0)) || ((RUIN_LAYOUT_DISPLAY_TABLE_ROW_GROUP & dtm) && (strcmp(d, "table-row-group") == 0)) || ((RUIN_LAYOUT_DISPLAY_TABLE_COLUMN & dtm) && (strcmp(d, "table-column") == 0)) || ((RUIN_LAYOUT_DISPLAY_TABLE_COLUMN_GROUP & dtm) && (strcmp(d, "table-column-group") == 0)) || ((RUIN_LAYOUT_DISPLAY_TABLE_HEADER_GROUP & dtm) && (strcmp(d, "table-header-group") == 0)) || ((RUIN_LAYOUT_DISPLAY_TABLE_FOOTER_GROUP & dtm) && (strcmp(d, "table-footer-group") == 0)) || ((RUIN_LAYOUT_DISPLAY_TABLE_ROW & dtm) && (strcmp(d, "table-row") == 0)) || ((RUIN_LAYOUT_DISPLAY_TABLE_CELL & dtm) && (strcmp(d, "table-cell") == 0)) || ((RUIN_LAYOUT_DISPLAY_TABLE_CAPTION & dtm) && (strcmp(d, "table-caption") == 0))) break; i = i->next; } return elt_ptr; } /* This will analyze the size requirements of the text contained in inline element t when constrained by dimensions w and h; if either w or h is negative, take this to mean "unconstrained." We need the entire element because it contains information about word / line spacing etc. */ ruin_layout_size_t ruin_layout_size_text(ruin_element_t *t, ruin_util_list *inheritance, int top, int left, int first_line, int w, int h, int last_ended_with_space) { int i; int current_line_length = 0; int num_lines = 1; int max_line_length = 0; ruin_layout_size_t return_val; _ruin_layout_parse_sizes(t, inheritance); ruin_layout_normalize_lengths(t, inheritance, ruin_layout_block_mask); t->top = top; t->left = left; t->first_line_start = left + first_line + t->margin_left.used; max_line_length = t->margin_left.used; return_val.first_line = t->margin_left.used; return_val.height = 0; return_val.width = 0; return_val.last_line = 0; current_line_length = first_line + t->parent->text_indent.used + t->margin_left.used; if (t->content != NULL) { char **words = NULL; int *word_lens = NULL; char *wstype = ruin_css_lookup(t, "white-space", inheritance); if (strcmp(wstype, "normal") == 0) { int num_words = ruin_render_get_words(t->content, &words, &word_lens); if ((num_words > 0) && (isspace(words[0][0]) && ((first_line == 0) || last_ended_with_space))) word_lens[0]--; for (i = 0; i < num_words; i++) { int addition = word_lens[i] * (1 + t->letter_spacing.used); if (w > 0 && (current_line_length + addition > w)) { if (addition > w) { addition -= w - current_line_length; num_lines++; while(addition > 0) { addition -= w; num_lines++; } current_line_length = w + addition; } else { current_line_length = 0; num_lines++; } } else { current_line_length += addition; if (current_line_length > max_line_length) max_line_length = current_line_length; /* Did the current word overflow the line box? */ if (i < (num_words - 1)) { current_line_length += t->word_spacing.used; if (w > 0 && (current_line_length > w)) { /* Did the word-spacing overflow the line box? If so, ditch it, because we don't observe whitespace across lines. */ current_line_length = 0; num_lines++; } } } } } else if (strcmp(wstype, "pre") == 0) { } else if (strcmp(wstype, "nowrap") == 0) { (void) ruin_render_get_words(t->content, &words, &word_lens); if ((isspace(words[0][0])) && ((first_line == 0) || last_ended_with_space)) word_lens[0]--; } else if (strcmp(wstype, "pre-wrap") == 0) { } else if (strcmp(wstype, "pre-line") == 0) { (void) ruin_render_get_words(t->content, &words, &word_lens); if ((isspace(words[0][0])) && ((first_line == 0) || last_ended_with_space)) word_lens[0]--; } if (current_line_length > max_line_length) max_line_length = current_line_length; return_val.height = ((h >= 0) && (num_lines > h)) ? h : num_lines; if (return_val.height > 1) return_val.width = max_line_length; else return_val.width = current_line_length - first_line; } else { int line_start = first_line; int offer_top = top; ruin_element_t *elt_ptr = t->first_child; last_ended_with_space = FALSE; return_val.height = 1; while(elt_ptr != NULL) { if (strcmp(ruin_css_lookup(elt_ptr, "display", inheritance), "inline") == 0) { ruin_layout_size_t ret = ruin_layout_size_text (elt_ptr, inheritance, offer_top, left, line_start, w, h == -1 ? h : (h - num_lines), last_ended_with_space); /* Did the whole child fit on the same line? */ if ((ret.height == 1) && ((w == -1) || (current_line_length + ret.first_line < w))) { line_start += ret.width; current_line_length += ret.width; return_val.width += ret.width; } else { return_val.height += ret.height - 1; line_start = ret.last_line; current_line_length = ret.last_line; if (ret.width > return_val.width) return_val.width = ret.width; } } if (elt_ptr->content != NULL) last_ended_with_space = isspace(elt_ptr->content[strlen(elt_ptr->content) - 1]); elt_ptr = elt_ptr->next_sibling; } } if ((t->next_sibling != NULL) && (strcmp(ruin_css_lookup(t->next_sibling, "display", inheritance), "inline") == 0)) { /* Need to size sibling inline elements here, where we have the detailed line information... */ ruin_layout_size_text (t->next_sibling, inheritance, top + return_val.height - 1, left, current_line_length + (last_ended_with_space ? 1 : 0), w, h, last_ended_with_space); } return_val.last_line = current_line_length + t->margin_right.used; if (return_val.height == 1) { t->width.used = return_val.last_line - first_line; return_val.first_line = return_val.last_line; } else t->width.used = return_val.width; t->last_line_length = current_line_length; t->height.used = return_val.height; return return_val; } int _get_block_level_width(ruin_element_t *t, ruin_util_list *inheritance, int mask) { int autos = 0, i = 0, limit = 7, width_auto = FALSE; int tentative_width = t->width.used; ruin_length_t *widths[7]; /* The order here is important. */ widths[0] = &t->margin_left; widths[1] = &t->margin_right; widths[2] = &t->padding_left; widths[3] = &t->padding_right; widths[4] = &t->border_left_width; widths[5] = &t->border_right_width; widths[6] = &t->width; for (i = 0; i < limit; i++) { if (widths[i]->computed == RUIN_LAYOUT_VALUE_AUTO) { if (widths[i] == &t->width) width_auto = TRUE; autos++; } /* } else { widths[i]->used = widths[i]->computed; } */ } for (i = 0; i < 3; i++) { int autos_copy = autos, which = -1, total_width = 0, j = 0; switch(i) { case 0: break; case 1: limit = 6; if (width_auto) autos_copy--; if (tentative_width > t->max_width.used) tentative_width = t->max_width.used; else continue; case 2: if (width_auto) autos_copy--; if (tentative_width < t->min_width.used) tentative_width = t->min_width.used; else continue; } for (j = 0; j < limit; j++) { if (widths[j]->computed == RUIN_LAYOUT_VALUE_AUTO) { if (autos_copy == 1) { which = j; break; } else { widths[j]->used = 0; autos_copy--; } } } if (which == -1) { if (strcmp(ruin_css_lookup(t, "direction", inheritance), "ltr") == 0) which = 1; else which = 0; } if (limit == 6) total_width = tentative_width; else total_width = 0; for (j = 0; j < limit; j++) if (j != which) total_width += widths[j]->used; if (t->parent == NULL) widths[which]->used = t->max_width.used - total_width; else widths[which]->used = ruin_layout_find_containing_block(inheritance, mask)->width.used - total_width; if ((widths[which]->used < 0) && (widths[which] != &t->margin_left) && (widths[which] != &t->margin_right)) widths[which] = 0; if (limit == 7) tentative_width = t->width.used; } return tentative_width; } ruin_layout_size_t ruin_layout_size_block(ruin_element_t *t, ruin_util_list *inheritance, int top, int left) { /* Important to note -- block-level width is NOT a function of the size of the children. */ ruin_layout_size_t result = { 0, 0, 0, 0 }; int tentative_width = 0, children_cumulative_height = 0; /* Let's go ahead and set the top + left of this element. */ t->top = top; t->left = left; ruin_layout_normalize_lengths(t, inheritance, ruin_layout_block_mask); tentative_width = _get_block_level_width(t, inheritance, ruin_layout_block_mask); /* We calculate the height of the child elements -- the width shouldn't change after this point, so we shouldn't have to calculate this more than once. */ { ruin_element_t *elt_ptr; char *d = NULL; int offer_top = t->top + t->margin_top.used + t->border_top_width.used; int offer_left = t->left + t->margin_left.used + t->border_left_width.used + t->padding_left.used; int last_was_inline = FALSE; if (t->width.computed != RUIN_LAYOUT_VALUE_AUTO) { } elt_ptr = t->first_child; inheritance = ruin_util_list_push_front (inheritance, ruin_util_list_new(t)); while (elt_ptr != NULL) { if (!((strcmp(d = ruin_css_lookup(elt_ptr, "display", inheritance), "inline") == 0) && last_was_inline)) { ruin_layout_size_t result; offer_top += t->padding_top.used; result = ruin_layout_size_tree (elt_ptr, inheritance, offer_top, offer_left); children_cumulative_height += t->padding_top.used + result.height + t->padding_bottom.used; offer_top += result.height + t->padding_bottom.used; if (strcmp(d, "inline") == 0) last_was_inline = TRUE; else last_was_inline = FALSE; } elt_ptr = elt_ptr->next_sibling; } free(inheritance); } result.width = tentative_width; result.height = (children_cumulative_height > t->height.used ? children_cumulative_height : t->height.used) + t->margin_top.used + t->border_top_width.used + t->border_bottom_width.used + t->margin_bottom.used; if (t->height.computed == RUIN_LAYOUT_VALUE_AUTO) { t->height.used = children_cumulative_height - (t->first_child == NULL ? 0 : (t->padding_top.used + t->padding_bottom.used)); } return result; } ruin_layout_size_t ruin_layout_size_inline(ruin_element_t *t, ruin_util_list *inheritance, int top, int left) { ruin_layout_size_t return_val; inheritance = ruin_util_list_push_front(inheritance, ruin_util_list_new(t)); return_val = ruin_layout_size_text (t, inheritance, top, left, 0, t->parent->width.used, -1, FALSE); t = t->next_sibling; while(t != NULL) { if (t->first_line_start + t->width.used > t->parent->width.used) return_val.height++; t = t->next_sibling; } free(inheritance); return return_val; } ruin_layout_size_t ruin_layout_size_table_fixed(ruin_element_t *t, ruin_util_list *inheritance, ruin_util_list *column_list, ruin_util_list *row_list, int top, int left) { ruin_util_list *widths = NULL; ruin_element_t *row_ptr = NULL; int first_cell = TRUE; ruin_layout_size_t return_val = { 0, 0, 0, 0 }; int offer_top = top, offer_left = left; int offer_top_adjusted = top; ruin_util_list *tmp = ruin_util_list_new(t); ruin_layout_normalize_lengths(t, inheritance, ruin_layout_block_mask); offer_top += t->margin_top.used + t->padding_top.used + t->border_top_width.used; offer_left += t->margin_left.used + t->padding_left.used + t->border_left_width.used; tmp->next = inheritance; inheritance = tmp; while(row_list != NULL) { ruin_element_t *cell_ptr = NULL; int offer_left_adjusted = offer_left; int running_max = -1; ruin_util_list *tmp = inheritance; row_ptr = row_list->data; cell_ptr = row_ptr->first_child; /* Now we have to adjust the top and left to take row and row group settings into account. */ ruin_layout_normalize_lengths /* row group */ (row_ptr->parent, inheritance, RUIN_LAYOUT_DISPLAY_TABLE); ruin_layout_normalize_lengths (row_ptr, inheritance, RUIN_LAYOUT_DISPLAY_TABLE); inheritance = ruin_util_list_new(row_ptr->parent); inheritance->next = tmp; tmp = inheritance; inheritance = ruin_util_list_new(row_ptr); inheritance->next = tmp; offer_top_adjusted += t->padding_top.used; while (cell_ptr != NULL) { if (first_cell) { if (column_list != NULL) { ruin_element_t *col_ptr = (ruin_element_t *) column_list->data; if (col_ptr->width.computed != RUIN_LAYOUT_VALUE_AUTO) widths->next = ruin_util_list_new (ruin_util_int_to_string(col_ptr->width.used)); } else if (cell_ptr->width.computed != RUIN_LAYOUT_VALUE_AUTO) { char *colspan = ruin_css_lookup (cell_ptr, "column-span", inheritance); if (colspan != NULL) { } else { widths->next = ruin_util_list_new (ruin_util_int_to_string(cell_ptr->width.used)); } } first_cell = FALSE; } offer_left_adjusted += t->padding_left.used; ruin_layout_size_tree (cell_ptr, inheritance, offer_top_adjusted, offer_left_adjusted); offer_left_adjusted += atoi(widths->data) + t->padding_right.used; if (running_max < cell_ptr->height.used) running_max = cell_ptr->height.used; cell_ptr = cell_ptr->next_sibling; } tmp = inheritance; inheritance = inheritance->next; free(tmp); tmp = inheritance; inheritance = inheritance->next; free(tmp); offer_top_adjusted += running_max + t->padding_bottom.used; } return return_val; } int ruin_layout_get_max_width(ruin_element_t *t, ruin_util_list *inheritance) { int max = 0; ruin_element_t *elt_ptr = t->first_child; int mask = ruin_layout_block_mask | RUIN_LAYOUT_DISPLAY_TABLE; _ruin_layout_parse_sizes(t, inheritance); ruin_layout_normalize_lengths(t, inheritance, mask); while (elt_ptr != NULL) { if (strcmp(ruin_css_lookup(elt_ptr, "display", inheritance), "inline") != 0) { int r = ruin_layout_get_max_width(elt_ptr, inheritance); if (r > max) max = r; } else { ruin_layout_size_t r = ruin_layout_size_text(elt_ptr, inheritance, t->top, t->left, 0, -1, -1, FALSE); if ((elt_ptr->prev_sibling != NULL) && (strcmp(ruin_css_lookup(elt_ptr->prev_sibling, "display", inheritance), "inline") == 0)) { max += r.width; } else { if (r.width > max) max = r.width; } } elt_ptr = elt_ptr->next_sibling; } if (t->padding_left.computed != RUIN_LAYOUT_VALUE_AUTO) max += t->padding_left.used; if (t->padding_right.computed != RUIN_LAYOUT_VALUE_AUTO) max += t->padding_right.used; if (t->border_left_width.computed != RUIN_LAYOUT_VALUE_AUTO) max += t->border_left_width.used; if (t->border_right_width.computed != RUIN_LAYOUT_VALUE_AUTO) max += t->border_right_width.used; return max; } /* The minimum width is the width of the element after we squeeze it just to the point of overflow. */ int ruin_layout_get_min_width(ruin_element_t *t, ruin_util_list *inheritance) { int max = 0; int mask = ruin_layout_block_mask | RUIN_LAYOUT_DISPLAY_TABLE; ruin_element_t *elt_ptr = NULL; char *d = ruin_css_lookup(t, "display", inheritance); int sib_max = 0; _ruin_layout_parse_sizes(t, inheritance); ruin_layout_normalize_lengths(t, inheritance, mask); if (strcmp(d, "table-row") == 0) { mask = RUIN_LAYOUT_DISPLAY_TABLE_ROW; } else if ((strcmp(d, "block") == 0) || (strcmp(d, "table-cell") == 0)) { elt_ptr = t->first_child; while (elt_ptr != NULL) { int width = 0; ruin_util_list *inh2 = ruin_util_list_new(t); inh2->next = inheritance; width = ruin_layout_get_min_width(elt_ptr, inh2); if (width > max) max = width; elt_ptr = elt_ptr->next_sibling; } } else if (strcmp(d, "inline") == 0) { ruin_layout_normalize_lengths(t, inheritance, mask); if (t->content != NULL) { int i = 0, width = 0, len = strlen(t->content); for (i = 0; i < len; i++) { if (isspace(t->content[i])) { if (width > max) max = width; width = 0; } else width++; } } } else { ruin_layout_normalize_lengths(t, inheritance, mask); } if (t->width.computed != RUIN_LAYOUT_VALUE_AUTO && t->width.used > max) max = t->width.used; if (t->padding_left.computed != RUIN_LAYOUT_VALUE_AUTO) max += t->padding_left.used; if (t->padding_right.computed != RUIN_LAYOUT_VALUE_AUTO) max += t->padding_right.used; if (t->border_left_width.computed != RUIN_LAYOUT_VALUE_AUTO) max += t->border_left_width.used; if (t->border_right_width.computed != RUIN_LAYOUT_VALUE_AUTO) max += t->border_right_width.used; if (t->next_sibling != NULL) sib_max = ruin_layout_get_min_width(t->next_sibling, inheritance); if (sib_max > max) max = sib_max; return max; } ruin_layout_size_t ruin_layout_size_table_cell(ruin_element_t *c, ruin_util_list *inheritance, int width, int height, int top, int left) { ruin_layout_size_t result; /* Don't need to parse sizes here -- that should have taken place during min/max calculations... */ ruin_layout_normalize_lengths (c, inheritance, RUIN_LAYOUT_DISPLAY_TABLE_ROW); if (width > 0) c->width.used = width - c->border_left_width.used - c->border_right_width.used - c->padding_left.used - c->padding_right.used; if (height > 0) c->height.used = height - c->border_top_width.used - c->border_bottom_width.used - c->padding_top.used - c->padding_bottom.used; c->top = top; c->left = left; inheritance = ruin_util_list_push_front(inheritance, ruin_util_list_new(c)); result = ruin_layout_size_tree (c->first_child, inheritance, top + c->border_top_width.used + c->padding_top.used, left + c->border_left_width.used + c->padding_left.used); free(inheritance); if (c->height.computed == RUIN_LAYOUT_VALUE_AUTO) c->height.used = result.height; result.height += c->border_top_width.used + c->padding_top.used + c->border_bottom_width.used + c->padding_bottom.used; return result; } /* This function uses a layout algorithm derived from KHTML which is more or less functionally equivalent to the one used by FF/IE. */ ruin_layout_size_t ruin_layout_size_table_auto(ruin_element_t *t, ruin_util_list *inheritance, ruin_util_list *column_list, ruin_util_list *row_list, int top, int left) { ruin_layout_size_t return_val = { 0, 0, 0, 0 }; ruin_util_list *column_widths = NULL; ruin_util_list *column_list_ptr = NULL; ruin_util_list *row_list_ptr = NULL; int i = 0, num_rows = ruin_util_list_length(row_list); int num_columns = 0; int offer_top = top, offer_left = left, offer_top_adjusted; int row_cumulative_width = 0; ruin_layout_normalize_lengths(t, inheritance, ruin_layout_block_mask); t->width.used = _get_block_level_width(t, inheritance, ruin_layout_block_mask); return_val.height = 0; return_val.width = 0; t->top = top; t->left = left; for (i = 0; i < num_rows; i++) { int j = 0; ruin_element_t *row = (ruin_element_t *) ruin_util_list_get_ith(row_list, i)->data; ruin_element_t *cell_ptr = row->first_child; ruin_layout_normalize_lengths (row, inheritance, RUIN_LAYOUT_DISPLAY_TABLE_ROW); inheritance = ruin_util_list_push_front (inheritance, ruin_util_list_new(row)); /* Obtain minimum and maximum content widths for the columns. */ while(cell_ptr != NULL) { int max_width = 0, min_width = 0; ruin_layout_table_width_blob_t *b = NULL; ruin_element_t *width_calc_start = cell_ptr; /* If the table cell is an artificially inserted node (such as for XUL, which doesn't require anything like "td") we need to snarf the width from cell's first child. */ _ruin_layout_parse_sizes(cell_ptr, inheritance); if (scm_string_p(cell_ptr->element) == SCM_BOOL_T) { _ruin_layout_parse_sizes(cell_ptr->first_child, inheritance); cell_ptr->width = cell_ptr->first_child->width; width_calc_start = cell_ptr->first_child; } ruin_layout_normalize_lengths (cell_ptr, inheritance, ruin_layout_block_mask | RUIN_LAYOUT_DISPLAY_TABLE); max_width = ruin_layout_get_max_width(width_calc_start, inheritance); min_width = ruin_layout_get_min_width(width_calc_start, inheritance); if (ruin_util_list_length(column_widths) < j + 1) { b = calloc(1, sizeof(ruin_layout_table_width_blob_t)); b->max_width = max_width; b->min_width = min_width; b->width.units = cell_ptr->width.units; b->width.computed = cell_ptr->width.computed; column_widths = ruin_util_list_append (column_widths, ruin_util_list_new(b)); } else { b = ruin_util_list_get_ith(column_widths, j)->data; if (max_width > b->max_width) b->max_width = max_width; if (min_width > b->min_width) b->min_width = min_width; if (cell_ptr->width.computed != RUIN_LAYOUT_VALUE_AUTO) { /* Percentage widths apparently clobber other types of widths for the purposes of these calculations... */ if (cell_ptr->width.units == RUIN_LAYOUT_UNITS_PERCENT) b->width = cell_ptr->width; else if (cell_ptr->width.used > b->width.used) { b->width.units = RUIN_LAYOUT_UNITS_CHARS; b->width.computed = b->width.used = cell_ptr->width.used; } } } cell_ptr = cell_ptr->next_sibling; j++; } inheritance = inheritance->next; } num_columns = ruin_util_list_length(column_list); /* Now set the effective widths on the columns. */ /* TODO: Revise this and the above to take spanning cells into account. */ { int p_widths = 0, abs_mwidths = 0, auto_mwidths = 0; int num_auto = 0; int available = t->width.used; ruin_util_list *column_widths_ptr = column_widths; ruin_util_list *column_list_ptr = column_list; for (i = 0; i < num_columns; i++) { ruin_layout_table_width_blob_t *b = (ruin_layout_table_width_blob_t *) column_widths_ptr->data; ruin_element_t *col = (ruin_element_t *) column_list_ptr->data; b->eff_min_width = b->min_width; b->eff_max_width = b->max_width; if (b->width.units == RUIN_LAYOUT_UNITS_PERCENT) p_widths += b->width.computed; else if (b->width.computed == RUIN_LAYOUT_VALUE_AUTO) { auto_mwidths += b->eff_max_width; num_auto++; } else abs_mwidths += b->eff_max_width; col->width = b->eff_width = b->width; column_list_ptr = column_list_ptr->next; column_widths_ptr = column_widths_ptr->next; } /* Distribute width to percent-valued columns. */ if ((available > 0) && (p_widths > 0)) { ruin_util_list *column_list_ptr = column_list; ruin_util_list *column_widths_ptr = column_widths; for (i = 0; i < num_columns; i++) { ruin_element_t *col = (ruin_element_t *) column_list_ptr->data; if (col->width.units == RUIN_LAYOUT_UNITS_PERCENT) { ruin_layout_table_width_blob_t *b = (ruin_layout_table_width_blob_t *) column_widths_ptr->data; int w = MIN(b->eff_min_width, col->width.used); col->width.used = w; available -= w; } column_list_ptr = column_list_ptr->next; column_widths_ptr = column_widths_ptr->next; } if (p_widths > 100) { /* Reduce, starting from the right, the percent column widths 'til they're under 100... */ } } /* Adjust fixed-width columns if effective width is more than computed width. */ if (available > 0) { ruin_util_list *column_list_ptr = column_list; ruin_util_list *column_widths_ptr = column_widths; for (i = 0; i < num_columns; i++) { ruin_element_t *col = (ruin_element_t *) column_list_ptr->data; if ((col->width.units != RUIN_LAYOUT_UNITS_PERCENT) && (col->width.computed != RUIN_LAYOUT_VALUE_AUTO)) { ruin_layout_table_width_blob_t *b = (ruin_layout_table_width_blob_t *) column_widths_ptr->data; int w = MAX(b->eff_width.used, col->width.used); col->width.used = w; available -= w; } column_list_ptr = column_list_ptr->next; column_widths_ptr = column_widths_ptr->next; } } /* Distribute the remaining width among auto-valued columns */ if ((available > 0) && (num_auto > 0)) { ruin_util_list *column_list_ptr = column_list; ruin_util_list *column_widths_ptr = column_widths; int old_avail = available; for (i = 0; i < num_columns; i++) { ruin_element_t *col = (ruin_element_t *) column_list_ptr->data; if (col->width.computed == RUIN_LAYOUT_VALUE_AUTO) { ruin_layout_table_width_blob_t *b = (ruin_layout_table_width_blob_t *) column_widths_ptr->data; int w = old_avail * ((float) b->eff_max_width / auto_mwidths); col->width.used = w; available -= w; } column_list_ptr = column_list_ptr->next; column_widths_ptr = column_widths_ptr->next; } } /* If there's still available width, add it to percentage and fixed-width columns. */ if ((available > 0) && (p_widths > 0)) { ruin_util_list *column_list_ptr = column_list; ruin_util_list *column_widths_ptr = column_widths; for (i = 0; i < num_columns; i++) { ruin_element_t *col = (ruin_element_t *) column_list_ptr->data; if (col->width.units == RUIN_LAYOUT_UNITS_PERCENT) { ruin_layout_table_width_blob_t *b = (ruin_layout_table_width_blob_t *) column_widths_ptr->data; int w = b->eff_max_width * (available / p_widths); col->width.used += w; available -= w; } column_list_ptr = column_list_ptr->next; column_widths_ptr = column_widths_ptr->next; } } if ((available > 0) && (abs_mwidths > 0)) { ruin_util_list *column_list_ptr = column_list; ruin_util_list *column_widths_ptr = column_widths; for (i = 0; i < num_columns; i++) { ruin_element_t *col = (ruin_element_t *) column_list_ptr->data; if (col->width.computed != RUIN_LAYOUT_VALUE_AUTO) { ruin_layout_table_width_blob_t *b = (ruin_layout_table_width_blob_t *) column_widths_ptr->data; int w = b->eff_max_width * (available / abs_mwidths); col->width.used += w; available -= w; } column_list_ptr = column_list_ptr->next; column_widths_ptr = column_widths_ptr->next; } } /* If we've run out of available width, we need to trim some from the columns we've allocated it to. */ if (available < 0) { int able_to_reduce; do { ruin_util_list *column_list_ptr = column_list; able_to_reduce = FALSE; for (i = 0; i < num_columns; i++) { ruin_element_t *col = (ruin_element_t *) column_list->data; if ((col->width.computed == RUIN_LAYOUT_VALUE_AUTO) && (col->width.used > 0)) { col->width.used--; able_to_reduce = TRUE; } column_list_ptr = column_list_ptr->next; } } while ((available < 0) && able_to_reduce); } if (available < 0) { int able_to_reduce; do { ruin_util_list *column_list_ptr = column_list; able_to_reduce = FALSE; for (i = 0; i < num_columns; i++) { ruin_element_t *col = (ruin_element_t *) column_list_ptr->data; if ((col->width.units != RUIN_LAYOUT_UNITS_PERCENT) && (col->width.computed != RUIN_LAYOUT_VALUE_AUTO) && (col->width.used > 0)) { col->width.used--; able_to_reduce = TRUE; } column_list_ptr = column_list_ptr->next; } } while ((available < 0) && able_to_reduce); } if (available < 0) { int able_to_reduce; do { ruin_util_list *column_list_ptr = column_list; able_to_reduce = FALSE; for (i = 0; i < num_columns; i++) { ruin_element_t *col = (ruin_element_t *) column_list_ptr->data; if ((col->width.units == RUIN_LAYOUT_UNITS_PERCENT) && (col->width.used > 0)) { col->width.used--; able_to_reduce = TRUE; } column_list_ptr = column_list_ptr->next; } } while ((available < 0) && able_to_reduce); } } offer_top_adjusted = offer_top; row_list_ptr = row_list; t->height.used = 0; for (i = 0; i < num_rows; i++) { ruin_element_t *row = (ruin_element_t *) row_list_ptr->data; ruin_element_t *cell_ptr = row->first_child; int j = 0; int offer_left_adjusted = offer_left; int row_max_height = 0; column_list_ptr = column_list; offer_top_adjusted += t->padding_top.used + t->border_top_width.used; t->height.used += t->padding_top.used + t->border_top_width.used; while (cell_ptr != NULL) { ruin_element_t *col_ptr = (ruin_element_t *) column_list_ptr->data; ruin_layout_size_t result; int inh_free_num = 2; int spanned_columns = 1; char *colspan = scm_string_p(cell_ptr->element) == SCM_BOOL_T ? NULL : ruin_scheme_sdom_get_attribute(cell_ptr->element, "colspan"); int offer_width = 0; offer_left_adjusted += t->padding_left.used + t->border_left_width.used; row_cumulative_width += t->padding_left.used + t->border_left_width.used; if (strcmp(ruin_css_lookup(col_ptr->parent, "display", NULL), "table-column-group") == 0) { inh_free_num++; inheritance = ruin_util_list_push_front (inheritance, ruin_util_list_new(col_ptr->parent)); } inheritance = ruin_util_list_push_front (inheritance, ruin_util_list_new(col_ptr)); if (strcmp(ruin_css_lookup(col_ptr->parent, "display", NULL), "table-row-group") == 0) { inh_free_num++; inheritance = ruin_util_list_push_front (inheritance, ruin_util_list_new(row->parent)); } inheritance = ruin_util_list_push_front (inheritance, ruin_util_list_new(row)); if (colspan != NULL) { spanned_columns = atoi(colspan); do { offer_width += ((ruin_element_t *) column_list_ptr->data)->width.used; column_list_ptr = column_list_ptr->next; spanned_columns--; } while(spanned_columns > 0 && column_list_ptr != NULL); } else offer_width = col_ptr->width.used; result = ruin_layout_size_table_cell (cell_ptr, inheritance, offer_width, -1, offer_top_adjusted, offer_left_adjusted); while(inh_free_num > 0) { inheritance = inheritance->next; inh_free_num--; } if (result.height > row_max_height) row_max_height = result.height; offer_left_adjusted += offer_width; cell_ptr = cell_ptr->next_sibling; /* We may have advanced the column list pointer to the end during the allocation for column-spanning cells... */ if (column_list_ptr != NULL) column_list_ptr = column_list_ptr->next; row_cumulative_width += offer_width; j++; } row->width.used = row_cumulative_width; offer_top_adjusted += row_max_height + t->padding_bottom.used + t->border_bottom_width.used; t->height.used += row_max_height + t->padding_bottom.used + t->border_bottom_width.used; /* Update the row-group's height as well. */ if (strcmp(ruin_css_lookup(row->parent, "display", NULL), "table-row-group") == 0) { ruin_layout_normalize_lengths (row->parent, inheritance, RUIN_LAYOUT_DISPLAY_TABLE); if (row_cumulative_width > row->parent->width.used) row->parent->width.used = row_cumulative_width; row->parent->height.used += row_max_height + t->padding_bottom.used + t->border_bottom_width.used + 1; } row_list_ptr = row_list_ptr->next; } return_val.height = t->height.used; return_val.width = t->width.used; return return_val; } ruin_layout_size_t ruin_layout_size_table(ruin_element_t *t, ruin_util_list *inheritance, int top, int left) { /* There are two different table layout algorithms -- fixed and auto. We need to figure out which one to use, make some calculations about the orientation of the table, then call the appropriate layout function. */ int use_fixed = FALSE; ruin_util_list *column_list = NULL; ruin_util_list *row_list = NULL; ruin_element_t *elt_ptr = t->first_child; { char *layout = ruin_css_lookup(t, "table-layout", inheritance); if ((layout != NULL) && (strcmp(layout, "fixed") == 0)) { use_fixed = TRUE; } } while (elt_ptr != NULL) { ruin_element_t *elt_backup = NULL; char *d = ruin_css_lookup(elt_ptr, "display", inheritance); if (strcmp(d, "table-row") == 0) { row_list = ruin_util_list_append(row_list, ruin_util_list_new(elt_ptr)); } else if (strcmp(d, "table-row-group") == 0) { elt_backup = elt_ptr; elt_ptr = elt_ptr->first_child; while (elt_ptr != NULL) { row_list = ruin_util_list_append (row_list, ruin_util_list_new(elt_ptr)); elt_ptr = elt_ptr->next_sibling; } elt_ptr = elt_backup; } else if (strcmp(d, "table-column") == 0) { column_list = ruin_util_list_append (column_list, ruin_util_list_new(elt_ptr)); } else if (strcmp(d, "table-column-group") == 0) { elt_backup = elt_ptr; elt_ptr = elt_ptr->first_child; while (elt_ptr != NULL) { column_list = ruin_util_list_append (column_list, ruin_util_list_new(elt_ptr)); elt_ptr = elt_ptr->next_sibling; } elt_ptr = elt_backup; break; } elt_ptr = elt_ptr->next_sibling; } /* Memory leak! */ inheritance = ruin_util_list_push_front(inheritance, ruin_util_list_new(t)); if (use_fixed) return ruin_layout_size_table_fixed (t, inheritance, column_list, row_list, top, left); else return ruin_layout_size_table_auto (t, inheritance, column_list, row_list, top, left); } int _get_list_marker_length(ruin_element_t *t, char *style) { int this_pos = 1; ruin_element_t *sib_ptr = NULL; if (strcmp(style, "none") == 0) return 0; else if ((strcmp(style, "disc") == 0) || (strcmp(style, "circle") == 0) || (strcmp(style, "square") == 0) || (strcmp(style, "lower-greek") == 0) || (strcmp(style, "lower-latin") == 0) || (strcmp(style, "upper-latin") == 0) || (strcmp(style, "lower-alpha") == 0) || (strcmp(style, "upper-alpha") == 0)) return 1; sib_ptr = t->prev_sibling; while(sib_ptr != NULL) { this_pos++; sib_ptr = sib_ptr->prev_sibling; } if (strcmp(style, "decimal") == 0) return 1 + (int) floor(log(this_pos) / log(10)); else if (strcmp(style, "decimal-leading-zero") == 0) return 2 + (int) floor(log(this_pos) / log(10)); else if (strcmp(style, "lower-roman") == 0) { char *c = ruin_util_arabic_to_roman(this_pos, FALSE); int l = strlen(c); free(c); return 1 + l; } else if (strcmp(style, "upper-roman") == 0) { char *c = ruin_util_arabic_to_roman(this_pos, TRUE); int l = strlen(c); free(c); return 1 + l; } else { /* Not suppporting Armenian / Georgian numbering yet. I mean, come on. */ return 0; } } ruin_layout_size_t ruin_layout_size_list_item(ruin_element_t *t, ruin_util_list *inheritance, int top, int left) { int pos_inside = FALSE; ruin_layout_size_t return_val = { 0, 0, 0, 0 }; int tentative_width = 0; int marker_len = _get_list_marker_length (t, ruin_css_lookup(t, "list-style-type", inheritance)); ruin_util_list *new_inh = ruin_util_list_new(t); new_inh->next = inheritance; t->top = top + t->margin_top.used; t->left = left + t->margin_left.used; _get_block_level_width(t, inheritance, ruin_layout_block_mask); if (strcmp(ruin_css_lookup(t, "list-style-position", new_inh), "inside") == 0) pos_inside = TRUE; if (pos_inside) { } else { int offer_top = top + t->margin_top.used + t->border_top_width.used; int offer_left = left + t->margin_left.used + t->border_left_width.used + marker_len + (2 * t->padding_left.used) + t->padding_right.used + 1; ruin_element_t *elt_ptr = t->first_child; while(elt_ptr != NULL) { ruin_layout_size_t ret; return_val.height += t->padding_top.used; offer_top += t->padding_top.used; ret = ruin_layout_size_tree(elt_ptr, new_inh, offer_top, offer_left); return_val.height += ret.height + t->padding_bottom.used; offer_top += ret.height + t->padding_bottom.used; elt_ptr = elt_ptr->next_sibling; } } return_val.height += t->margin_top.used + t->margin_bottom.used + t->border_top_width.used + t->border_bottom_width.used; return_val.width = tentative_width; free(new_inh); return return_val; } ruin_layout_size_t ruin_layout_size_none(ruin_element_t *t, ruin_util_list *inheritance, int top, int left) { /* This isn't exactly how this should work... */ ruin_layout_size_t nil = { 0, 0, 0, 0 }; return nil; } ruin_layout_size_t ruin_layout_size_tree(ruin_element_t *t, ruin_util_list *inheritance, int top, int left) { char *d = ruin_css_lookup(t, "display", inheritance); _ruin_layout_parse_sizes(t, inheritance); /* Now switch. */ if ((strcmp(d, "block") == 0) || (strcmp(d, "table-cell") == 0)) return ruin_layout_size_block(t, inheritance, top, left); else if (strcmp(d, "inline") == 0) return ruin_layout_size_inline(t, inheritance, top, left); else if (strcmp(d, "table") == 0) return ruin_layout_size_table(t, inheritance, top, left); else if (strcmp(d, "list-item") == 0) return ruin_layout_size_list_item(t, inheritance, top, left); else return ruin_layout_size_none(t, inheritance, top, left); } void ruin_layout_add_style(SCM *list, char *prop, char *value) { SCM addition = scm_list_2(scm_makfrom0str(prop), scm_makfrom0str(value)); if (scm_eq_p(*list, SCM_EOL) != SCM_BOOL_T) scm_append_x(scm_list_2(SCM_CDR(*list), scm_list_1(addition))); else { *list = scm_list_1(scm_list_2(scm_list_1(scm_makfrom0str("*")), addition)); scm_gc_protect_object(*list); } } void ruin_generate_tree_parse_attrs(ruin_element_t *t) { switch(t->dialect) { case RUIN_LAYOUT_XML_DIALECT_XUL: ruin_xul_generate_tree_parse_attrs(t); break; case RUIN_LAYOUT_XML_DIALECT_XHTML: ruin_xhtml_generate_tree_parse_attrs(t); default: break; } return; }