1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  This file is part of the GtkHTML library.
3  *
4  *  Copyright (C) 1997 Martin Jones (mjones@kde.org)
5  *  Copyright (C) 1997 Torben Weis (weis@kde.org)
6  *  Copyright (C) 1999, 2000 Helix Code, Inc.
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Library General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Library General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Library General Public License
19  *  along with this library; see the file COPYING.LIB.  If not, write to
20  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  *  Boston, MA 02110-1301, USA.
22 */
23 
24 #include <config.h>
25 #include "htmlclue.h"
26 #include "htmlclueflow.h"
27 #include "htmlobject.h"
28 #include "htmlsearch.h"
29 #include "htmltextslave.h"
30 #include "htmltype.h"
31 
32 
33 #define HC_CLASS(x) (HTML_CLUE_CLASS (HTML_OBJECT (x)->klass))
34 
35 HTMLClueClass html_clue_class;
36 static HTMLObjectClass *parent_class = NULL;
37 
38 static void set_parent (HTMLObject *o, HTMLObject *tail, HTMLObject *parent);
39 
40 /* HTMLObject methods.  */
41 
42 static void
destroy(HTMLObject * o)43 destroy (HTMLObject *o)
44 {
45 	HTMLObject *p;
46 	HTMLObject *next;
47 
48 	for (p = HTML_CLUE (o)->head; p != NULL; p = next) {
49 		next = p->next;
50 		html_object_destroy (p);
51 	}
52 	HTML_CLUE (o)->head = NULL;
53 	HTML_CLUE (o)->tail = NULL;
54 
55 	HTML_OBJECT_CLASS (parent_class)->destroy (o);
56 }
57 
58 static gint
get_n_children(HTMLObject * o)59 get_n_children (HTMLObject *o)
60 {
61 	HTMLObject *cur = HTML_CLUE (o)->head;
62 	gint n_children = 0;
63 
64 	while (cur) {
65 		n_children++;
66 		cur = html_object_next_not_slave (cur);
67 	}
68 
69 	return n_children;
70 }
71 
72 static HTMLObject *
get_child(HTMLObject * o,gint index)73 get_child (HTMLObject *o,
74            gint index)
75 {
76 	HTMLObject *cur = HTML_CLUE (o)->head;
77 
78 	g_return_val_if_fail (index >= 0, NULL);
79 
80 	while (cur) {
81 		if (!index)
82 			break;
83 		index--;
84 		cur = html_object_next_not_slave (cur);
85 	}
86 
87 	return cur;
88 }
89 
90 static gint
get_child_index(HTMLObject * self,HTMLObject * child)91 get_child_index (HTMLObject *self,
92                  HTMLObject *child)
93 {
94 	HTMLObject *cur = HTML_CLUE (self)->head;
95 	gint index = 0;
96 
97 	while (cur) {
98 		if (cur == child)
99 			return index;
100 		index++;
101 		cur = html_object_next_not_slave (cur);
102 	}
103 
104 	return -1;
105 }
106 
107 static guint
get_recursive_length(HTMLObject * self)108 get_recursive_length (HTMLObject *self)
109 {
110 	HTMLObject *o = HTML_CLUE (self)->head;
111 	guint len = 0;
112 
113 	while (o) {
114 		len += html_object_get_recursive_length (o);
115 		o = o->next;
116 	}
117 
118 	return len;
119 }
120 
121 static void
copy(HTMLObject * self,HTMLObject * dest)122 copy (HTMLObject *self,
123       HTMLObject *dest)
124 {
125 	(* HTML_OBJECT_CLASS (parent_class)->copy) (self, dest);
126 
127 	HTML_CLUE (dest)->head = NULL;
128 	HTML_CLUE (dest)->tail = NULL;
129 	HTML_CLUE (dest)->curr = NULL;
130 
131 	HTML_CLUE (dest)->valign = HTML_CLUE (self)->valign;
132 	HTML_CLUE (dest)->halign = HTML_CLUE (self)->halign;
133 }
134 
135 static HTMLObject *
op_helper(HTMLObject * self,HTMLEngine * e,GList * from,GList * to,GList * left,GList * right,guint * len,gboolean cut)136 op_helper (HTMLObject *self,
137            HTMLEngine *e,
138            GList *from,
139            GList *to,
140            GList *left,
141            GList *right,
142            guint *len,
143            gboolean cut)
144 {
145 	HTMLClue *clue = HTML_CLUE (self);
146 	HTMLObject *cc;
147 	HTMLObject *o, *last, *cnext, *child;
148 
149 	cc   = html_object_dup (self);
150 	o    = (from) ? HTML_OBJECT (from->data) : clue->head;
151 	last = (to)   ? HTML_OBJECT (to->data)   : clue->tail;
152 
153 	if ((o == NULL) || (last == NULL))
154 		return cc;
155 
156 	if (HTML_IS_TEXT_SLAVE (last))
157 		last = html_object_prev_not_slave (last);
158 
159 	g_assert (o->parent == self);
160 	g_assert (last->parent == self);
161 
162 	while (o) {
163 		cnext = html_object_next_not_slave (o);
164 		child = cut ? html_object_op_cut (o, e,
165 					      html_object_get_bound_list (o, from),
166 					      html_object_get_bound_list (o, to),
167 					      left ? left->next : NULL, right ? right->next : NULL, len)
168 			: html_object_op_copy (o, cc, e,
169 					       html_object_get_bound_list (o, from),
170 					       html_object_get_bound_list (o, to), len);
171 		if (child)
172 			html_clue_append (HTML_CLUE (cc), child);
173 
174 		if (o == last)
175 			break;
176 		o = cnext;
177 	}
178 
179 	return cc;
180 }
181 
182 static HTMLObject *
op_copy(HTMLObject * self,HTMLObject * parent,HTMLEngine * e,GList * from,GList * to,guint * len)183 op_copy (HTMLObject *self,
184          HTMLObject *parent,
185          HTMLEngine *e,
186          GList *from,
187          GList *to,
188          guint *len)
189 {
190 	return op_helper (self, e, from, to, NULL, NULL, len, FALSE);
191 }
192 
193 static HTMLObject *
op_cut(HTMLObject * self,HTMLEngine * e,GList * from,GList * to,GList * left,GList * right,guint * len)194 op_cut (HTMLObject *self,
195         HTMLEngine *e,
196         GList *from,
197         GList *to,
198         GList *left,
199         GList *right,
200         guint *len)
201 {
202 	HTMLObject *rv;
203 	HTMLClue *clue;
204 
205 	clue = HTML_CLUE (self);
206 	rv   = op_helper (self, e, from, to, left, right, len, TRUE);
207 	if (!clue->head) {
208 		if (self->parent)
209 			html_object_remove_child (self->parent, self);
210 		html_object_destroy (self);
211 	} else
212 		html_object_change_set (self, HTML_CHANGE_ALL_CALC);
213 
214 	return rv;
215 }
216 
217 static gboolean
merge(HTMLObject * self,HTMLObject * with,HTMLEngine * e,GList ** left,GList ** right,HTMLCursor * cursor)218 merge (HTMLObject *self,
219        HTMLObject *with,
220        HTMLEngine *e,
221        GList **left,
222        GList **right,
223        HTMLCursor *cursor)
224 {
225 	HTMLClue   *clue1, *clue2;
226 
227 	clue1 = HTML_CLUE (self);
228 	clue2 = HTML_CLUE (with);
229 
230 	html_clue_append (clue1, clue2->head);
231 	clue2->head = NULL;
232 	clue2->tail = NULL;
233 
234 	html_object_change_set (self, HTML_CHANGE_ALL_CALC);
235 	return TRUE;
236 }
237 
238 static void
remove_child(HTMLObject * self,HTMLObject * child)239 remove_child (HTMLObject *self,
240               HTMLObject *child)
241 {
242 	html_clue_remove (HTML_CLUE (self), child);
243 }
244 
245 static void
split(HTMLObject * self,HTMLEngine * e,HTMLObject * child,gint offset,gint level,GList ** left,GList ** right)246 split (HTMLObject *self,
247        HTMLEngine *e,
248        HTMLObject *child,
249        gint offset,
250        gint level,
251        GList **left,
252        GList **right)
253 {
254 	HTMLObject *dup;
255 
256 	dup  = html_object_dup (self);
257 
258 	HTML_CLUE (dup)->tail  = HTML_CLUE (self)->tail;
259 	HTML_CLUE (self)->tail = child->prev;
260 	if (child->prev)
261 			child->prev->next = NULL;
262 	child->prev = NULL;
263 	if (child == HTML_CLUE (self)->head)
264 		HTML_CLUE (self)->head = NULL;
265 	HTML_CLUE (dup)->head  = child;
266 	set_parent (child, NULL, dup);
267 
268 	if (self->parent && HTML_OBJECT_TYPE (self->parent) != HTML_TYPE_TABLE)
269 		html_clue_append_after (HTML_CLUE (self->parent), dup, self);
270 
271 	self->x = 0;
272 	self->y = 0;
273 
274 	*left  = g_list_prepend (*left, self);
275 	*right = g_list_prepend (*right, dup);
276 
277 	level--;
278 	if (level > 0)
279 		html_object_split (self->parent, e, dup, 0, level, left, right);
280 }
281 
282 static void
draw(HTMLObject * o,HTMLPainter * p,gint x,gint y,gint width,gint height,gint tx,gint ty)283 draw (HTMLObject *o,
284       HTMLPainter *p,
285       gint x,
286       gint y,
287       gint width,
288       gint height,
289       gint tx,
290       gint ty)
291 {
292 	HTMLObject *obj;
293 
294 	if (y + height < o->y - o->ascent || y > o->y + o->descent)
295 		return;
296 
297 	tx += o->x;
298 	ty += o->y - o->ascent;
299 
300 	for (obj = HTML_CLUE (o)->head; obj != 0; obj = obj->next) {
301 		if (!(obj->flags & HTML_OBJECT_FLAG_ALIGNED)) {
302 			html_object_draw (obj,
303 					  p,
304 					  x - o->x, y - (o->y - o->ascent),
305 					  width, height,
306 					  tx, ty);
307 		}
308 	}
309 }
310 
311 static void
set_max_height(HTMLObject * o,HTMLPainter * painter,gint height)312 set_max_height (HTMLObject *o,
313                 HTMLPainter *painter,
314                 gint height)
315 {
316 	HTMLClue *clue = HTML_CLUE (o);
317 	HTMLObject *obj;
318 
319 	if (o->ascent < height) {
320 		for (obj = HTML_CLUE (o)->head; obj != 0; obj = obj->next) {
321 			html_object_set_max_height (obj, painter, height);
322 			if (clue->valign == HTML_VALIGN_MIDDLE)
323 				obj->y += (height - o->ascent) / 2;
324 			else if (clue->valign == HTML_VALIGN_BOTTOM)
325 				obj->y += height - o->ascent;
326 		}
327 
328 		o->ascent = height;
329 	}
330 }
331 
332 static void
reset(HTMLObject * clue)333 reset (HTMLObject *clue)
334 {
335 	HTMLObject *obj;
336 
337 	for (obj = HTML_CLUE (clue)->head; obj != NULL; obj = obj->next)
338 		html_object_reset (obj);
339 
340 	HTML_CLUE (clue)->curr = NULL;
341 
342 	(* HTML_OBJECT_CLASS (parent_class)->reset) (HTML_OBJECT (clue));
343 }
344 
345 static gboolean
html_clue_real_calc_size(HTMLObject * o,HTMLPainter * painter,GList ** changed_objs)346 html_clue_real_calc_size (HTMLObject *o,
347                           HTMLPainter *painter,
348                           GList **changed_objs)
349 {
350 	gboolean changed;
351 
352 	/* If we have already called calc_size for the children, then just
353 	 * continue from the last object done in previous call. */
354 	if (HTML_CLUE (o)->curr == NULL) {
355 		o->ascent = 0;
356 		HTML_CLUE (o)->curr = HTML_CLUE (o)->head;
357 	}
358 
359 	changed = FALSE;
360 
361 	while (HTML_CLUE (o)->curr != NULL) {
362 		changed |= html_object_calc_size (HTML_CLUE (o)->curr, painter, changed_objs);
363 		HTML_CLUE (o)->curr = HTML_CLUE (o)->curr->next;
364 	}
365 
366 	/* Remember the last object so that we can start from here next time
367 	 * we are called */
368 	HTML_CLUE (o)->curr = HTML_CLUE (o)->tail;
369 
370 	return changed;
371 }
372 
373 static gint
calc_preferred_width(HTMLObject * o,HTMLPainter * painter)374 calc_preferred_width (HTMLObject *o,
375                       HTMLPainter *painter)
376 {
377 	gint prefWidth = 0;
378 	HTMLObject *obj;
379 
380 	for (obj = HTML_CLUE (o)->head; obj != 0; obj = obj->next) {
381 		gint w;
382 
383 		w = html_object_calc_preferred_width (obj, painter);
384 		if (w > prefWidth)
385 			prefWidth = w;
386 	}
387 
388 	return prefWidth;
389 }
390 
391 /* FIXME: This should be in HTMLClueV.  */
392 static gint
calc_min_width(HTMLObject * o,HTMLPainter * painter)393 calc_min_width (HTMLObject *o,
394                 HTMLPainter *painter)
395 {
396 	HTMLObject *obj;
397 	gint minWidth = 0;
398 
399 	for (obj = HTML_CLUE (o)->head; obj != 0; obj = obj->next) {
400 		gint w;
401 
402 		w = html_object_calc_min_width (obj, painter);
403 		if (w > minWidth)
404 			minWidth = w;
405 	}
406 
407 	return minWidth;
408 }
409 
410 static HTMLAnchor *
find_anchor(HTMLObject * self,const gchar * name,gint * x,gint * y)411 find_anchor (HTMLObject *self,
412              const gchar *name,
413              gint *x,
414              gint *y)
415 {
416 	HTMLClue *clue;
417 	HTMLObject *obj;
418 	HTMLAnchor *anchor;
419 
420 	*x += self->x;
421 	*y += self->y - self->ascent;
422 
423 	clue = HTML_CLUE (self);
424 
425 	for (obj = clue->head; obj != NULL; obj = obj->next) {
426 		if ((anchor = html_object_find_anchor (obj, name, x, y)) != 0 )
427 			return anchor;
428 	}
429 
430 	*x -= self->x;
431 	*y -= self->y - self->ascent;
432 
433 	return 0;
434 }
435 
436 static HTMLObject *
check_point(HTMLObject * o,HTMLPainter * painter,gint x,gint y,guint * offset_return,gboolean for_cursor)437 check_point (HTMLObject *o,
438              HTMLPainter *painter,
439              gint x,
440              gint y,
441              guint *offset_return,
442              gboolean for_cursor)
443 {
444 	HTMLObject *obj;
445 	HTMLObject *obj2;
446 
447 	if (x < o->x || x >= o->x + o->width
448 	    || y >= o->y + o->descent || y < o->y - o->ascent)
449 		return NULL;
450 
451 	x = x - o->x;
452 	y = y - o->y + o->ascent;
453 
454 	for (obj = HTML_CLUE (o)->head; obj != 0; obj = obj->next) {
455 		obj2 = html_object_check_point (obj, painter,
456 						x, y, offset_return,
457 						for_cursor);
458 		if (obj2 != NULL)
459 			return obj2;
460 	}
461 
462 	return NULL;
463 }
464 
465 static gint
check_page_split(HTMLObject * self,HTMLPainter * painter,gint y)466 check_page_split (HTMLObject *self,
467                   HTMLPainter *painter,
468                   gint y)
469 {
470 	HTMLClue *clue;
471 	HTMLObject *p;
472 	gint last_under = 0;
473 
474 	clue = HTML_CLUE (self);
475 	for (p = clue->head; p != NULL; p = p->next) {
476 		gint y1, y2;
477 
478 		y1 = p->y - p->ascent;
479 		y2 = p->y + p->descent;
480 
481 		if (y1 > y)
482 			return last_under;
483 
484 		if (y >= y1 && y < y2)
485 			return html_object_check_page_split (p, painter, y - y1) + y1;
486 		last_under = y2;
487 	}
488 
489 	return y;
490 }
491 
492 static void
forall(HTMLObject * self,HTMLEngine * e,HTMLObjectForallFunc func,gpointer data)493 forall (HTMLObject *self,
494         HTMLEngine *e,
495         HTMLObjectForallFunc func,
496         gpointer data)
497 {
498 	HTMLObject *p, *pnext;
499 
500 	for (p = HTML_CLUE (self)->head; p != NULL; p = pnext) {
501 		pnext = p->next;
502 		html_object_forall (p, e, func, data);
503 	}
504 
505 	html_object_class.forall (self, e, func, data);
506 }
507 
508 static gboolean
is_container(HTMLObject * self)509 is_container (HTMLObject *self)
510 {
511 	return TRUE;
512 }
513 
514 static gboolean
save(HTMLObject * self,HTMLEngineSaveState * state)515 save (HTMLObject *self,
516       HTMLEngineSaveState *state)
517 {
518 	HTMLObject *p;
519 	HTMLClue *clue;
520 
521 	clue = HTML_CLUE (self);
522 
523 	for (p = clue->head; p != NULL; p = p->next) {
524 		if (!html_object_save (p, state))
525 		    return FALSE;
526 	}
527 
528 	return TRUE;
529 }
530 
531 static gboolean
save_plain(HTMLObject * self,HTMLEngineSaveState * state,gint requested_width)532 save_plain (HTMLObject *self,
533             HTMLEngineSaveState *state,
534             gint requested_width)
535 {
536 	HTMLObject *p;
537 	HTMLClue *clue;
538 
539 	clue = HTML_CLUE (self);
540 
541 	for (p = clue->head; p != NULL; p = p->next) {
542 		if (!html_object_save_plain (p, state, requested_width))
543 		    return FALSE;
544 	}
545 
546 	return TRUE;
547 }
548 
549 /* HTMLClue methods.  */
550 
551 static gint
get_left_clear(HTMLClue * o,gint y)552 get_left_clear (HTMLClue *o,
553                 gint y)
554 {
555 	return y;
556 }
557 
558 static gint
get_right_clear(HTMLClue * o,gint y)559 get_right_clear (HTMLClue *o,
560                  gint y)
561 {
562 	return y;
563 }
564 
565 static void
find_free_area(HTMLClue * clue,HTMLPainter * painter,gint y,gint width,gint height,gint indent,gint * y_pos,gint * lmargin,gint * rmargin)566 find_free_area (HTMLClue *clue,
567                 HTMLPainter *painter,
568                 gint y,
569                 gint width,
570                 gint height,
571                 gint indent,
572                 gint *y_pos,
573                 gint *lmargin,
574                 gint *rmargin)
575 {
576 	*y_pos = y;
577 	*lmargin = 0;
578 	*rmargin = MAX (HTML_OBJECT (clue)->max_width, HTML_OBJECT (clue)->width);
579 }
580 
581 static void
append_right_aligned(HTMLClue * clue,HTMLPainter * painter,HTMLClue * aclue,gint * lmargin,gint * rmargin,gint indent)582 append_right_aligned (HTMLClue *clue,
583                       HTMLPainter *painter,
584                       HTMLClue *aclue,
585                       gint *lmargin,
586                       gint *rmargin,
587                       gint indent)
588 {
589 	/* This needs to be implemented in the subclasses.  */
590 	g_warning ("`%s' does not implement `append_right_aligned()'.",
591 		   html_type_name (HTML_OBJECT_TYPE (clue)));
592 }
593 
594 static gboolean
appended(HTMLClue * clue,HTMLClue * aclue)595 appended (HTMLClue *clue,
596           HTMLClue *aclue)
597 {
598 	return FALSE;
599 }
600 
601 static gboolean
search(HTMLObject * obj,HTMLSearch * info)602 search (HTMLObject *obj,
603         HTMLSearch *info)
604 {
605 	HTMLObject *cur;
606 	HTMLClue *clue = HTML_CLUE (obj);
607 	gboolean next = FALSE;
608 
609 	/* search_next? */
610 	if (html_search_child_on_stack (info, obj)) {
611 		cur  = html_search_pop (info);
612 		cur = (info->forward) ? cur->next : cur->prev;
613 		next = TRUE;
614 	} else
615 		cur = (info->forward) ? clue->head : clue->tail;
616 
617 	while (cur) {
618 		html_search_push (info, cur);
619 		if (html_object_search (cur, info))
620 			return TRUE;
621 
622 		html_search_pop (info);
623 		cur = (info->forward) ? cur->next : cur->prev;
624 	}
625 
626 	if (next)
627 		return html_search_next_parent (info);
628 
629 	return FALSE;
630 }
631 
632 static void
append_selection_string(HTMLObject * self,GString * buffer)633 append_selection_string (HTMLObject *self,
634                          GString *buffer)
635 {
636 	HTMLObject *o = HTML_CLUE (self)->head;
637 
638 	while (o) {
639 		html_object_append_selection_string (o, buffer);
640 		o = o->next;
641 	}
642 }
643 
644 static HTMLObject *
clue_head(HTMLObject * self)645 clue_head (HTMLObject *self)
646 {
647 	return HTML_CLUE (self)->head;
648 }
649 
650 static HTMLObject *
clue_tail(HTMLObject * self)651 clue_tail (HTMLObject *self)
652 {
653 	HTMLObject *obj;
654 
655 	obj = HTML_CLUE (self)->tail;
656 	return (obj && HTML_OBJECT_TYPE (obj) == HTML_TYPE_TEXTSLAVE) ? html_object_prev_not_slave (obj) : obj;
657 }
658 
659 
660 void
html_clue_type_init(void)661 html_clue_type_init (void)
662 {
663 	html_clue_class_init (&html_clue_class, HTML_TYPE_CLUE, sizeof (HTMLClue));
664 }
665 
666 void
html_clue_class_init(HTMLClueClass * klass,HTMLType type,guint size)667 html_clue_class_init (HTMLClueClass *klass,
668                       HTMLType type,
669                       guint size)
670 {
671 	HTMLObjectClass *object_class;
672 
673 	g_return_if_fail (klass != NULL);
674 
675 	object_class = HTML_OBJECT_CLASS (klass);
676 	html_object_class_init (object_class, type, size);
677 
678 	/* HTMLObject functions */
679 	object_class->destroy = destroy;
680 	object_class->copy = copy;
681 	object_class->op_copy = op_copy;
682 	object_class->op_cut = op_cut;
683 	object_class->merge = merge;
684 	object_class->remove_child = remove_child;
685 	object_class->split = split;
686 	object_class->draw = draw;
687 	object_class->set_max_height = set_max_height;
688 	object_class->reset = reset;
689 	object_class->calc_size = html_clue_real_calc_size;
690 	object_class->calc_preferred_width = calc_preferred_width;
691 	object_class->calc_min_width = calc_min_width;
692 	object_class->check_point = check_point;
693 	object_class->check_page_split = check_page_split;
694 	object_class->find_anchor = find_anchor;
695 	object_class->forall = forall;
696 	object_class->is_container = is_container;
697 	object_class->save = save;
698 	object_class->save_plain = save_plain;
699 	object_class->search = search;
700 	object_class->append_selection_string = append_selection_string;
701 	object_class->head = clue_head;
702 	object_class->tail = clue_tail;
703 	object_class->get_recursive_length = get_recursive_length;
704 	object_class->get_n_children = get_n_children;
705 	object_class->get_child = get_child;
706 	object_class->get_child_index = get_child_index;
707 
708 	/* HTMLClue methods.  */
709 	klass->get_left_clear = get_left_clear;
710 	klass->get_right_clear = get_right_clear;
711 	klass->find_free_area = find_free_area;
712 	klass->append_right_aligned = append_right_aligned;
713 	klass->appended = appended;
714 
715 	parent_class = &html_object_class;
716 }
717 
718 void
html_clue_init(HTMLClue * clue,HTMLClueClass * klass)719 html_clue_init (HTMLClue *clue,
720                 HTMLClueClass *klass)
721 {
722 	HTMLObject *object;
723 
724 	object = HTML_OBJECT (clue);
725 	html_object_init (object, HTML_OBJECT_CLASS (klass));
726 
727 	clue->head = NULL;
728 	clue->tail = NULL;
729 	clue->curr = NULL;
730 
731 	clue->valign = HTML_VALIGN_TOP;
732 	clue->halign = HTML_HALIGN_LEFT;
733 }
734 
735 
736 gint
html_clue_get_left_clear(HTMLClue * clue,gint y)737 html_clue_get_left_clear (HTMLClue *clue,
738                           gint y)
739 {
740 	return (* HC_CLASS (clue)->get_left_clear) (clue, y);
741 }
742 
743 gint
html_clue_get_right_clear(HTMLClue * clue,gint y)744 html_clue_get_right_clear (HTMLClue *clue,
745                            gint y)
746 {
747 	return (* HC_CLASS (clue)->get_right_clear) (clue, y);
748 }
749 
750 void
html_clue_find_free_area(HTMLClue * clue,HTMLPainter * painter,gint y,gint width,gint height,gint indent,gint * y_pos,gint * lmargin,gint * rmargin)751 html_clue_find_free_area (HTMLClue *clue,
752                           HTMLPainter *painter,
753                           gint y,
754                           gint width,
755                           gint height,
756                           gint indent,
757                           gint *y_pos,
758                           gint *lmargin,
759                           gint *rmargin)
760 {
761 	(* HC_CLASS (clue)->find_free_area) (clue, painter, y, width, height, indent, y_pos, lmargin, rmargin);
762 }
763 
764 void
html_clue_append_right_aligned(HTMLClue * clue,HTMLPainter * painter,HTMLClue * aclue,gint * lmargin,gint * rmargin,gint indent)765 html_clue_append_right_aligned (HTMLClue *clue,
766                                 HTMLPainter *painter,
767                                 HTMLClue *aclue,
768                                 gint *lmargin,
769                                 gint *rmargin,
770                                 gint indent)
771 {
772 	g_assert (clue != NULL);
773 	g_assert (aclue != NULL);
774 
775 	html_object_change_set (HTML_OBJECT (clue), HTML_OBJECT (aclue)->change);
776 
777 	(* HC_CLASS (clue)->append_right_aligned) (clue, painter, aclue, lmargin, rmargin, indent);
778 }
779 
780 void
html_clue_append_left_aligned(HTMLClue * clue,HTMLPainter * painter,HTMLClue * aclue,gint * lmargin,gint * rmargin,gint indent)781 html_clue_append_left_aligned (HTMLClue *clue,
782                                HTMLPainter *painter,
783                                HTMLClue *aclue,
784                                gint *lmargin,
785                                gint *rmargin,
786                                gint indent)
787 {
788 	g_assert (clue != NULL);
789 	g_assert (aclue != NULL);
790 
791 	html_object_change_set (HTML_OBJECT (clue), HTML_OBJECT (aclue)->change);
792 
793 	(* HC_CLASS (clue)->append_left_aligned) (clue, painter, aclue, lmargin, rmargin, indent);
794 }
795 
796 gboolean
html_clue_appended(HTMLClue * clue,HTMLClue * aclue)797 html_clue_appended (HTMLClue *clue,
798                     HTMLClue *aclue)
799 {
800 	return (* HC_CLASS (clue)->appended) (clue, aclue);
801 }
802 
803 
804 /* Utility functions.  */
805 
806 static HTMLObject *
get_tail(HTMLObject * p)807 get_tail (HTMLObject *p)
808 {
809 	if (p == NULL)
810 		return NULL;
811 
812 	while (p->next != NULL)
813 		p = p->next;
814 
815 	return p;
816 }
817 
818 static void
set_parent(HTMLObject * o,HTMLObject * tail,HTMLObject * parent)819 set_parent (HTMLObject *o,
820             HTMLObject *tail,
821             HTMLObject *parent)
822 {
823 	while (o) {
824 		html_object_set_parent (o, parent);
825 		if (o == tail)
826 			break;
827 		o = o->next;
828 	}
829 }
830 
831 /**
832  * html_clue_append_after:
833  * @clue: An HTMLClue.
834  * @o: An HTMLObject.
835  * @where: A child of @clue.
836  *
837  * Insert @o and its successors in @clue after @clue's element @where.
838  **/
839 void
html_clue_append_after(HTMLClue * clue,HTMLObject * o,HTMLObject * where)840 html_clue_append_after (HTMLClue *clue,
841                         HTMLObject *o,
842                         HTMLObject *where)
843 {
844 	HTMLObject *tail;
845 
846 	g_return_if_fail (o != NULL);
847 	g_return_if_fail (html_object_is_clue (HTML_OBJECT (clue)));
848 
849 	if (where == NULL) {
850 		html_clue_prepend (clue, o);
851 		return;
852 	}
853 	g_return_if_fail (where->parent == HTML_OBJECT (clue));
854 
855 	html_object_change_set (HTML_OBJECT (clue), o->change);
856 
857 	tail = get_tail (o);
858 
859 	if (where->next != NULL)
860 		where->next->prev = tail;
861 	tail->next = where->next;
862 
863 	where->next = o;
864 	o->prev = where;
865 
866 	if (where == clue->tail)
867 		clue->tail = tail;
868 
869 	set_parent (o, tail, HTML_OBJECT (clue));
870 }
871 
872 /**
873  * html_clue_append:
874  * @clue: An HTMLClue.
875  * @o: An HTMLObject.
876  *
877  * Append @o and its successors to @clue.
878  **/
879 void
html_clue_append(HTMLClue * clue,HTMLObject * o)880 html_clue_append (HTMLClue *clue,
881                   HTMLObject *o)
882 {
883 	HTMLObject *tail;
884 
885 	g_return_if_fail (clue != NULL);
886 	g_return_if_fail (html_object_is_clue (HTML_OBJECT (clue)));
887 	g_return_if_fail (o != NULL);
888 
889 	html_object_change_set (HTML_OBJECT (clue), o->change);
890 
891 	tail = get_tail (o);
892 
893 	if (!clue->head) {
894 		clue->head = o;
895 		o->prev = NULL;
896 	} else {
897 		clue->tail->next = o;
898 		o->prev = clue->tail;
899 	}
900 
901 	clue->tail = tail;
902 	tail->next = NULL;
903 
904 	html_object_set_parent (o, HTML_OBJECT (clue));
905 
906 	set_parent (o, tail, HTML_OBJECT (clue));
907 }
908 
909 /**
910  * html_clue_prepend:
911  * @clue: An HTMLClue.
912  * @o: An HTMLObject.
913  *
914  * Prepend @o and its successors to @clue.
915  **/
916 void
html_clue_prepend(HTMLClue * clue,HTMLObject * o)917 html_clue_prepend (HTMLClue *clue,
918                    HTMLObject *o)
919 {
920 	HTMLObject *tail;
921 
922 	g_return_if_fail (clue != NULL);
923 	g_return_if_fail (o != NULL);
924 
925 	html_object_change_set (HTML_OBJECT (clue), o->change);
926 
927 	tail = get_tail (o);
928 
929 	if (!clue->head) {
930 		clue->head = o;
931 		clue->tail = tail;
932 		o->prev = NULL;
933 	} else {
934 		o->next = clue->head;
935 		clue->head->prev = o;
936 		clue->head = o;
937 	}
938 
939 	o->prev = NULL;
940 
941 	set_parent (o, tail, HTML_OBJECT (clue));
942 }
943 
944 /**
945  * html_clue_remove:
946  * @clue: An HTMLClue.
947  * @o: An HTMLObject.
948  *
949  * Remove object @o from the clue.
950  **/
951 void
html_clue_remove(HTMLClue * clue,HTMLObject * o)952 html_clue_remove (HTMLClue *clue,
953                   HTMLObject *o)
954 {
955 	g_return_if_fail (clue != NULL);
956 	g_return_if_fail (o != NULL);
957 	g_return_if_fail (clue == HTML_CLUE (o->parent));
958 
959 	if (o == clue->head)
960 		clue->head = o->next;
961 	if (o == clue->tail)
962 		clue->tail = o->prev;
963 
964 	if (o->next != NULL)
965 		o->next->prev = o->prev;
966 	if (o->prev != NULL)
967 		o->prev->next = o->next;
968 
969 	o->parent = NULL;
970 	o->prev = NULL;
971 	o->next = NULL;
972 }
973 
974 void
html_clue_remove_text_slaves(HTMLClue * clue)975 html_clue_remove_text_slaves (HTMLClue *clue)
976 {
977 	HTMLObject *p;
978 	HTMLObject *pnext;
979 
980 	g_return_if_fail (clue != NULL);
981 
982 	for (p = clue->head; p != NULL; p = pnext) {
983 		pnext = p->next;
984 
985 		if (HTML_OBJECT_TYPE (p) == HTML_TYPE_TEXTSLAVE) {
986 			html_clue_remove (clue, p);
987 			html_object_destroy (p);
988 		}
989 	}
990 }
991 
992 gboolean
html_clue_is_empty(HTMLClue * clue)993 html_clue_is_empty (HTMLClue *clue)
994 {
995 	if (clue->head == NULL)
996 		return TRUE;
997 	if (clue->head == clue->tail
998 	    && HTML_IS_CLUEFLOW (clue->head) && html_clueflow_is_empty (HTML_CLUEFLOW (clue->head)))
999 		return TRUE;
1000 	return FALSE;
1001 }
1002