maug
Quick and dirty C mini-augmentation library.
Loading...
Searching...
No Matches
retrohtr.h
Go to the documentation of this file.
1
2#ifndef RETROHTR_H
3#define RETROHTR_H
4
11#define RETROHTR_TREE_FLAG_GUI_ACTIVE 1
12
13#define RETROHTR_NODE_FLAG_DIRTY 2
14
15#ifndef RETROHTR_RENDER_NODES_INIT_SZ
16# define RETROHTR_RENDER_NODES_INIT_SZ 10
17#endif /* !RETROHTR_RENDER_NODES_INIT_SZ */
18
19#ifndef RETROHTR_TRACE_LVL
20# define RETROHTR_TRACE_LVL 0
21#endif /* !RETROHTR_TRACE_LVL */
22
23#define RETROHTR_EDGE_UNKNOWN 0
24#define RETROHTR_EDGE_LEFT 1
25#define RETROHTR_EDGE_TOP 2
26#define RETROHTR_EDGE_INSIDE 4
27
29 uint8_t flags;
30 /* TODO: Maybe get rid of these and replace them with MCSS_STYLE node? */
31 ssize_t x;
32 ssize_t y;
33 size_t w;
34 size_t h;
35 size_t m_l;
36 size_t m_r;
37 size_t m_t;
38 size_t m_b;
39 uint8_t pos;
40 uint8_t pos_flags;
41 uint8_t edge;
44#ifdef RETROGXC_PRESENT
45 ssize_t font_idx;
46#else
47 MAUG_MHANDLE font_h;
48#endif /* RETROGXC_PRESENT */
49 ssize_t tag;
51 ssize_t parent;
53 ssize_t first_child;
55 ssize_t next_sibling;
56 struct RETROFLAT_BITMAP bitmap;
57};
58
60 uint8_t flags;
61 MAUG_MHANDLE nodes_h;
65 size_t nodes_sz;
68 struct RETROGUI gui;
69};
70
71/* TODO: Function names should be verb_noun! */
72
73#define retrohtr_node( tree, idx ) \
74 (0 <= (ssize_t)idx ? &((tree)->nodes[idx]) : NULL)
75
76#define retrohtr_node_parent( tree, idx ) \
77 (0 <= idx && 0 <= (tree)->nodes[idx].parent ? \
78 &((tree)->nodes[(tree)->nodes[idx].parent]) : NULL)
79
80#define retrohtr_tree_lock( tree ) \
81 if( NULL == (tree)->nodes ) { \
82 maug_mlock( (tree)->nodes_h, (tree)->nodes ); \
83 maug_cleanup_if_null_alloc( struct RETROHTR_RENDER_NODE*, (tree)->nodes ); \
84 }
85
86#define retrohtr_tree_unlock( tree ) \
87 if( NULL != (tree)->nodes ) { \
88 maug_munlock( (tree)->nodes_h, (tree)->nodes ); \
89 }
90
91#define retrohtr_tree_is_locked( tree ) (NULL != (tree)->nodes)
92
93/* TODO: Make these offset by element scroll on screen. */
94
95#define retrohtr_node_screen_x( tree, node_idx ) \
96 ((tree)->nodes[node_idx].x)
97
98#define retrohtr_node_screen_y( tree, node_idx ) \
99 ((tree)->nodes[node_idx].y)
100
101MERROR_RETVAL retrohtr_tree_create(
102 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
103 size_t x, size_t y, size_t w, size_t h,
104 ssize_t tag_idx, ssize_t node_idx, size_t d );
105
118 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
119 struct MCSS_STYLE* parent_style, struct MCSS_STYLE* effect_style,
120 ssize_t tag_idx );
121
122MERROR_RETVAL retrohtr_tree_size(
123 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
124 struct MCSS_STYLE* prev_sibling_style,
125 struct MCSS_STYLE* parent_style, ssize_t node_idx, size_t d );
126
127MERROR_RETVAL retrohtr_tree_pos(
128 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
129 struct MCSS_STYLE* prev_sibling_style,
130 struct MCSS_STYLE* parent_style, ssize_t node_idx, size_t d );
131
132MERROR_RETVAL retrohtr_tree_draw(
133 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
134 ssize_t node_idx, size_t d );
135
136retrogui_idc_t retrohtr_tree_poll_ctls(
137 struct RETROHTR_RENDER_TREE* tree,
138 RETROFLAT_IN_KEY* input,
139 struct RETROFLAT_INPUT* input_evt );
140
141MERROR_RETVAL retrohtr_tree_dump(
142 struct RETROHTR_RENDER_TREE* tree, struct MHTML_PARSER* parser,
143 ssize_t iter, size_t d );
144
145void retrohtr_tree_free( struct RETROHTR_RENDER_TREE* tree );
146
147MERROR_RETVAL retrohtr_tree_init( struct RETROHTR_RENDER_TREE* tree );
148
149#ifdef RETROHTR_C
150
151ssize_t retrohtr_get_next_free_node( struct RETROHTR_RENDER_TREE* tree ) {
152 uint8_t auto_unlocked = 0;
153 ssize_t retidx = -1;
154 MAUG_MHANDLE new_nodes_h = (MAUG_MHANDLE)NULL;
155
156 if( NULL != tree->nodes ) {
157 debug_printf( RETROHTR_TRACE_LVL, "auto-unlocking nodes..." );
158 maug_munlock( tree->nodes_h, tree->nodes );
159 auto_unlocked = 1;
160 }
161
162 assert( 0 < tree->nodes_sz_max );
163 assert( NULL == tree->nodes );
164 assert( (MAUG_MHANDLE)NULL != tree->nodes_h );
165 if( tree->nodes_sz_max <= tree->nodes_sz + 1 ) {
166 /* We've run out of nodes, so double the available number. */
167 /* TODO: Check for sz overflow. */
168 new_nodes_h = maug_mrealloc( tree->nodes_h, tree->nodes_sz_max * 2,
169 sizeof( struct RETROHTR_RENDER_NODE ) );
170 if( (MAUG_MHANDLE)NULL == new_nodes_h ) {
171 error_printf(
172 "unable to reallocate " SIZE_T_FMT " nodes!",
173 tree->nodes_sz_max * 2 );
174 goto cleanup;
175 }
176 tree->nodes_h = new_nodes_h;
177 tree->nodes_sz_max *= 2;
178 }
179
180 /* Assume handle is unlocked. */
181 assert( NULL == tree->nodes );
182 maug_mlock( tree->nodes_h, tree->nodes );
183 if( NULL == tree->nodes ) {
184 error_printf( "unable to lock nodes!" );
185 goto cleanup;
186 }
187
188 /* Zero out the last node, add it to the list, and return its index. */
189 debug_printf( RETROHTR_TRACE_LVL,
190 "zeroing node " SIZE_T_FMT " (of " SIZE_T_FMT ")...",
191 tree->nodes_sz, tree->nodes_sz_max );
192 maug_mzero( &(tree->nodes[tree->nodes_sz]),
193 sizeof( struct RETROHTR_RENDER_NODE ) );
194 retidx = tree->nodes_sz;
195 tree->nodes_sz++;
196
197 /* Compensate for cleanup below. */
198 maug_munlock( tree->nodes_h, tree->nodes );
199
200cleanup:
201
202 if( auto_unlocked ) {
203 debug_printf( RETROHTR_TRACE_LVL, "auto-locking nodes..." );
204 maug_mlock( tree->nodes_h, tree->nodes );
205 }
206
207 return retidx;
208}
209
210ssize_t retrohtr_add_node_child(
211 struct RETROHTR_RENDER_TREE* tree, ssize_t node_parent_idx
212) {
213 ssize_t node_new_idx = -1,
214 node_sibling_idx = -1;
215
216 node_new_idx = retrohtr_get_next_free_node( tree );
217 if( 0 > node_new_idx ) {
218 goto cleanup;
219 }
220
221#ifdef RETROGXC_PRESENT
222 retrohtr_node( tree, node_new_idx )->font_idx = -1;
223#endif /* RETROGXC_PRESENT */
224 retrohtr_node( tree, node_new_idx )->parent = node_parent_idx;
225 retrohtr_node( tree, node_new_idx )->first_child = -1;
226 retrohtr_node( tree, node_new_idx )->next_sibling = -1;
227
228 if( 0 > node_parent_idx ) {
229 debug_printf(
230 1, "adding root node under " SSIZE_T_FMT "...", node_parent_idx );
231 goto cleanup;
232 } else {
233 debug_printf(
234 1, "adding node " SSIZE_T_FMT " under " SSIZE_T_FMT,
235 node_new_idx, node_parent_idx );
236 }
237
238 /* Add new child under current node. */
239 if( 0 > retrohtr_node( tree, node_parent_idx )->first_child ) {
240 debug_printf( RETROHTR_TRACE_LVL, "adding first child..." );
241 assert( -1 == retrohtr_node( tree, node_parent_idx )->first_child );
242 retrohtr_node( tree, node_parent_idx )->first_child = node_new_idx;
243 } else {
244 assert( NULL != retrohtr_node( tree, node_parent_idx ) );
245 node_sibling_idx = retrohtr_node( tree, node_parent_idx )->first_child;
246 assert( NULL != retrohtr_node( tree, node_sibling_idx ) );
247 while( 0 <= retrohtr_node( tree, node_sibling_idx )->next_sibling ) {
248 node_sibling_idx =
249 retrohtr_node( tree, node_sibling_idx )->next_sibling;
250 }
251 retrohtr_node( tree, node_sibling_idx )->next_sibling = node_new_idx;
252 }
253
254cleanup:
255
256 return node_new_idx;
257}
258
259MERROR_RETVAL retrohtr_tree_create(
260 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
261 size_t x, size_t y, size_t w, size_t h,
262 ssize_t tag_idx, ssize_t node_idx, size_t d
263) {
264 ssize_t node_new_idx = -1;
265 ssize_t tag_iter_idx = -1;
266 MERROR_RETVAL retval = MERROR_OK;
267 union MHTML_TAG* p_tag_iter = NULL;
268 ssize_t tag_next_idx = 0;
269
270 debug_printf( RETROHTR_TRACE_LVL,
271 "creating render node for tag: " SSIZE_T_FMT, tag_idx );
272
273 mdata_vector_lock( &(parser->tags) );
274
275 if( 0 > tag_idx ) {
276 goto cleanup;
277 }
278 p_tag_iter = mdata_vector_get( &(parser->tags), tag_idx, union MHTML_TAG );
279 if( NULL == p_tag_iter ) {
280 goto cleanup;
281 }
282
283 /* Make sure we have a single root node. */
284 if( 0 > node_idx ) {
285 assert( MHTML_TAG_TYPE_BODY == p_tag_iter->base.type );
286
287 node_new_idx = retrohtr_add_node_child( tree, node_idx );
288 if( 0 > node_new_idx ) {
289 goto cleanup;
290 }
291 debug_printf( RETROHTR_TRACE_LVL,
292 "created initial root node: " SIZE_T_FMT, node_new_idx );
293
294 node_idx = node_new_idx;
295
296 /* The common root is the body tag. */
297 retrohtr_node( tree, node_idx )->tag = tag_idx;
298
299 retrohtr_node( tree, node_idx )->x = x;
300 retrohtr_node( tree, node_idx )->y = y;
301 retrohtr_node( tree, node_idx )->w = w;
302 retrohtr_node( tree, node_idx )->h = h;
303 }
304
305 tag_iter_idx = p_tag_iter->base.first_child;
306 while( 0 <= tag_iter_idx ) {
307 node_new_idx = retrohtr_add_node_child( tree, node_idx );
308 p_tag_iter = mdata_vector_get(
309 &(parser->tags), tag_iter_idx, union MHTML_TAG );
310 assert( NULL != p_tag_iter );
311 if( 0 > node_new_idx ) {
312 goto cleanup;
313 }
314
315 retrohtr_node( tree, node_new_idx )->tag = tag_iter_idx;
316
317 debug_printf( RETROHTR_TRACE_LVL,
318 "rendering node " SSIZE_T_FMT " (%s) under node " SSIZE_T_FMT,
319 node_new_idx,
320 gc_mhtml_tag_names[p_tag_iter->base.type],
321 node_idx );
322
323 /* Tag-specific rendering preparations. */
324 if( MHTML_TAG_TYPE_IMG == p_tag_iter->base.type ) {
325 /* Load the image for rendering later. */
326 retval = retroflat_load_bitmap(
327 p_tag_iter->IMG.src,
328 &(retrohtr_node( tree, node_new_idx )->bitmap),
330 if( MERROR_OK == retval ) {
331 debug_printf( RETROHTR_TRACE_LVL, "loaded img: %s",
332 p_tag_iter->IMG.src );
333 } else {
334 error_printf( "could not load img: %s", p_tag_iter->IMG.src );
335 }
336 }
337
338 tag_next_idx = p_tag_iter->base.next_sibling;
339
340 mdata_vector_unlock( &(parser->tags) );
341
342 retval = retrohtr_tree_create( parser, tree, x, y, w, h,
343 tag_iter_idx, node_new_idx, d + 1 );
344 maug_cleanup_if_not_ok();
345
346 mdata_vector_lock( &(parser->tags) );
347
348 tag_iter_idx = tag_next_idx;
349 }
350
351cleanup:
352
353 if( mdata_vector_is_locked( &(parser->tags) ) ) {
354 mdata_vector_unlock( &(parser->tags) );
355 }
356
357 return retval;
358}
359
361 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
362 struct MCSS_STYLE* parent_style, struct MCSS_STYLE* effect_style,
363 ssize_t tag_idx
364) {
365 MERROR_RETVAL retval = MERROR_OK;
366 ssize_t tag_style_idx = -1;
367 size_t tag_type = 0,
368 i = 0;
369 struct MCSS_STYLE* style = NULL;
370 union MHTML_TAG* p_tag_iter = NULL;
371
372 debug_printf( RETROHTR_TRACE_LVL,
373 "applying styles for tag: " SSIZE_T_FMT, tag_idx );
374
375 assert( !mdata_vector_is_locked( &(parser->tags) ) );
376 mdata_vector_lock( &(parser->styler.styles) );
377 mdata_vector_lock( &(parser->tags) );
378
379 maug_mzero( effect_style, sizeof( struct MCSS_STYLE ) );
380
381 if( 0 >= tag_idx ) {
382 goto cleanup;
383 }
384 p_tag_iter = mdata_vector_get( &(parser->tags), tag_idx, union MHTML_TAG );
385 if( NULL == p_tag_iter ) {
386 goto cleanup;
387 }
388
389 tag_type = p_tag_iter->base.type;
390
391 /* Merge style based on HTML element class. */
392 if( 0 < p_tag_iter->base.classes_sz ) {
393 for( i = 0 ; mdata_vector_ct( &(parser->styler.styles) ) > i ; i++ ) {
394 style = mdata_vector_get(
395 &(parser->styler.styles), i, struct MCSS_STYLE );
396
397 if(
398 NULL != style &&
399 0 == strncmp(
400 p_tag_iter->base.classes,
401 style->class,
402 p_tag_iter->base.classes_sz
403 )
404 ) {
405 debug_printf( RETROHTR_TRACE_LVL, "found style for tag class: %s",
406 style->class );
407
408 mcssmerge_styles( effect_style, parent_style, style, tag_type );
409 }
410 }
411 }
412
413 /* Merge style based on HTML element ID. */
414 if( 0 < p_tag_iter->base.id_sz ) {
415 for( i = 0 ; mdata_vector_ct( &(parser->styler.styles) ) > i ; i++ ) {
416 style = mdata_vector_get(
417 &(parser->styler.styles), i, struct MCSS_STYLE );
418
419 if(
420 NULL != style &&
421 0 == strncmp(
422 p_tag_iter->base.id,
423 style->id,
424 p_tag_iter->base.id_sz
425 )
426 ) {
427 debug_printf( RETROHTR_TRACE_LVL, "found style for tag ID: %s",
428 style->id );
429
430 mcssmerge_styles( effect_style, parent_style, style, tag_type );
431 }
432 }
433 }
434
435 /* Grab element-specific style last. */
436 tag_style_idx = p_tag_iter->base.style;
437
438cleanup:
439
440 /* TODO: Separate this out of cleanup phase. */
441
442 /* This might be NULL! */
443 style = mdata_vector_get(
444 &(parser->styler.styles), tag_style_idx, struct MCSS_STYLE );
445
446 /* Make sure we have a root style. */
447 mcssmerge_styles( effect_style, parent_style, style, tag_type );
448
449 mdata_vector_unlock( &(parser->tags) );
450 mdata_vector_unlock( &(parser->styler.styles) );
451
452 return retval;
453}
454
455static MERROR_RETVAL retrohtr_load_font(
456 struct MCSS_PARSER* styler,
457#ifdef RETROGXC_PRESENT
458 ssize_t* font_idx_p,
459#else
460 MAUG_MHANDLE* font_h_p,
461#endif /* RETROGXC_PRESENT */
462 struct MCSS_STYLE* effect_style
463) {
464 MERROR_RETVAL retval = MERROR_OK;
465 char* strpool = NULL;
466
467#ifdef RETROGXC_PRESENT
468 if( 0 <= *font_idx_p ) {
469 error_printf( "tried to load font but font already loaded, idx: "
470 SSIZE_T_FMT, *font_idx_p );
471#else
472 if( (MAUG_MHANDLE)NULL != *font_h_p ) {
473 error_printf( "tried to load font but font already loaded, p: %p",
474 *font_h_p );
475#endif /* RETROGXC_PRESENT */
476 goto cleanup;
477 }
478
479 mdata_strpool_lock( &(styler->strpool), strpool );
480
481 debug_printf( RETROHTR_TRACE_LVL,
482 "loading font: %s (" SSIZE_T_FMT ")",
483 &(strpool[effect_style->FONT_FAMILY]), effect_style->FONT_FAMILY );
484
485 if( 0 >= effect_style->FONT_FAMILY ) {
486 error_printf( "style has no font associated!" );
487 /* TODO: Load fallback font? */
488 retval = MERROR_GUI;
489 goto cleanup;
490 }
491
492 /* Load the font into the cache. */
493#ifdef RETROGXC_PRESENT
494 *font_idx_p =
495 retrogxc_load_font( &(strpool[effect_style->FONT_FAMILY]), 0, 33, 93 );
496#else
497 retval = retrofont_load(
498 &(strpool[effect_style->FONT_FAMILY]), font_h_p, 0, 33, 93 );
499#endif /* RETROGXC_PRESENT */
500
501cleanup:
502
503 mdata_strpool_unlock( &(styler->strpool), strpool );
504
505 return retval;
506}
507
508MERROR_RETVAL retrohtr_tree_gui(
509 struct RETROHTR_RENDER_TREE* tree, struct MCSS_PARSER* styler,
510 struct MCSS_STYLE* effect_style
511) {
512 MERROR_RETVAL retval = MERROR_OK;
513
514 /* Create a GUI handler just for this tree. */
515 if(
516 RETROHTR_TREE_FLAG_GUI_ACTIVE ==
517 (RETROHTR_TREE_FLAG_GUI_ACTIVE & tree->flags)
518 ) {
519 debug_printf( RETROHTR_TRACE_LVL, "tree GUI already active!" );
520 goto cleanup;
521 }
522
523 /* This means all GUI items will use the font from the first node
524 * loaded with a GUI item!
525 */
526 retval = retrogui_init( &(tree->gui) );
527 maug_cleanup_if_not_ok();
528
529 retval = retrohtr_load_font(
530 styler,
531#ifdef RETROGXC_PRESENT
532 &(tree->gui.font_idx),
533#else
534 &(tree->gui.font_h),
535#endif /* RETROGXC_PRESENT */
536 effect_style );
537 maug_cleanup_if_not_ok();
538
539 tree->flags |= RETROHTR_TREE_FLAG_GUI_ACTIVE;
540
541 debug_printf( RETROHTR_TRACE_LVL, "tree GUI initialized!" );
542
543cleanup:
544 return retval;
545}
546
547MERROR_RETVAL retrohtr_tree_size(
548 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
549 struct MCSS_STYLE* prev_sibling_style,
550 struct MCSS_STYLE* parent_style, ssize_t node_idx, size_t d
551) {
552 struct MCSS_STYLE effect_style;
553 struct MCSS_STYLE child_prev_sibling_style;
554 struct MCSS_STYLE child_style;
555 char* strpool = NULL;
556 ssize_t child_iter_idx = -1;
557 ssize_t tag_idx = -1;
558 ssize_t node_iter_idx = -1;
559 size_t this_line_w = 0;
560 size_t this_line_h = 0;
561 MERROR_RETVAL retval = MERROR_OK;
562 union RETROGUI_CTL ctl;
563 union MHTML_TAG* p_tag_iter = NULL;
564 union MHTML_TAG* p_tag_node = NULL;
565
566 if( NULL == retrohtr_node( tree, node_idx ) ) {
567 goto cleanup;
568 }
569
570 tag_idx = retrohtr_node( tree, node_idx )->tag;
571
572 retval = retrohtr_apply_styles(
573 parser, tree, parent_style, &effect_style, tag_idx );
574 maug_cleanup_if_not_ok();
575
576 assert( !mdata_vector_is_locked( &(parser->tags) ) );
577 mdata_vector_lock( &(parser->tags) );
578
579 p_tag_iter = mdata_vector_get( &(parser->tags), tag_idx, union MHTML_TAG );
580 assert( NULL != p_tag_iter );
581
582 /* position */
583
584 if( mcss_prop_is_active( effect_style.POSITION ) ) {
585 debug_printf( RETROHTR_TRACE_LVL,
586 "node " SSIZE_T_FMT ": applying %s positioning",
587 node_idx, gc_mcss_position_names[effect_style.POSITION] );
588 /* TODO: MCSS_POS_NOTE: We'd like to get rid of this so all positioning
589 * is done through CSS... unfortunately, we only track the current
590 * and previous effective styles while working that out later, so
591 * we need to pin this to the element directly so we can rule it
592 * out of the box model e.g. when determining x/y coords of its
593 * neighbors.
594 */
595 retrohtr_node( tree, node_idx )->pos = effect_style.POSITION;
596 retrohtr_node( tree, node_idx )->pos_flags = effect_style.POSITION_flags;
597 }
598
599 /* Grab fixed dimensions before content-based calculations of children, so
600 * we know if there are constraints. If these aren't set, then we'll size
601 * based on childrens' sizes after we determine childrens' sizes below.
602 */
603
604 if( mcss_prop_is_active_NOT_flag( effect_style.WIDTH, AUTO ) ) {
605 retrohtr_node( tree, node_idx )->w = effect_style.WIDTH;
606 }
607
608 if( mcss_prop_is_active_NOT_flag( effect_style.HEIGHT, AUTO ) ) {
609 retrohtr_node( tree, node_idx )->h = effect_style.HEIGHT;
610 }
611
612 /* Figure out how big the contents of this node are. */
613
614 /* Font is heritable, so load it for all nodes even if we don't use it. */
615 retval = retrohtr_load_font(
616 &(parser->styler),
617#ifdef RETROGXC_PRESENT
618 &(retrohtr_node( tree, node_idx )->font_idx),
619#else
620 &(retrohtr_node( tree, node_idx )->font_h),
621#endif /* RETROGXC_PRESENT */
622 &effect_style );
623 maug_cleanup_if_not_ok();
624
625 if( 0 <= tag_idx && MHTML_TAG_TYPE_TEXT == p_tag_iter->base.type ) {
626 /* Get text size to use in calculations below. */
627
628 mdata_strpool_lock( &(parser->strpool), strpool );
629
630#ifdef RETROGXC_PRESENT
631 retrogxc_string_sz(
632#else
633 retrofont_string_sz(
634#endif /* RETROGXC_PRESENT */
635 NULL, &(strpool[p_tag_iter->TEXT.content_idx]),
636 p_tag_iter->TEXT.content_sz,
637#ifdef RETROGXC_PRESENT
638 retrohtr_node( tree, node_idx )->font_idx,
639#else
640 retrohtr_node( tree, node_idx )->font_h,
641#endif /* RETROGXC_PRESENT */
642 /* Constrain node text size to parent size. */
643 retrohtr_node_parent( tree, node_idx )->w,
644 retrohtr_node_parent( tree, node_idx )->h,
645 &(retrohtr_node( tree, node_idx )->w),
646 &(retrohtr_node( tree, node_idx )->h), 0 );
647
648 debug_printf( RETROHTR_TRACE_LVL, "TEXT w: " SIZE_T_FMT,
649 retrohtr_node( tree, node_idx )->w );
650
651 mdata_strpool_unlock( &(parser->strpool), strpool );
652
653 } else if(
654 0 <= tag_idx &&
655 MHTML_TAG_TYPE_INPUT == p_tag_iter->base.type
656 ) {
657 /* Push the control (for the client renderer to redraw later). */
658
659 retval = retrohtr_tree_gui( tree, &(parser->styler), &effect_style );
660
661 retrogui_lock( &(tree->gui) );
662
663 if(
664 /* Use the same ID for the node and control it creates. */
665 MERROR_OK != retrogui_init_ctl(
666 &ctl, RETROGUI_CTL_TYPE_BUTTON, node_idx )
667 ) {
668 error_printf( "could not initialize control!" );
669 retrogui_unlock( &(tree->gui) );
670 goto cleanup;
671 }
672
673 p_tag_node = mdata_vector_get(
674 &(parser->tags),
675 retrohtr_node( tree, node_idx )->tag, union MHTML_TAG );
676
677 ctl.base.x = retrohtr_node( tree, node_idx )->x;
678 ctl.base.y = retrohtr_node( tree, node_idx )->y;
679 ctl.base.w = 0;
680 ctl.base.h = 0;
681 ctl.BUTTON.label = p_tag_node->INPUT.value;
682
683 /* Grab determined size back from control. */
684 retrohtr_node( tree, node_idx )->w = ctl.base.w;
685 retrohtr_node( tree, node_idx )->h = ctl.base.h;
686
687 debug_printf( RETROHTR_TRACE_LVL, "initialized control for INPUT..." );
688
689 retrogui_push_ctl( &(tree->gui), &ctl );
690
691 retrogui_unlock( &(tree->gui) );
692
693 } else if( 0 <= tag_idx && MHTML_TAG_TYPE_IMG == p_tag_iter->base.type ) {
694
695 if( retroflat_bitmap_ok( &(retrohtr_node( tree, node_idx )->bitmap) ) ) {
696 retrohtr_node( tree, node_idx )->w =
697 retroflat_bitmap_w( &(retrohtr_node( tree, node_idx )->bitmap) );
698 retrohtr_node( tree, node_idx )->h =
699 retroflat_bitmap_h( &(retrohtr_node( tree, node_idx )->bitmap) );
700 }
701
702 debug_printf( RETROHTR_TRACE_LVL, "TEXT w: " SIZE_T_FMT,
703 retrohtr_node( tree, node_idx )->w );
704
705 } else {
706 /* Get sizing of child nodes. */
707
708 maug_mzero( &child_prev_sibling_style, sizeof( struct MCSS_STYLE ) );
709 node_iter_idx = retrohtr_node( tree, node_idx )->first_child;
710 mdata_vector_unlock( &(parser->tags) );
711 while( 0 <= node_iter_idx ) {
712 retrohtr_tree_size(
713 parser, tree, &child_prev_sibling_style, &effect_style,
714 node_iter_idx, d + 1 );
715
716 node_iter_idx = retrohtr_node( tree, node_iter_idx )->next_sibling;
717 }
718 }
719
720 if( mdata_vector_is_locked( &(parser->tags) ) ) {
721 mdata_vector_unlock( &(parser->tags) );
722 }
723
724 /* If our width is still zero, then size based on children. */
725 if( 0 == retrohtr_node( tree, node_idx )->w ) {
726 if(
727 MCSS_DISPLAY_BLOCK == effect_style.DISPLAY &&
728 0 <= retrohtr_node( tree, node_idx )->parent
729 ) {
730 /* Use parent width. */
731 /* TODO: Subtract parent padding! */
732 retrohtr_node( tree, node_idx )->w =
733 retrohtr_node_parent( tree, node_idx )->w;
734 }
735
736 /* Cycle through children and use greatest width. */
737 child_iter_idx = retrohtr_node( tree, node_idx )->first_child;
738 while( 0 <= child_iter_idx ) {
739 assert( !mdata_vector_is_locked( &(parser->tags) ) );
740 retval = retrohtr_apply_styles(
741 parser, tree, &effect_style, &child_style,
742 retrohtr_node( tree, child_iter_idx )->tag );
743 maug_cleanup_if_not_ok();
744
745 /* Skip ABSOLUTE nodes. */
746 if( MCSS_POSITION_ABSOLUTE == child_style.POSITION ) {
747 child_iter_idx =
748 retrohtr_node( tree, child_iter_idx )->next_sibling;
749 continue;
750 }
751
752 if( MCSS_DISPLAY_BLOCK == child_style.DISPLAY ) {
753 /* Reset the line width counter for coming BLOCK node. */
754 this_line_w = 0;
755
756 if(
757 retrohtr_node( tree, child_iter_idx )->w >
758 retrohtr_node( tree, node_idx )->w
759 ) {
760 /* This BLOCK node is the longest yet! */
761 retrohtr_node( tree, node_idx )->w =
762 retrohtr_node( tree, child_iter_idx )->w;
763 }
764 } else {
765 /* Add inline node to this node line's width. */
766 this_line_w += retrohtr_node( tree, child_iter_idx )->w;
767
768 if( this_line_w > retrohtr_node( tree, node_idx )->w ) {
769 /* The line of nodes we've been adding up is the longest yet! */
770 retrohtr_node( tree, node_idx )->w = this_line_w;
771 }
772 }
773 child_iter_idx = retrohtr_node( tree, child_iter_idx )->next_sibling;
774 }
775 }
776
777 /* If our height is still zero, then size based on children. */
778 if( 0 == retrohtr_node( tree, node_idx )->h ) {
779 /* Cycle through children and add heights. */
780 child_iter_idx = retrohtr_node( tree, node_idx )->first_child;
781 while( 0 <= child_iter_idx ) {
782 assert( !mdata_vector_is_locked( &(parser->tags) ) );
784 parser, tree, &effect_style, &child_style,
785 retrohtr_node( tree, child_iter_idx )->tag );
786
787 /* Skip ABSOLUTE nodes. */
788 if( MCSS_POSITION_ABSOLUTE == child_style.POSITION ) {
789 child_iter_idx = retrohtr_node( tree, child_iter_idx )->next_sibling;
790 continue;
791 }
792
793 if( MCSS_DISPLAY_BLOCK == child_style.DISPLAY ) {
794 /* Add the last line to the running height. */
795 retrohtr_node( tree, node_idx )->h += this_line_h;
796
797 /* Start a new running line height with this BLOCK node. */
798 this_line_h = retrohtr_node( tree, child_iter_idx )->h;
799 } else {
800 /* Make sure this line is at least as tall as this INLINE node. */
801 if( this_line_h < retrohtr_node( tree, child_iter_idx )->h ) {
802 this_line_h = retrohtr_node( tree, child_iter_idx )->h;
803 }
804 }
805
806 child_iter_idx = retrohtr_node( tree, child_iter_idx )->next_sibling;
807 }
808
809 /* Add the last line height the node height. */
810 retrohtr_node( tree, node_idx )->h += this_line_h;
811 this_line_h = 0;
812 }
813
814 /* Apply additional modifiers (padding, etc) after children have all been
815 * calculated.
816 */
817
818 /* Try specific left padding first, then try general padding. */
819 if( mcss_prop_is_active_NOT_flag( effect_style.PADDING_LEFT, AUTO ) ) {
820 retrohtr_node( tree, node_idx )->w += effect_style.PADDING_LEFT;
821 } else if( mcss_prop_is_active_NOT_flag( effect_style.PADDING, AUTO ) ) {
822 retrohtr_node( tree, node_idx )->w += effect_style.PADDING;
823 }
824
825 /* Try specific right padding first, then try general padding. */
826 if( mcss_prop_is_active_NOT_flag( effect_style.PADDING_RIGHT, AUTO ) ) {
827 retrohtr_node( tree, node_idx )->w += effect_style.PADDING_RIGHT;
828 } else if( mcss_prop_is_active_NOT_flag( effect_style.PADDING, AUTO ) ) {
829 retrohtr_node( tree, node_idx )->w += effect_style.PADDING;
830 }
831
832 /* Try specific top padding first, then try general padding. */
833 if( mcss_prop_is_active_NOT_flag( effect_style.PADDING_TOP, AUTO ) ) {
834 retrohtr_node( tree, node_idx )->h += effect_style.PADDING_TOP;
835 } else if( mcss_prop_is_active_NOT_flag( effect_style.PADDING, AUTO ) ) {
836 retrohtr_node( tree, node_idx )->h += effect_style.PADDING;
837 }
838
839 /* Try specific bottom padding first, then try general padding. */
840 if( mcss_prop_is_active_NOT_flag( effect_style.PADDING_BOTTOM, AUTO ) ) {
841 retrohtr_node( tree, node_idx )->h += effect_style.PADDING_BOTTOM;
842 } else if( mcss_prop_is_active_NOT_flag( effect_style.PADDING, AUTO ) ) {
843 retrohtr_node( tree, node_idx )->h += effect_style.PADDING;
844 }
845
846 debug_printf( RETROHTR_TRACE_LVL,
847 "setting node " SIZE_T_FMT " dirty...", node_idx );
848 retrohtr_node( tree, node_idx )->flags |= RETROHTR_NODE_FLAG_DIRTY;
849
850cleanup:
851
852 if( mdata_vector_is_locked( &(parser->tags) ) ) {
853 mdata_vector_unlock( &(parser->tags) );
854 }
855
856 /* We're done with the prev_sibling_style for this iter, so prepare it for
857 * the next called by the parent!
858 */
859 if( NULL != prev_sibling_style ) {
860 maug_mcpy(
861 prev_sibling_style, &effect_style,
862 sizeof( struct MCSS_STYLE ) );
863 }
864
865 return retval;
866}
867
868/* TODO: See MCSS_POS_NOTE. */
870#define retrohtr_break_on_active_pos( iter_idx ) \
871 if( mcss_prop_is_active( retrohtr_node( tree, iter_idx )->pos ) ) { \
872 break; \
873 }
874
875static ssize_t retrohtr_find_prev_sibling_in_box_model(
876 struct RETROHTR_RENDER_TREE* tree,
877 ssize_t node_idx
878) {
879 ssize_t sibling_iter_idx = -1;
880 ssize_t sibling_found_idx = -1;
881
882 if( 0 > retrohtr_node( tree, node_idx )->parent ) {
883 /* Can't determine sibling! */
884 goto cleanup;
885 }
886
887 sibling_iter_idx = retrohtr_node_parent( tree, node_idx )->first_child;
888
889 if( sibling_iter_idx == node_idx ) {
890 /* No previous siblings! */
891 goto cleanup;
892 }
893
894 while( 0 <= sibling_iter_idx && node_idx != sibling_iter_idx ) {
895 if(
896 /* TODO: See MCSS_POS_NOTE. This is what we were talking about. */
897 MCSS_POSITION_ABSOLUTE != retrohtr_node( tree, sibling_iter_idx )->pos
898 ) {
899 sibling_found_idx = sibling_iter_idx;
900 }
901
902 /* TODO: Reset on <br />? */
903
904 sibling_iter_idx = retrohtr_node( tree, sibling_iter_idx )->next_sibling;
905 }
906
907cleanup:
908 return sibling_found_idx;
909}
910
911static MERROR_RETVAL retrohtr_mark_edge_child_nodes(
912 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
913 ssize_t node_parent_idx
914) {
915 ssize_t node_sibling_idx = -1;
916 MERROR_RETVAL retval = MERROR_OK;
917 struct MCSS_STYLE effect_style;
918 size_t col_idx = 0; /* How many nodes right (X)? */
919 size_t row_idx = 0; /* How many nodes down (Y)? */
920 union MHTML_TAG* p_tag_iter = NULL;
921
922 node_sibling_idx = retrohtr_node( tree, node_parent_idx )->first_child;
923 while( 0 <= node_sibling_idx ) {
924 maug_mzero( &effect_style, sizeof( struct MCSS_STYLE ) );
926 parser, tree, NULL, &effect_style,
927 retrohtr_node( tree, node_sibling_idx )->tag );
928
929 if( MCSS_POSITION_ABSOLUTE == effect_style.POSITION ) {
930 /* Absolute nodes are never on the edge. */
931 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_INSIDE;
932
933 } else if( MCSS_DISPLAY_INLINE == effect_style.DISPLAY ) {
934 /* Inline, or something that follows previous column. */
935 if( 0 == col_idx ) {
936 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_LEFT;
937 }
938 if( 0 == row_idx ) {
939 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_TOP;
940 }
941 if( 0 < row_idx && 0 < col_idx ) {
942 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_INSIDE;
943 }
944 col_idx++;
945
946 } else {
947 /* Block element will be on the next line, so take that into account
948 * when deciding the edge below.
949 */
950 row_idx++;
951 col_idx = 0;
952
953 /* Block, or something else in a new row. */
954 if( 0 == row_idx ) {
955 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_TOP;
956 }
957
958 /* Assume block is always on a new line. */
959 retrohtr_node( tree, node_sibling_idx )->edge |= RETROHTR_EDGE_LEFT;
960 }
961
962 assert( !mdata_vector_is_locked( &(parser->tags) ) );
963 mdata_vector_lock( &(parser->tags) );
964
965 p_tag_iter = mdata_vector_get( &(parser->tags),
966 retrohtr_node( tree, node_sibling_idx )->tag,
967 union MHTML_TAG );
968 assert( NULL != p_tag_iter );
969
970 debug_printf( 1, "marking node " SIZE_T_FMT " (%s) edge: %u",
971 node_sibling_idx,
972 gc_mhtml_tag_names[p_tag_iter->base.type],
973 retrohtr_node( tree, node_sibling_idx )->edge );
974
975 mdata_vector_unlock( &(parser->tags) );
976
977 node_sibling_idx =
978 retrohtr_node( tree, node_sibling_idx )->next_sibling;
979 }
980
981cleanup:
982
983 if( mdata_vector_is_locked( &(parser->tags) ) ) {
984 mdata_vector_unlock( &(parser->tags) );
985 }
986
987 return retval;
988}
989
990MERROR_RETVAL retrohtr_tree_pos(
991 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
992 struct MCSS_STYLE* prev_sibling_style,
993 struct MCSS_STYLE* parent_style, ssize_t node_idx, size_t d
994) {
995 struct MCSS_STYLE child_prev_sibling_style;
996 struct MCSS_STYLE effect_style;
997 ssize_t child_iter_idx = -1;
998 ssize_t tag_idx = -1;
999 ssize_t node_iter_idx = -1;
1000 ssize_t prev_sibling_idx = -1;
1001 MERROR_RETVAL retval = MERROR_OK;
1002 union MHTML_TAG* p_tag_iter = NULL;
1003
1004 if( NULL == retrohtr_node( tree, node_idx ) ) {
1005 goto cleanup;
1006 }
1007
1008 tag_idx = retrohtr_node( tree, node_idx )->tag;
1009
1011 parser, tree, parent_style, &effect_style, tag_idx );
1012
1013 prev_sibling_idx =
1014 retrohtr_find_prev_sibling_in_box_model( tree, node_idx );
1015
1016 /* x */
1017
1018 if( MCSS_POSITION_ABSOLUTE == effect_style.POSITION ) {
1019 /* This node is positioned absolutely. (Relatively) simple! */
1020
1021 if( mcss_prop_is_active_NOT_flag( effect_style.LEFT, AUTO ) ) {
1022
1023 child_iter_idx = retrohtr_node( tree, node_idx )->parent;
1024 while( 0 <= retrohtr_node( tree, child_iter_idx )->parent ) {
1025 retrohtr_break_on_active_pos( child_iter_idx );
1026 child_iter_idx = retrohtr_node( tree, child_iter_idx )->parent;
1027 }
1028
1029 /* Set X to highest non-explicit ancestor. */
1030 retrohtr_node( tree, node_idx )->x =
1031 retrohtr_node( tree, child_iter_idx )->x + effect_style.LEFT;
1032 }
1033 if( mcss_prop_is_active_NOT_flag( effect_style.RIGHT, AUTO ) ) {
1034
1035 child_iter_idx = retrohtr_node( tree, node_idx )->parent;
1036 while( 0 <= retrohtr_node( tree, child_iter_idx )->parent ) {
1037 retrohtr_break_on_active_pos( child_iter_idx );
1038 child_iter_idx = retrohtr_node( tree, child_iter_idx )->parent;
1039 }
1040
1041 /* Set X to highest non-explicit ancestor. */
1042 retrohtr_node( tree, node_idx )->x =
1043 retrohtr_node( tree, child_iter_idx )->w -
1044 retrohtr_node( tree, node_idx )->w -
1045 effect_style.RIGHT;
1046 }
1047
1048 } else if(
1049 MCSS_DISPLAY_INLINE == effect_style.DISPLAY &&
1050 MCSS_DISPLAY_INLINE == prev_sibling_style->DISPLAY &&
1051 0 <= prev_sibling_idx
1052 ) {
1053 /* Place to the right of the previous sibling. */
1054 retrohtr_node( tree, node_idx )->x =
1055 retrohtr_node( tree, prev_sibling_idx )->x +
1056 retrohtr_node( tree, prev_sibling_idx )->w;
1057
1058 } else if( 0 <= retrohtr_node( tree, node_idx )->parent ) {
1059 retrohtr_node( tree, node_idx )->x = retrohtr_node_parent( tree, node_idx )->x;
1060 }
1061
1062 /* y */
1063
1064 /* TODO: Add margins of children? */
1065
1066 if( MCSS_POSITION_ABSOLUTE == effect_style.POSITION ) {
1067 /* This node is positioned absolutely. (Relatively) simple! */
1068
1069 if( mcss_prop_is_active_NOT_flag( effect_style.TOP, AUTO ) ) {
1070
1071 child_iter_idx = retrohtr_node( tree, node_idx )->parent;
1072 while( 0 <= retrohtr_node( tree, child_iter_idx )->parent ) {
1073 retrohtr_break_on_active_pos( child_iter_idx );
1074 child_iter_idx = retrohtr_node( tree, child_iter_idx )->parent;
1075 }
1076
1077 /* Set Y to highest non-explicit ancestor. */
1078 retrohtr_node( tree, node_idx )->y =
1079 retrohtr_node( tree, child_iter_idx )->y + effect_style.TOP;
1080 }
1081 if( mcss_prop_is_active_NOT_flag( effect_style.BOTTOM, AUTO ) ) {
1082
1083 child_iter_idx = retrohtr_node( tree, node_idx )->parent;
1084 while( 0 <= retrohtr_node( tree, child_iter_idx )->parent ) {
1085 retrohtr_break_on_active_pos( child_iter_idx );
1086 child_iter_idx = retrohtr_node( tree, child_iter_idx )->parent;
1087 }
1088
1089 /* Set Y to highest non-explicit ancestor. */
1090 retrohtr_node( tree, node_idx )->y =
1091 retrohtr_node( tree, child_iter_idx )->h -
1092 retrohtr_node( tree, node_idx )->h -
1093 effect_style.BOTTOM;
1094 }
1095
1096 } else if(
1097 MCSS_DISPLAY_INLINE == effect_style.DISPLAY &&
1098 MCSS_DISPLAY_INLINE == prev_sibling_style->DISPLAY &&
1099 0 <= prev_sibling_idx
1100 ) {
1101 /* Place to the right of the previous sibling. */
1102 retrohtr_node( tree, node_idx )->y = retrohtr_node( tree, prev_sibling_idx )->y;
1103
1104 } else if( 0 <= prev_sibling_idx ) {
1105 /* Place below the previous block sibling. */
1106
1107 /* TODO: We should probably use the tallest element on the prev sibling's
1108 * line, but that seems hard...
1109 */
1110
1111 retrohtr_node( tree, node_idx )->y =
1112 retrohtr_node( tree, prev_sibling_idx )->y +
1113 retrohtr_node( tree, prev_sibling_idx )->h;
1114
1115 } else if( 0 <= retrohtr_node( tree, node_idx )->parent ) {
1116 /* Position relative to other nodes. */
1117
1118 retrohtr_node( tree, node_idx )->y = retrohtr_node_parent( tree, node_idx )->y;
1119 }
1120
1121 /* margin-left, margin-right */
1122
1123 if(
1124 MCSS_POSITION_ABSOLUTE != retrohtr_node( tree, node_idx )->pos &&
1125 0 <= retrohtr_node( tree, node_idx )->parent &&
1126 mcss_prop_is_active_flag( effect_style.MARGIN_LEFT, AUTO ) &&
1127 mcss_prop_is_active_flag( effect_style.MARGIN_RIGHT, AUTO )
1128 ) {
1129 /* Center */
1130 retrohtr_node( tree, node_idx )->x =
1131 retrohtr_node_parent( tree, node_idx )->x +
1132 (retrohtr_node_parent( tree, node_idx )->w >> 1) -
1133 (retrohtr_node( tree, node_idx )->w >> 1);
1134
1135 } else if(
1136 0 <= retrohtr_node( tree, node_idx )->parent &&
1137 mcss_prop_is_active_flag( effect_style.MARGIN_LEFT, AUTO ) &&
1138 mcss_prop_is_active_NOT_flag( effect_style.MARGIN_RIGHT, AUTO )
1139 ) {
1140 /* Justify right. */
1141 /* TODO: Subtract padding below, as well. */
1142 retrohtr_node( tree, node_idx )->x =
1143 retrohtr_node_parent( tree, node_idx )->w -
1144 retrohtr_node( tree, node_idx )->w;
1145
1146 } else if( mcss_prop_is_active( effect_style.MARGIN_LEFT ) ) {
1147 /* Justify left. */
1148 retrohtr_node( tree, node_idx )->x += effect_style.MARGIN_LEFT;
1149 }
1150
1151 /* padding */
1152
1153 /* TODO: Padding is still broken. Needs more involved understanding of
1154 * where elements are in their container.
1155 */
1156
1157 debug_printf( 1, "(d: " SIZE_T_FMT ") node " SIZE_T_FMT " is on edge: %u",
1158 d, node_idx, retrohtr_node( tree, node_idx )->edge );
1159
1160 assert(
1161 0 == node_idx ||
1162 RETROHTR_EDGE_UNKNOWN != retrohtr_node( tree, node_idx )->edge );
1163
1164 if(
1165 RETROHTR_EDGE_LEFT ==
1166 (RETROHTR_EDGE_LEFT & retrohtr_node( tree, node_idx )->edge)
1167 ) {
1168 /* Try specific left padding first, then try general padding. */
1169 if( mcss_prop_is_active_NOT_flag( parent_style->PADDING_LEFT, AUTO ) ) {
1170 retrohtr_node( tree, node_idx )->x += parent_style->PADDING_LEFT;
1171 } else if( mcss_prop_is_active_NOT_flag( parent_style->PADDING, AUTO ) ) {
1172 retrohtr_node( tree, node_idx )->x += parent_style->PADDING;
1173 }
1174 }
1175
1176 if(
1177 RETROHTR_EDGE_TOP ==
1178 (RETROHTR_EDGE_TOP & retrohtr_node( tree, node_idx )->edge) &&
1179 /* Only apply padding to first node in line. The rest will pick it up. */
1180 RETROHTR_EDGE_LEFT ==
1181 (RETROHTR_EDGE_LEFT & retrohtr_node( tree, node_idx )->edge)
1182 ) {
1183 /* Try specific top padding first, then try general padding. */
1184 if( mcss_prop_is_active_NOT_flag( parent_style->PADDING_TOP, AUTO ) ) {
1185 retrohtr_node( tree, node_idx )->y += parent_style->PADDING_TOP;
1186 } else if( mcss_prop_is_active_NOT_flag( parent_style->PADDING, AUTO ) ) {
1187 retrohtr_node( tree, node_idx )->y += parent_style->PADDING;
1188 }
1189 }
1190
1191 /* color */
1192
1193 if( mcss_prop_is_active( effect_style.COLOR ) ) {
1194 retrohtr_node( tree, node_idx )->fg = effect_style.COLOR;
1195 }
1196
1197 if( mcss_prop_is_active( effect_style.BACKGROUND_COLOR ) ) {
1198 retrohtr_node( tree, node_idx )->bg = effect_style.BACKGROUND_COLOR;
1199 }
1200
1201 /* Figure out child positions. */
1202
1203 retrohtr_mark_edge_child_nodes( parser, tree, node_idx );
1204
1205 maug_mzero( &child_prev_sibling_style, sizeof( struct MCSS_STYLE ) );
1206 node_iter_idx = retrohtr_node( tree, node_idx )->first_child;
1207 while( 0 <= node_iter_idx ) {
1208 /* Mark child nodes on the edge so applying padding can be done. */
1209
1210 /* Figure out child node positioning. */
1211 retrohtr_tree_pos(
1212 parser, tree, &child_prev_sibling_style, &effect_style,
1213 node_iter_idx, d + 1 );
1214
1215 node_iter_idx = retrohtr_node( tree, node_iter_idx )->next_sibling;
1216 }
1217
1218 assert( !mdata_vector_is_locked( &(parser->tags) ) );
1219 mdata_vector_lock( &(parser->tags) );
1220 p_tag_iter = mdata_vector_get( &(parser->tags), tag_idx, union MHTML_TAG );
1221 assert( NULL != p_tag_iter );
1222
1223 if( MHTML_TAG_TYPE_INPUT == p_tag_iter->base.type ) {
1224 /* Feed the position back to the GUI control created during tree_size. */
1225 retval = retrogui_pos_ctl( &(tree->gui), node_idx,
1226 retrohtr_node_screen_x( tree, node_idx ),
1227 retrohtr_node_screen_y( tree, node_idx ),
1228 retrohtr_node( tree, node_idx )->w,
1229 retrohtr_node( tree, node_idx )->h );
1230 maug_cleanup_if_not_ok();
1231 }
1232
1233 debug_printf( RETROHTR_TRACE_LVL,
1234 "setting node " SIZE_T_FMT " dirty...", node_idx );
1235 retrohtr_node( tree, node_idx )->flags |= RETROHTR_NODE_FLAG_DIRTY;
1236
1237cleanup:
1238
1239 if( mdata_vector_is_locked( &(parser->tags) ) ) {
1240 mdata_vector_unlock( &(parser->tags) );
1241 }
1242
1243 /* We're done with the prev_sibling_style for this iter, so prepare it for
1244 * the next called by the parent!
1245 */
1246 if( NULL != prev_sibling_style ) {
1247 maug_mcpy(
1248 prev_sibling_style, &effect_style,
1249 sizeof( struct MCSS_STYLE ) );
1250 }
1251
1252 return retval;
1253}
1254
1255MERROR_RETVAL retrohtr_tree_draw(
1256 struct MHTML_PARSER* parser, struct RETROHTR_RENDER_TREE* tree,
1257 ssize_t node_idx, size_t d
1258) {
1259 char* strpool = NULL;
1260 union MHTML_TAG* p_tag = NULL;
1261 struct RETROHTR_RENDER_NODE* node = NULL;
1262 MERROR_RETVAL retval = MERROR_OK;
1263
1264 node = retrohtr_node( tree, node_idx );
1265
1266 if( NULL == node ) {
1267 return MERROR_OK;
1268 }
1269
1270 /* TODO: Multi-pass, draw absolute pos afterwards. */
1271
1272 if( 0 > node->tag ) {
1273 goto cleanup;
1274 }
1275
1276 if( RETROHTR_NODE_FLAG_DIRTY != (RETROHTR_NODE_FLAG_DIRTY & node->flags) ) {
1277 goto cleanup;
1278 }
1279
1280 assert( !mdata_vector_is_locked( &(parser->tags) ) );
1281 mdata_vector_lock( &(parser->tags) );
1282
1283 p_tag = mdata_vector_get( &(parser->tags), node->tag, union MHTML_TAG );
1284 if( NULL == p_tag ) {
1285 goto cleanup;
1286 }
1287
1288 /* Perform drawing. */
1289 if( MHTML_TAG_TYPE_TEXT == p_tag->base.type ) {
1290
1291 if(
1292 0 > p_tag->TEXT.content_idx ||
1293#ifdef RETROGXC_PRESENT
1294 0 > node->font_idx
1295#else
1296 NULL == node->font_h
1297#endif /* RETROGXC_PRESENT */
1298 ) {
1299 goto cleanup;
1300 }
1301
1302 mdata_strpool_lock( &(parser->strpool), strpool );
1303
1304#ifdef RETROGXC_PRESENT
1305 retrogxc_string(
1306#else
1307 retrofont_string(
1308#endif /* RETROGXC_PRESENT */
1309 NULL, node->fg,
1310 &(strpool[p_tag->TEXT.content_idx]), p_tag->TEXT.content_sz,
1311#ifdef RETROGXC_PRESENT
1312 node->font_idx,
1313#else
1314 node->font_h,
1315#endif /* RETROGXC_PRESENT */
1316 retrohtr_node_screen_x( tree, node_idx ),
1317 retrohtr_node_screen_y( tree, node_idx ),
1318 node->w, node->h, 0 );
1319
1320 mdata_strpool_unlock( &(parser->strpool), strpool );
1321
1322 } else if( MHTML_TAG_TYPE_BODY == p_tag->base.type ) {
1323
1324 debug_printf(
1325 RETROHTR_TRACE_LVL, "drawing BODY node " SIZE_T_FMT "...", node_idx );
1326
1327 /* Draw body BG. */
1328 if( RETROFLAT_COLOR_NULL != node->bg ) {
1329 retroflat_rect(
1330 NULL, node->bg,
1331 retrohtr_node_screen_x( tree, node_idx ),
1332 retrohtr_node_screen_y( tree, node_idx ),
1333 retrohtr_node( tree, node_idx )->w,
1334 retrohtr_node( tree, node_idx )->h,
1335 RETROFLAT_FLAGS_FILL );
1336 }
1337
1338 } else if( MHTML_TAG_TYPE_IMG == p_tag->base.type ) {
1339 /* Blit the image. */
1340
1341 if( !retroflat_bitmap_ok( &(retrohtr_node( tree, node_idx )->bitmap) ) ) {
1342 goto cleanup;
1343 }
1344
1345 debug_printf(
1346 RETROHTR_TRACE_LVL, "drawing IMG node " SIZE_T_FMT "...", node_idx );
1347
1349 NULL, &(retrohtr_node( tree, node_idx )->bitmap),
1350 0, 0,
1351 retrohtr_node_screen_x( tree, node_idx ),
1352 retrohtr_node_screen_y( tree, node_idx ),
1353 retroflat_bitmap_w( &(retrohtr_node( tree, node_idx )->bitmap) ),
1354 retroflat_bitmap_h( &(retrohtr_node( tree, node_idx )->bitmap) ),
1356 /* retrohtr_node( tree, node_idx )->w,
1357 retrohtr_node( tree, node_idx )->h */ );
1358
1359 } else if( MHTML_TAG_TYPE_INPUT == p_tag->base.type ) {
1360
1361 debug_printf(
1362 RETROHTR_TRACE_LVL, "setting tree GUI dirty..." );
1363
1364 tree->gui.flags |= RETROGUI_FLAGS_DIRTY;
1365
1366 } else {
1367 if( RETROFLAT_COLOR_NULL == node->bg ) {
1368 goto cleanup;
1369 }
1370
1371 debug_printf(
1372 RETROHTR_TRACE_LVL, "drawing xs node " SIZE_T_FMT "...",
1373 /* gc_mhtml_tag_names[mhtml_tag( parser,
1374 retrohtr_node( tree, node_idx )->tag )->base.type], */
1375 node_idx );
1376
1378 NULL, node->bg,
1379 retrohtr_node_screen_x( tree, node_idx ),
1380 retrohtr_node_screen_y( tree, node_idx ),
1381 node->w, node->h,
1383 }
1384
1385 node->flags &= ~RETROHTR_NODE_FLAG_DIRTY;
1386
1387cleanup:
1388
1389 if( retrogui_is_locked( &(tree->gui) ) ) {
1390 retrogui_unlock( &(tree->gui) );
1391 }
1392
1393 if( mdata_vector_is_locked( &(parser->tags) ) ) {
1394 mdata_vector_unlock( &(parser->tags) );
1395 }
1396
1397 if( MERROR_OK != retval ) {
1398 error_printf( "failed drawing node: " SIZE_T_FMT, node_idx );
1399 }
1400
1401 /* Keep trying to render children, tho. */
1402
1403 retrohtr_tree_draw( parser, tree, node->first_child, d + 1 );
1404
1405 retrohtr_tree_draw( parser, tree, node->next_sibling, d );
1406
1407 /* If this is the root redraw call, redraw GUI elements. */
1408 if(
1409 0 == d &&
1410 RETROHTR_TREE_FLAG_GUI_ACTIVE ==
1411 (tree->flags & RETROHTR_TREE_FLAG_GUI_ACTIVE)
1412 ) {
1413 retrogui_lock( &(tree->gui) );
1414 retrogui_redraw_ctls( &(tree->gui) );
1415 retrogui_unlock( &(tree->gui) );
1416 }
1417
1418 return retval;
1419}
1420
1421retrogui_idc_t retrohtr_tree_poll_ctls(
1422 struct RETROHTR_RENDER_TREE* tree,
1423 RETROFLAT_IN_KEY* input,
1424 struct RETROFLAT_INPUT* input_evt
1425) {
1426 retrogui_idc_t idc = 0;
1427 MERROR_RETVAL retval = MERROR_OK;
1428
1429 assert( retrohtr_tree_is_locked( tree ) );
1430
1431 if(
1432 RETROHTR_TREE_FLAG_GUI_ACTIVE !=
1433 (RETROHTR_TREE_FLAG_GUI_ACTIVE & tree->flags)
1434 ) {
1435 /* No GUI, so exit without even unlocking. */
1436 return 0;
1437 }
1438
1439 idc = retrogui_poll_ctls( &(tree->gui), input, input_evt );
1440
1441 if( 0 < idc ) {
1442 debug_printf(
1443 RETROHTR_TRACE_LVL, "setting node " SIZE_T_FMT " dirty...", idc );
1444 retrohtr_node( tree, idc )->flags |= RETROHTR_NODE_FLAG_DIRTY;
1445 }
1446
1447 if( MERROR_OK != retval ) {
1448 idc = 0;
1449 }
1450
1451 return idc;
1452}
1453
1454MERROR_RETVAL retrohtr_tree_dump(
1455 struct RETROHTR_RENDER_TREE* tree, struct MHTML_PARSER* parser,
1456 ssize_t node_idx, size_t d
1457) {
1458 size_t i = 0;
1459 char indents[31];
1460 union MHTML_TAG* p_tag_iter = NULL;
1461 MERROR_RETVAL retval = MERROR_OK;
1462
1463 if( 0 > node_idx ) {
1464 return MERROR_OK;
1465 }
1466
1467 assert( !mdata_vector_is_locked( &(parser->tags) ) );
1468 mdata_vector_lock( &(parser->tags) );
1469
1470 p_tag_iter = mdata_vector_get(
1471 &(parser->tags), tree->nodes[node_idx].tag, union MHTML_TAG );
1472 if( NULL == p_tag_iter ) {
1473 goto cleanup;
1474 }
1475
1476 /* Generate the indentation. */
1477 maug_mzero( indents, 30 );
1478 for( i = 0 ; d > i ; i++ ) {
1479 if( maug_strlen( indents ) >= 30 ) {
1480 break;
1481 }
1482 strcat( indents, " " );
1483 }
1484
1485 /* Print the debug line. */
1486 debug_printf(
1487 1,
1488 "%s" SSIZE_T_FMT " (tag %s): x: " SSIZE_T_FMT ", y: " SSIZE_T_FMT
1489 " (" SSIZE_T_FMT " x " SSIZE_T_FMT ") f: "
1490#ifdef RETROGXC_PRESENT
1491 SSIZE_T_FMT,
1492#else
1493 "%p",
1494#endif /* RETROGXC_PRESENT */
1495 indents, node_idx,
1496 0 <= tree->nodes[node_idx].tag ?
1497 gc_mhtml_tag_names[p_tag_iter->base.type] : "ROOT",
1498 tree->nodes[node_idx].x, tree->nodes[node_idx].y,
1499 tree->nodes[node_idx].w, tree->nodes[node_idx].h,
1500#ifdef RETROGXC_PRESENT
1501 tree->nodes[node_idx].font_idx
1502#else
1503 tree->nodes[node_idx].font_h
1504#endif /* RETROGXC_PRESENT */
1505 );
1506
1507 mdata_vector_unlock( &(parser->tags) );
1508
1509 retval = retrohtr_tree_dump(
1510 tree, parser, tree->nodes[node_idx].first_child, d + 1 );
1511 maug_cleanup_if_not_ok();
1512
1513 retval = retrohtr_tree_dump(
1514 tree, parser, tree->nodes[node_idx].next_sibling, d );
1515 maug_cleanup_if_not_ok();
1516
1517cleanup:
1518
1519 return retval;
1520}
1521
1522void retrohtr_tree_free( struct RETROHTR_RENDER_TREE* tree ) {
1523
1524 debug_printf( RETROHTR_TRACE_LVL, "freeing render nodes..." );
1525
1526 /* TODO: Free bitmaps from img! */
1527
1528 /* TODO: Free node->font_h! */
1529
1530 /* Free GUI if present. */
1531 if(
1532 RETROHTR_TREE_FLAG_GUI_ACTIVE ==
1533 (tree->flags & RETROHTR_TREE_FLAG_GUI_ACTIVE)
1534 ) {
1535 retrogui_free( &(tree->gui) );
1536 }
1537
1538 /* Unlock nodes before trying to free them. */
1539 retrohtr_tree_unlock( tree );
1540
1541 if( NULL != tree->nodes_h ) {
1542 maug_mfree( tree->nodes_h );
1543 }
1544}
1545
1546MERROR_RETVAL retrohtr_tree_init( struct RETROHTR_RENDER_TREE* tree ) {
1547 MERROR_RETVAL retval = MERROR_OK;
1548
1549 maug_mzero( tree, sizeof( struct RETROHTR_RENDER_TREE ) );
1550
1551 /* Perform initial node allocation. */
1552 tree->nodes_sz_max = MHTML_PARSER_TAGS_INIT_SZ;
1553 debug_printf( RETROHTR_TRACE_LVL,
1554 "allocating " SIZE_T_FMT " nodes...", tree->nodes_sz_max );
1555 tree->nodes_h = maug_malloc(
1556 tree->nodes_sz_max, sizeof( struct RETROHTR_RENDER_NODE ) );
1557 maug_cleanup_if_null_alloc( struct RETROHTR_RENDER_NODE*, tree->nodes_h );
1558
1559 /* XXX
1560 r.w_max = retroflat_screen_w();
1561 r.h_max = retroflat_screen_h(); */
1562
1563cleanup:
1564
1565 return retval;
1566}
1567
1568#endif /* RETROHTR_C */
1569
1570#endif /* !RETROHTR_H */
1571
int MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition merror.h:19
#define maug_mzero(ptr, sz)
Zero the block of memory pointed to by ptr.
Definition mmem.h:62
#define maug_mrealloc(handle, nmemb, sz)
Definition mmem.h:54
MERROR_RETVAL retroflat_blit_bitmap(struct RETROFLAT_BITMAP *target, struct RETROFLAT_BITMAP *src, size_t s_x, size_t s_y, int16_t d_x, int16_t d_y, size_t w, size_t h, int16_t instance)
Blit the contents of a RETROFLAT_BITMAP onto another RETROFLAT_BITMAP.
MERROR_RETVAL retroflat_load_bitmap(const char *filename, struct RETROFLAT_BITMAP *bmp_out, uint8_t flags)
Load a bitmap into the given RETROFLAT_BITMAP structure if it is available. Bitmaps are subject to th...
#define RETROFLAT_INSTANCE_NULL
Pass to retroflat_blit_bitmap() instance arg if this is not a sprite (i.e. if it is a background tile...
Definition retroflt.h:567
#define retroflat_bitmap_ok(bitmap)
Check to see if a bitmap is loaded.
Definition retpltd.h:30
int8_t RETROFLAT_COLOR
Defines an index in the platform-specific color-table.
Definition retroflt.h:307
#define RETROFLAT_FLAGS_LITERAL_PATH
Flag for retroflat_load_bitmap() to not use assets path.
Definition retroflt.h:374
void retroflat_rect(struct RETROFLAT_BITMAP *target, const RETROFLAT_COLOR color, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t flags)
Draw a rectangle onto the target RETROFLAT_BITMAP.
#define RETROFLAT_FLAGS_FILL
Flag for retroflat_rect() or retroflat_ellipse(), indicating drawn shape should be filled.
Definition retroflt.h:355
#define RETROGUI_FLAGS_DIRTY
RETROGUI::flags indicating controls should be redrawn.
Definition retrogui.h:16
retrogui_idc_t retrogui_poll_ctls(struct RETROGUI *gui, RETROFLAT_IN_KEY *p_input, struct RETROFLAT_INPUT *input_evt)
Poll for the last clicked control and maintain listboxes and menus.
size_t retrogui_idc_t
Unique identifying constant number for controls.
Definition retrogui.h:89
size_t nodes_sz_max
Current alloc'd number of nodes in RETROHTR_RENDER_NODE::nodes_h.
Definition retrohtr.h:67
ssize_t parent
Index of container's render node in RETROHTR_RENDER_TREE.
Definition retrohtr.h:51
ssize_t first_child
Index of first child's render node in RETROHTR_RENDER_TREE.
Definition retrohtr.h:53
MERROR_RETVAL retrohtr_apply_styles(struct MHTML_PARSER *parser, struct RETROHTR_RENDER_TREE *tree, struct MCSS_STYLE *parent_style, struct MCSS_STYLE *effect_style, ssize_t tag_idx)
Create a style node that is a composite of a parent style and the styles applicable to the classes/ID...
ssize_t next_sibling
Index of next sibling's render node in RETROHTR_RENDER_TREE.
Definition retrohtr.h:55
size_t nodes_sz
Current active number of nodes in RETROHTR_RENDER_NODE::nodes_h.
Definition retrohtr.h:65
struct RETROHTR_RENDER_NODE * nodes
Locked pointer to nodes when locked with retrohtr_tree_lock().
Definition retrohtr.h:63
Definition mhtml.h:150
Platform-specific bitmap structure. retroflat_bitmap_ok() can be used on a pointer to it to determine...
Definition retpltd.h:21
Struct passed to retroflat_poll_input() to hold return data.
Definition retroflt.h:836
Definition retrogui.h:167
Definition retrohtr.h:28
Definition retrohtr.h:59
Definition mhtml.h:145
Definition retrogui.h:158