1 /* box.c: Box generation routines for libRUIN
2 * Copyright (C) 2011 Julian Graham
3 *
4 * libRUIN is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <assert.h>
19 #include <glib.h>
20 #include <libguile.h>
21 #include <math.h>
22 #include <strings.h>
23
24 #include "box.h"
25 #include "css.h"
26 #include "parse.h"
27 #include "window.h"
28
29 static GHashTable *display_table = NULL;
30 static GHashTable *display_table_cell = NULL;
31 static GHashTable *display_table_column = NULL;
32 static GHashTable *display_table_row = NULL;
33
34 static GList *box_generate
35 (ruin_window_t *, ruin_node_t *, ruin_box_t *, ruin_box_t *);
36
37 typedef struct {
38 ruin_node_t *node;
39 char *display;
40 } ruin_node_display_t;
41
inline_box_type(enum ruin_layout_box_type t)42 int inline_box_type (enum ruin_layout_box_type t)
43 {
44 return t == RUIN_LAYOUT_BOX_TYPE_INLINE ||
45 t == RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE;
46 }
47
inline_display(char * display)48 int inline_display (char *display)
49 {
50 return strcmp (display, "inline") == 0;
51 }
52
row_group_box(char * display)53 static int row_group_box (char *display)
54 {
55 return strcmp (display, "table-row-group") == 0
56 || strcmp (display, "table-header-group") == 0
57 || strcmp (display, "table-footer-group") == 0;
58 }
59
proper_table_child(char * display)60 static int proper_table_child (char *display)
61 {
62 return strcmp (display, "table-caption") == 0
63 || strcmp (display, "table-column") == 0
64 || strcmp (display, "table-column-group") == 0
65 || strcmp (display, "table-row") == 0
66 || row_group_box (display);
67 }
68
display_to_box_type(char * disp)69 static enum ruin_layout_box_type display_to_box_type (char *disp)
70 {
71 if (strcmp (disp, "block") == 0)
72 return RUIN_LAYOUT_BOX_TYPE_BLOCK;
73 else if (strcmp (disp, "list-item") == 0)
74 return RUIN_LAYOUT_BOX_TYPE_BLOCK;
75 else if (strcmp (disp, "inline") == 0)
76 return RUIN_LAYOUT_BOX_TYPE_INLINE;
77 else if (strcmp (disp, "none") == 0)
78 return RUIN_LAYOUT_BOX_TYPE_NONE;
79 else if (strcmp (disp, "table") == 0
80 || strcmp (disp, "table-caption") == 0
81 || strcmp (disp, "table-cell") == 0
82 || strcmp (disp, "table-column") == 0
83 || strcmp (disp, "table-column-group") == 0
84 || strcmp (disp, "table-row") == 0
85 || row_group_box (disp))
86 return RUIN_LAYOUT_BOX_TYPE_BLOCK;
87
88 assert (1 == 0);
89 }
90
get_box_type(ruin_window_t * w,ruin_node_t * n)91 static enum ruin_layout_box_type get_box_type (ruin_window_t *w, ruin_node_t *n)
92 {
93 switch (n->type)
94 {
95 case RUIN_PARSE_NODE_TYPE_ELEMENT:
96 return display_to_box_type (ruin_css_lookup (w, n, "display"));
97 case RUIN_PARSE_NODE_TYPE_PROTOTYPE:
98 return RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK;
99 case RUIN_PARSE_NODE_TYPE_TEXT:
100 return RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE;
101 default: assert (1 == 0);
102 }
103 }
104
ruin_box_new(enum ruin_layout_box_type type,ruin_box_t * p,ruin_box_t * cb,ruin_window_t * w,ruin_node_t * t)105 ruin_box_t *ruin_box_new
106 (enum ruin_layout_box_type type, ruin_box_t *p, ruin_box_t *cb,
107 ruin_window_t *w, ruin_node_t *t)
108 {
109 ruin_box_t *box = NULL;
110 if (type == RUIN_LAYOUT_BOX_TYPE_INLINE ||
111 type == RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE)
112 box = calloc (1, sizeof (ruin_inline_box_t));
113 else if (type == RUIN_LAYOUT_BOX_TYPE_MARKER)
114 box = calloc (1, sizeof (ruin_marker_box_t));
115 else box = calloc (1, sizeof (ruin_box_t));
116
117 if (type == RUIN_LAYOUT_BOX_TYPE_LINE)
118 box->height.used = 1;
119
120 box->line_height.used = 1;
121
122 box->type = type;
123 box->containing_block = cb;
124 box->parent = p;
125 box->window = w;
126 box->generator = t;
127
128 box->visible = TRUE;
129
130 return box;
131 }
132
ruin_inline_box_new(enum ruin_layout_box_type type,ruin_box_t * p,ruin_box_t * cb,ruin_window_t * w,ruin_node_t * t,char * content_ptr)133 ruin_inline_box_t *ruin_inline_box_new
134 (enum ruin_layout_box_type type, ruin_box_t *p, ruin_box_t *cb,
135 ruin_window_t *w, ruin_node_t *t, char *content_ptr)
136 {
137 ruin_inline_box_t *box = (ruin_inline_box_t *)
138 ruin_box_new (type, p, cb, w, t);
139 box->content_ptr = content_ptr;
140 return box;
141 }
142
ruin_marker_box_new(enum ruin_layout_box_type type,ruin_box_t * p,ruin_box_t * cb,ruin_window_t * w,ruin_node_t * t,enum ruin_layout_list_style_type style,int ordinal,int length)143 ruin_marker_box_t *ruin_marker_box_new
144 (enum ruin_layout_box_type type, ruin_box_t *p, ruin_box_t *cb,
145 ruin_window_t *w, ruin_node_t *t, enum ruin_layout_list_style_type style,
146 int ordinal, int length)
147 {
148 ruin_marker_box_t *box = (ruin_marker_box_t *)
149 ruin_box_new (type, p, cb, w, t);
150
151 box->style = style;
152 box->ordinal = ordinal;
153 box->length = length;
154
155 return box;
156 }
157
ruin_box_clone(ruin_box_t * src,short deep)158 ruin_box_t *ruin_box_clone (ruin_box_t *src, short deep)
159 {
160 ruin_box_t *b = NULL;
161
162 assert (deep == FALSE); /* for now */
163
164 switch (src->type)
165 {
166 case RUIN_LAYOUT_BOX_TYPE_INLINE:
167 case RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE:
168 b = (ruin_box_t *) ruin_inline_box_new
169 (src->type, src->parent, src->containing_block, src->window,
170 src->generator, ((ruin_inline_box_t *) src)->content_ptr);
171 break;
172 case RUIN_LAYOUT_BOX_TYPE_MARKER:
173 b = (ruin_box_t *) ruin_marker_box_new
174 (src->type, src->parent, src->containing_block, src->window,
175 src->generator, ((ruin_marker_box_t *) src)->style,
176 ((ruin_marker_box_t *) src)->ordinal,
177 ((ruin_marker_box_t *) src)->length);
178 break;
179 default:
180 b = ruin_box_new
181 (src->type, src->parent, src->containing_block, src->window,
182 src->generator);
183 }
184
185 b->visible = src->visible;
186 b->level = src->level;
187
188 b->top = src->top;
189 b->left = src->left;
190
191 b->width = src->width;
192 b->height = src->height;
193
194 b->min_width = src->min_width;
195 b->max_width = src->max_width;
196
197 b->min_height = src->min_height;
198 b->max_height = src->max_height;
199
200 b->margin_top = src->margin_top;
201 b->margin_left = src->margin_left;
202 b->margin_bottom = src->margin_bottom;
203 b->margin_right = src->margin_right;
204
205 b->padding_top = src->padding_top;
206 b->padding_left = src->padding_left;
207 b->padding_bottom = src->padding_bottom;
208 b->padding_right = src->padding_right;
209
210 b->border_top_width = src->border_top_width;
211 b->border_left_width = src->border_left_width;
212 b->border_bottom_width = src->border_bottom_width;
213 b->border_right_width = src->border_right_width;
214
215 b->letter_spacing = src->letter_spacing;
216 b->word_spacing = src->word_spacing;
217 b->line_height = src->line_height;
218
219 b->text_indent = src->text_indent;
220
221 return b;
222 }
223
ruin_box_free(ruin_box_t * box)224 void ruin_box_free (ruin_box_t *box)
225 {
226 free (box);
227 }
228
generate_block(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)229 static ruin_box_t *generate_block
230 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
231 {
232 ruin_box_t *b = ruin_box_new (RUIN_LAYOUT_BOX_TYPE_BLOCK, p, cb, w, e);
233 if (e->first_child != NULL)
234 b->children = box_generate (w, e->first_child, b, b);
235 return b;
236 }
237
generate_table_caption(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)238 static ruin_box_t *generate_table_caption
239 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
240 {
241 ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
242 if (e->first_child != NULL)
243 b->children = box_generate (w, e->first_child, b, b);
244 return b;
245 }
246
generate_table_cell(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)247 static ruin_box_t *generate_table_cell
248 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
249 {
250 ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
251 if (e->first_child != NULL)
252 b->children = box_generate (w, e->first_child, b, b);
253 return b;
254 }
255
256 typedef ruin_box_t *(*generation_function)
257 (ruin_window_t *, ruin_node_t *, ruin_box_t *, ruin_box_t *);
258
259 typedef struct _generation_context
260 {
261 ruin_window_t *window;
262 ruin_box_t *parent;
263 ruin_box_t *containing_block;
264
265 generation_function generator;
266 } generation_context;
267
268 typedef struct _prototype_wrapper_generation_context
269 {
270 generation_context *subcontext;
271
272 int (*display_predicate)(char *);
273 GHashTable *prototype_properties;
274 GList *wrap_children;
275 } prototype_wrapper_generation_context;
276
generation_context_init(generation_context * context,ruin_window_t * window,ruin_box_t * parent,ruin_box_t * containing_block,generation_function generator)277 static void generation_context_init
278 (generation_context *context, ruin_window_t *window, ruin_box_t *parent,
279 ruin_box_t *containing_block, generation_function generator)
280 {
281 context->window = window;
282 context->parent = parent;
283 context->containing_block = containing_block;
284 context->generator = generator;
285 }
286
prototype_wrapper_generation_context_init(prototype_wrapper_generation_context * context,generation_context * subcontext,int (* display_predicate)(char *),GHashTable * prototype_properties,GList * wrap_children)287 static void prototype_wrapper_generation_context_init
288 (prototype_wrapper_generation_context *context, generation_context *subcontext,
289 int (*display_predicate)(char *), GHashTable *prototype_properties,
290 GList *wrap_children)
291 {
292 context->subcontext = subcontext;
293 context->display_predicate = display_predicate;
294 context->prototype_properties = prototype_properties;
295 context->wrap_children = wrap_children;
296 }
297
generate_child(ruin_node_t * e,generation_context * context)298 static ruin_box_t *generate_child
299 (ruin_node_t *e, generation_context *context)
300 {
301 return context->generator
302 (context->window, e, context->parent, context->containing_block);
303 }
304
is_table_cell(char * display)305 static int is_table_cell (char *display)
306 {
307 return strcmp (display, "table-cell") == 0;
308 }
309
is_table_column(char * display)310 static int is_table_column (char *display)
311 {
312 return strcmp (display, "table-column") == 0;
313 }
314
is_table_row(char * display)315 static int is_table_row (char *display)
316 {
317 return strcmp (display, "table-row") == 0;
318 }
319
generate_prototype_child(prototype_wrapper_generation_context * context)320 static void generate_prototype_child
321 (prototype_wrapper_generation_context *context)
322 {
323 ruin_box_t *b = context->subcontext->parent;
324 ruin_node_t *prototype = (ruin_node_t *) ruin_node_prototype_new
325 (context->prototype_properties);
326
327 prototype->children = context->wrap_children;
328 prototype->first_child = g_list_nth_data (prototype->children, 0);
329
330 b->children = g_list_append
331 (b->children, generate_child (prototype, context->subcontext));
332 context->wrap_children = NULL;
333 }
334
generate_wrapped_child(gpointer data,gpointer user_data)335 static void generate_wrapped_child (gpointer data, gpointer user_data)
336 {
337 ruin_node_t *c = (ruin_node_t *) data;
338
339 prototype_wrapper_generation_context *context =
340 (prototype_wrapper_generation_context *) user_data;
341
342 ruin_window_t *w = context->subcontext->window;
343 ruin_box_t *b = context->subcontext->parent;
344
345 char *display = ruin_css_lookup (w, c, "display");
346 if (context->display_predicate (display))
347 {
348 if (g_list_length (context->wrap_children) > 0)
349 generate_prototype_child (context);
350 b->children = g_list_append
351 (b->children, generate_child (c, context->subcontext));
352 }
353 else context->wrap_children = g_list_append (context->wrap_children, c);
354 if (c->next_sibling == NULL && g_list_length (context->wrap_children) > 0)
355 generate_prototype_child (context);
356 }
357
generate_table_row(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)358 static ruin_box_t *generate_table_row
359 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
360 {
361 ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
362
363 generation_context context;
364 prototype_wrapper_generation_context wrapping_context;
365
366 generation_context_init (&context, w, b, b, generate_table_cell);
367 prototype_wrapper_generation_context_init
368 (&wrapping_context, &context, is_table_cell, display_table_cell, NULL);
369
370 g_list_foreach (e->children, generate_wrapped_child, &wrapping_context);
371 return b;
372 }
373
generate_table_row_group(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)374 static ruin_box_t *generate_table_row_group
375 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
376 {
377 ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
378
379 generation_context context;
380 prototype_wrapper_generation_context wrapping_context;
381
382 generation_context_init (&context, w, b, b, generate_table_row);
383 prototype_wrapper_generation_context_init
384 (&wrapping_context, &context, is_table_row, display_table_row, NULL);
385
386 g_list_foreach (e->children, generate_wrapped_child, &wrapping_context);
387 return b;
388 }
389
generate_table_column(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)390 static ruin_box_t *generate_table_column
391 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
392 {
393 ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
394
395 generation_context context;
396 prototype_wrapper_generation_context wrapping_context;
397
398 generation_context_init (&context, w, b, b, generate_table_cell);
399 prototype_wrapper_generation_context_init
400 (&wrapping_context, &context, is_table_cell, display_table_cell, NULL);
401
402 g_list_foreach (e->children, generate_wrapped_child, &wrapping_context);
403 return b;
404 }
405
generate_table_column_group(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)406 static ruin_box_t *generate_table_column_group
407 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
408 {
409 ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
410
411 generation_context context;
412 prototype_wrapper_generation_context wrapping_context;
413
414 generation_context_init (&context, w, b, b, generate_table_column);
415 prototype_wrapper_generation_context_init
416 (&wrapping_context, &context, is_table_column, display_table_column, NULL);
417
418 g_list_foreach (e->children, generate_wrapped_child, &wrapping_context);
419 return b;
420 }
421
generate_table(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)422 static ruin_box_t *generate_table
423 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
424 {
425 ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
426 ruin_node_t *c = e->first_child;
427 while (c != NULL)
428 {
429 char *display = ruin_css_lookup (w, c, "display");
430 if (proper_table_child (display))
431 {
432 ruin_box_t *child_box = NULL;
433 if (row_group_box (display))
434 child_box = generate_table_row_group (w, c, b, b);
435 else if (strcmp (display, "table-caption") == 0)
436 child_box = generate_table_caption (w, c, b, b);
437 else if (strcmp (display, "table-row") == 0)
438 child_box = generate_table_row (w, c, b, b);
439 else if (strcmp (display, "table-column") == 0)
440 child_box = generate_table_column (w, c, b, b);
441 else if (strcmp (display, "table-column-group") == 0)
442 child_box = generate_table_column_group (w, c, b, b);
443
444 b->children = g_list_append (b->children, child_box);
445 c = c->next_sibling;
446 }
447 else
448 {
449 ruin_node_t *prototype_table_row = (ruin_node_t *)
450 ruin_node_prototype_new (display_table_row);
451 while (!proper_table_child (display))
452 {
453 if (prototype_table_row->first_child == NULL)
454 prototype_table_row->first_child = c;
455
456 prototype_table_row->children =
457 g_list_append (prototype_table_row->children, c);
458 c = c->next_sibling;
459 if (c == NULL)
460 break;
461 display = ruin_css_lookup (w, c, "display");
462 }
463
464 b->children = g_list_append
465 (b->children, generate_table_row (w, prototype_table_row, b, b));
466 }
467 }
468
469 return b;
470 }
471
parse_list_style_type(char * style)472 static enum ruin_layout_list_style_type parse_list_style_type (char *style)
473 {
474 if (strcmp (style, "disc") == 0)
475 return RUIN_LAYOUT_LIST_STYLE_TYPE_DISC;
476 else if (strcmp (style, "circle") == 0)
477 return RUIN_LAYOUT_LIST_STYLE_TYPE_CIRCLE;
478 else if (strcmp (style, "square") == 0)
479 return RUIN_LAYOUT_LIST_STYLE_TYPE_SQUARE;
480 else if (strcmp (style, "decimal") == 0)
481 return RUIN_LAYOUT_LIST_STYLE_TYPE_DECIMAL;
482 else if (strcmp (style, "decimal-leading-zero") == 0)
483 return RUIN_LAYOUT_LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO;
484 else if (strcmp (style, "lower-roman") == 0)
485 return RUIN_LAYOUT_LIST_STYLE_TYPE_LOWER_ROMAN;
486 else if (strcmp (style, "upper-roman") == 0)
487 return RUIN_LAYOUT_LIST_STYLE_TYPE_UPPER_ROMAN;
488 else if (strcmp (style, "lower-greek") == 0)
489 return RUIN_LAYOUT_LIST_STYLE_TYPE_LOWER_GREEK;
490 else if (strcmp (style, "lower-latin") == 0)
491 return RUIN_LAYOUT_LIST_STYLE_TYPE_LOWER_LATIN;
492 else if (strcmp (style, "upper-latin") == 0)
493 return RUIN_LAYOUT_LIST_STYLE_TYPE_UPPER_LATIN;
494 else if (strcmp (style, "armenian") == 0)
495 return RUIN_LAYOUT_LIST_STYLE_TYPE_ARMENIAN;
496 else if (strcmp (style, "georgian") == 0)
497 return RUIN_LAYOUT_LIST_STYLE_TYPE_GEORGIAN;
498 else if (strcmp (style, "lower-alpha") == 0)
499 return RUIN_LAYOUT_LIST_STYLE_TYPE_LOWER_ALPHA;
500 else if (strcmp (style, "upper-alpha") == 0)
501 return RUIN_LAYOUT_LIST_STYLE_TYPE_UPPER_ALPHA;
502 else if (strcmp (style, "none") == 0)
503 return RUIN_LAYOUT_LIST_STYLE_TYPE_NONE;
504 else assert (1 == 0);
505 }
506
generate_list_item(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb,int o,int l)507 static GList *generate_list_item
508 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb, int o, int l)
509 {
510 char *position = ruin_css_lookup (w, e, "list-style-position");
511 enum ruin_layout_list_style_type s = parse_list_style_type
512 (ruin_css_lookup (w, e, "list-style-type"));
513 ruin_box_t *principal_box = ruin_box_new
514 (RUIN_LAYOUT_BOX_TYPE_BLOCK, p, cb, w, e);
515
516 if (strcmp (position, "inside") == 0)
517 {
518 ruin_box_t *line_box = ruin_box_new
519 (RUIN_LAYOUT_BOX_TYPE_LINE, principal_box, principal_box, w, e);
520 ruin_box_t *marker_box = (ruin_box_t *) ruin_marker_box_new
521 (RUIN_LAYOUT_BOX_TYPE_MARKER, line_box, principal_box, w, e, s, o, l);
522
523 line_box->children = g_list_append (NULL, marker_box);
524 line_box->children = g_list_concat
525 (line_box->children, box_generate
526 (w, e->first_child, principal_box, principal_box));
527
528 principal_box->children = g_list_append (NULL, line_box);
529
530 return g_list_append (NULL, principal_box);
531 }
532 else if (strcmp (position, "outside") == 0)
533 {
534 ruin_box_t *marker_box = (ruin_box_t *)
535 ruin_marker_box_new (RUIN_LAYOUT_BOX_TYPE_MARKER, p, cb, w, e, s, o, l);
536 principal_box->children = box_generate
537 (w, e->first_child, principal_box, principal_box);
538
539 return g_list_append (g_list_append (NULL, marker_box), principal_box);
540 }
541 assert (1 == 0);
542 }
543
box_clone(ruin_box_t * b)544 static ruin_box_t *box_clone (ruin_box_t *b)
545 {
546 if (inline_box_type (b->type))
547 return (ruin_box_t *) ruin_inline_box_new
548 (b->type, b->parent, b->containing_block, b->window, b->generator,
549 ((ruin_inline_box_t *) b)->content_ptr);
550 return ruin_box_new
551 (b->type, b->parent, b->containing_block, b->window, b->generator);
552 }
553
generate_non_inline(ruin_window_t * w,ruin_node_t ** e,ruin_box_t * p,ruin_box_t * cb)554 static GList *generate_non_inline
555 (ruin_window_t *w, ruin_node_t **e, ruin_box_t *p, ruin_box_t *cb)
556 {
557 char *display = ruin_css_lookup (w, *e, "display");
558 GList *ret = NULL;
559
560 if (strcmp (display, "block") == 0)
561 ret = g_list_append (NULL, generate_block (w, *e, p, cb));
562 else if (strcmp (display, "list-item") == 0)
563 {
564 int len = 0, ord = 0;
565 ruin_node_t *e_ptr = *e;
566 do
567 {
568 len++;
569
570 *e = (*e)->next_sibling;
571 if (*e == NULL)
572 break;
573
574 display = ruin_css_lookup (w, *e, "display");
575 }
576 while (strcmp (display, "list-item") == 0);
577
578 *e = e_ptr;
579 for (ord = 0; ord < len; ord++)
580 {
581 ret = g_list_concat
582 (ret, generate_list_item (w, *e, p, cb, ord, len));
583 *e = (*e)->next_sibling;
584 }
585
586 return ret;
587 }
588 else if (strcmp (display, "table") == 0)
589 ret = g_list_append (NULL, generate_table (w, *e, p, cb));
590 else if (proper_table_child (display))
591 {
592 ruin_node_t *prototype_table = (ruin_node_t *)
593 ruin_node_prototype_new (display_table);
594 do
595 {
596 if (prototype_table->first_child == NULL)
597 prototype_table->first_child = *e;
598 prototype_table->children = g_list_append
599 (prototype_table->children, *e);
600
601 *e = (*e)->next_sibling;
602 if (*e == NULL)
603 break;
604
605 display = ruin_css_lookup (w, *e, "display");
606 }
607 while (proper_table_child (display));
608 return g_list_append (NULL, generate_table (w, prototype_table, p, cb));
609 }
610 else if (strcmp (display, "none") == 0);
611 else assert (1 == 0);
612
613 *e = (*e)->next_sibling;
614 return ret;
615 }
616
generate_inline(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)617 static GList *generate_inline
618 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
619 {
620 enum ruin_layout_box_type box_type = get_box_type (w, e);
621 char *content = NULL;
622
623 if (box_type != RUIN_LAYOUT_BOX_TYPE_INLINE &&
624 box_type != RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE)
625 return generate_non_inline (w, &e, p, cb);
626 if (e->type == RUIN_PARSE_NODE_TYPE_TEXT)
627 content = ((ruin_node_text_t *) e)->content;
628
629 if (content != NULL)
630 {
631 ruin_inline_box_t *b = ruin_inline_box_new
632 (RUIN_LAYOUT_BOX_TYPE_INLINE, p, cb, w, e, content);
633 return g_list_append (NULL, b);
634 }
635 else
636 {
637 ruin_inline_box_t *b = ruin_inline_box_new
638 (RUIN_LAYOUT_BOX_TYPE_INLINE, p, cb, w, e, NULL);
639 GList *ret = g_list_append (NULL, b);
640 GList *child_ptr = e->children;
641 int inline_context_latch = 0;
642
643 while (child_ptr != NULL)
644 {
645 GList *boxes = generate_inline
646 (w, (ruin_node_t *) child_ptr->data, (ruin_box_t *) b, cb);
647 GList *box_ptr = boxes;
648
649 while (box_ptr != NULL)
650 {
651 ruin_box_t *box = box_ptr->data;
652 if (!inline_box_type (box->type))
653 {
654 inline_context_latch = 1;
655 ret = g_list_append (ret, box);
656 }
657 else
658 {
659 if (inline_context_latch)
660 {
661 inline_context_latch = 0;
662 b = (ruin_inline_box_t *) box_clone ((ruin_box_t *) b);
663 ret = g_list_append (ret, b);
664 }
665 box->parent = (ruin_box_t *) b;
666 ((ruin_box_t *) b)->children =
667 g_list_append (((ruin_box_t *) b)->children, box);
668 }
669 box_ptr = box_ptr->next;
670 }
671
672 child_ptr = child_ptr->next;
673 }
674
675 return ret;
676 }
677 }
678
generate_lines(ruin_window_t * w,ruin_node_t ** e,ruin_box_t * p,ruin_box_t * cb,int non_inline_siblings)679 static GList *generate_lines
680 (ruin_window_t *w, ruin_node_t **e, ruin_box_t *p,
681 ruin_box_t *cb, int non_inline_siblings)
682 {
683 GList *ret = NULL;
684 ruin_box_t *current_line = NULL;
685
686 while (*e != NULL)
687 {
688 GList *children = NULL;
689 GList *child_ptr = NULL;
690 enum ruin_layout_box_type box_type = get_box_type (w, *e);
691
692 if (box_type != RUIN_LAYOUT_BOX_TYPE_INLINE &&
693 box_type != RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE)
694 {
695 if (g_list_length (ret) == 1 && !non_inline_siblings)
696 {
697 ruin_box_t *b = ruin_box_new
698 (RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK, p, cb, w, NULL);
699 ruin_box_t *l = ret->data;
700
701 l->parent = b;
702 l->containing_block = b;
703 b->children = g_list_append (b->children, l);
704
705 return g_list_append (NULL, b);
706 }
707 break;
708 }
709
710 children = generate_inline (w, *e, p, cb);
711 child_ptr = children;
712 while (child_ptr != NULL)
713 {
714 ruin_box_t *child = (ruin_box_t *) child_ptr->data;
715 if (!inline_box_type (child->type))
716 {
717 current_line = NULL;
718 if (g_list_length (ret) == 1 && !non_inline_siblings)
719 {
720 ruin_box_t *b = ruin_box_new
721 (RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK, p, cb, w, NULL);
722 ruin_box_t *l = ret->data;
723
724 l->parent = b;
725 l->containing_block = b;
726 b->children = g_list_append (b->children, l);
727
728 ret = g_list_append (NULL, b);
729 non_inline_siblings = TRUE;
730 }
731
732 ret = g_list_append (ret, child);
733 }
734 else
735 {
736 if (current_line == NULL)
737 {
738 if (non_inline_siblings)
739 {
740 ruin_box_t *b = ruin_box_new
741 (RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK, p, cb, w, NULL);
742 current_line = ruin_box_new
743 (RUIN_LAYOUT_BOX_TYPE_LINE, b, b, w, *e);
744 b->children = g_list_append (b->children, current_line);
745 ret = g_list_append (ret, b);
746 }
747 else
748 {
749 current_line = ruin_box_new
750 (RUIN_LAYOUT_BOX_TYPE_LINE, p, cb, w, *e);
751 ret = g_list_append (ret, current_line);
752 }
753 }
754 child->parent = current_line;
755 current_line->children = g_list_append
756 (current_line->children, child);
757 }
758 child_ptr = child_ptr->next;
759 }
760
761 *e = (*e)->next_sibling;
762 }
763
764 return ret;
765 }
766
box_generate(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)767 static GList *box_generate
768 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
769 {
770 GList *ret = NULL;
771
772 while (e != NULL)
773 {
774 enum ruin_layout_box_type box_type = get_box_type (w, e);
775 if (box_type == RUIN_LAYOUT_BOX_TYPE_INLINE ||
776 box_type == RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE)
777 {
778 ret = g_list_concat
779 (ret, generate_lines (w, &e, p, cb, g_list_length (ret) > 0));
780 continue;
781 }
782 else ret = g_list_concat (ret, generate_non_inline (w, &e, p, cb));
783 }
784
785 return ret;
786 }
787
ruin_box_generate(ruin_window_t * w,ruin_node_t * e)788 GList *ruin_box_generate (ruin_window_t *w, ruin_node_t *e)
789 {
790 ruin_box_t *viewport_box = ruin_box_new
791 (RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK, NULL, NULL, w, NULL);
792 assert (e->next_sibling == NULL);
793 return box_generate (w, e, NULL, viewport_box);
794 }
795
ruin_box_css_lookup(ruin_box_t * box,char * prop)796 char *ruin_box_css_lookup (ruin_box_t *box, char *prop)
797 {
798 if (box->type == RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK
799 || box->type == RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE
800 || box->type == RUIN_LAYOUT_BOX_TYPE_LINE)
801 {
802 if (box->parent == NULL)
803 return ruin_css_initial_value (box->window, prop);
804 else if (ruin_css_is_inherited (box->window, prop))
805 return ruin_box_css_lookup (box->parent, prop);
806 else return ruin_css_initial_value (box->window, prop);
807 }
808 else if (box->type == RUIN_LAYOUT_BOX_TYPE_BLOCK
809 || box->type == RUIN_LAYOUT_BOX_TYPE_INLINE
810 || box->type == RUIN_LAYOUT_BOX_TYPE_MARKER)
811 {
812 assert (box->generator != NULL);
813 return ruin_css_lookup (box->window, box->generator, prop);
814 }
815 else assert (1 == 0);
816 return NULL;
817 }
818
ruin_box_normalize_width(ruin_length_t * l,ruin_box_t * containing_block,int round_to_zero)819 void ruin_box_normalize_width
820 (ruin_length_t *l, ruin_box_t *containing_block, int round_to_zero)
821 {
822 ruin_css_normalize_length
823 (l, &containing_block->width, containing_block->window->font_width,
824 containing_block->window->dpi, round_to_zero);
825 }
826
ruin_box_normalize_height(ruin_length_t * l,ruin_box_t * containing_block,int round_to_zero)827 void ruin_box_normalize_height
828 (ruin_length_t *l, ruin_box_t *containing_block, int round_to_zero)
829 {
830 ruin_css_normalize_length
831 (l, &containing_block->height, containing_block->window->font_height,
832 containing_block->window->dpi, round_to_zero);
833 }
834
_ruin_box_init()835 void _ruin_box_init ()
836 {
837 display_table = g_hash_table_new (g_str_hash, g_str_equal);
838 display_table_cell = g_hash_table_new (g_str_hash, g_str_equal);
839 display_table_column = g_hash_table_new (g_str_hash, g_str_equal);
840 display_table_row = g_hash_table_new (g_str_hash, g_str_equal);
841
842 g_hash_table_insert (display_table, "display", "table");
843 g_hash_table_insert (display_table_cell, "display", "table-cell");
844 g_hash_table_insert (display_table_column, "display", "table-column");
845 g_hash_table_insert (display_table_row, "display", "table-row");
846 }
847