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 /* This is the object that defines a paragraph in the HTML document.  */
25 
26 /* WARNING: it must always be the child of a clue.  */
27 
28 #include <config.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <stdlib.h>
32 
33 #include "gtkhtml-properties.h"
34 
35 #include "htmlcolor.h"
36 #include "htmlcolorset.h"
37 #include "htmlclue.h"
38 #include "htmlclueflow.h"
39 #include "htmlcluealigned.h"
40 #include "htmlentity.h"
41 #include "htmlengine-edit.h"
42 #include "htmlengine-save.h"
43 #include "htmlpainter.h"
44 #include "htmlplainpainter.h"
45 #include "htmlprinter.h"
46 #include "htmlsearch.h"
47 #include "htmlselection.h"
48 #include "htmlsettings.h"
49 #include "htmltable.h"
50 #include "htmltablecell.h"
51 #include "htmltext.h"
52 #include "htmltextslave.h"	/* FIXME */
53 
54 HTMLClueFlowClass html_clueflow_class;
55 static HTMLClueClass *parent_class = NULL;
56 
57 #define HCF_CLASS(x) HTML_CLUEFLOW_CLASS (HTML_OBJECT (x)->klass)
58 
59 static gchar *        get_item_marker_str                   (HTMLClueFlow *flow, gboolean ascii_only);
60 static guint          get_post_padding                      (HTMLClueFlow *flow,
61 							     guint pad);
62 static gint            get_similar_depth                     (HTMLClueFlow *self,
63 							     HTMLClueFlow *neighbor);
64 
65 static void
copy_levels(GByteArray * dst,GByteArray * src)66 copy_levels (GByteArray *dst,
67              GByteArray *src)
68 {
69 	gint i;
70 
71 	g_byte_array_set_size (dst, src->len);
72 
73 	for (i = 0; i < src->len; i++)
74 		dst->data[i] = src->data[i];
75 }
76 
77 static gboolean
is_levels_equal(HTMLClueFlow * me,HTMLClueFlow * you)78 is_levels_equal (HTMLClueFlow *me,
79                  HTMLClueFlow *you)
80 {
81 	if (!you)
82 		return FALSE;
83 
84 	if (me->levels->len != you->levels->len)
85 		return FALSE;
86 
87 	if (me->levels->len == 0)
88 		return TRUE;
89 
90 	return !memcmp (me->levels->data, you->levels->data, you->levels->len);
91 }
92 
93 static inline gboolean
is_item(HTMLClueFlow * flow)94 is_item (HTMLClueFlow *flow)
95 {
96 	return flow && flow->style == HTML_CLUEFLOW_STYLE_LIST_ITEM;
97 }
98 
99 static void
destroy(HTMLObject * self)100 destroy (HTMLObject *self)
101 {
102 	HTMLClueFlow *flow = HTML_CLUEFLOW (self);
103 
104 	g_byte_array_free (flow->levels, TRUE);
105 	if (flow->item_color) {
106 		html_color_unref (flow->item_color);
107 		flow->item_color = NULL;
108 	}
109 
110 	(* HTML_OBJECT_CLASS (parent_class)->destroy) (self);
111 }
112 
113 static void
copy(HTMLObject * self,HTMLObject * dest)114 copy (HTMLObject *self,
115       HTMLObject *dest)
116 {
117 	(* HTML_OBJECT_CLASS (parent_class)->copy) (self, dest);
118 
119 	HTML_CLUEFLOW (dest)->levels = html_clueflow_dup_levels (HTML_CLUEFLOW (self));
120 	HTML_CLUEFLOW (dest)->style = HTML_CLUEFLOW (self)->style;
121 	HTML_CLUEFLOW (dest)->item_type = HTML_CLUEFLOW (self)->item_type;
122 	HTML_CLUEFLOW (dest)->item_number = HTML_CLUEFLOW (self)->item_number;
123 	HTML_CLUEFLOW (dest)->clear = HTML_CLUEFLOW (self)->clear;
124 	HTML_CLUEFLOW (dest)->item_color = HTML_CLUEFLOW (self)->item_color;
125 	HTML_CLUEFLOW (dest)->indent_width = HTML_CLUEFLOW (self)->indent_width;
126 	HTML_CLUEFLOW (dest)->dir = HTML_CLUEFLOW (self)->dir;
127 
128 	if (HTML_CLUEFLOW (dest)->item_color)
129 		html_color_ref (HTML_CLUEFLOW (dest)->item_color);
130 }
131 
132 static inline gboolean
is_blockquote(HTMLListType type)133 is_blockquote (HTMLListType type)
134 {
135 	if ((type == HTML_LIST_TYPE_BLOCKQUOTE_CITE)
136 	    || (type == HTML_LIST_TYPE_BLOCKQUOTE))
137 		return TRUE;
138 
139 	return FALSE;
140 }
141 
142 static inline gboolean
items_are_relative(HTMLObject * self,HTMLObject * next_object)143 items_are_relative (HTMLObject *self,
144                     HTMLObject *next_object)
145 {
146 	HTMLClueFlow *flow, *next;
147 
148 	if (!self || !next_object)
149 		return FALSE;
150 	flow = HTML_CLUEFLOW (self);
151 	next = HTML_CLUEFLOW (next_object);
152 
153 	if (!is_item (flow)
154 	    || !is_item (next)
155 	    || !is_levels_equal (flow, next)
156 	    || next->item_type != flow->item_type)
157 		return FALSE;
158 
159 	return TRUE;
160 }
161 
162 static HTMLObject *
get_prev_relative_item(HTMLObject * self)163 get_prev_relative_item (HTMLObject *self)
164 {
165 	HTMLObject *prev;
166 
167 	prev = self->prev;
168 	while (prev
169 	       && HTML_IS_CLUEFLOW (prev)
170 	       && (HTML_CLUEFLOW (prev)->levels->len > HTML_CLUEFLOW (self)->levels->len
171 		   || (HTML_CLUEFLOW (prev)->levels->len == HTML_CLUEFLOW (self)->levels->len
172 		       && !is_item (HTML_CLUEFLOW (prev))))
173 	       && !memcmp (HTML_CLUEFLOW (prev)->levels->data,
174 			   HTML_CLUEFLOW (self)->levels->data,
175 			   HTML_CLUEFLOW (self)->levels->len))
176 
177 		prev = prev->prev;
178 
179 	return prev;
180 }
181 
182 static HTMLObject *
get_next_relative_item(HTMLObject * self)183 get_next_relative_item (HTMLObject *self)
184 {
185 	HTMLObject *next;
186 
187 	next = self->next;
188 	while (next
189 	       && HTML_IS_CLUEFLOW (next)
190 	       && (HTML_CLUEFLOW (next)->levels->len > HTML_CLUEFLOW (self)->levels->len
191 		   || (HTML_CLUEFLOW (next)->levels->len == HTML_CLUEFLOW (self)->levels->len
192 		       && !is_item (HTML_CLUEFLOW (next))))
193 	       && !memcmp (HTML_CLUEFLOW (next)->levels->data,
194 			   HTML_CLUEFLOW (self)->levels->data,
195 			   HTML_CLUEFLOW (self)->levels->len))
196 
197 		next = next->next;
198 
199 	return next;
200 }
201 
202 static void
update_item_number(HTMLObject * self,HTMLEngine * e)203 update_item_number (HTMLObject *self,
204                     HTMLEngine *e)
205 {
206 	HTMLObject *prev, *next;
207 
208 	if (!self || !is_item (HTML_CLUEFLOW (self)))
209 		return;
210 
211 	/* printf ("update_item_number\n"); */
212 
213 	prev = get_prev_relative_item (self);
214 	if (items_are_relative (prev, self))
215 		HTML_CLUEFLOW (self)->item_number = HTML_CLUEFLOW (prev)->item_number + 1;
216 	else if (is_item (HTML_CLUEFLOW (self)))
217 		HTML_CLUEFLOW (self)->item_number = 1;
218 	html_engine_queue_draw (e, self);
219 
220 	next = self;
221 	while ((next = get_next_relative_item (next)) && items_are_relative (self, next)) {
222 		HTML_CLUEFLOW (next)->item_number = HTML_CLUEFLOW (self)->item_number + 1;
223 		html_engine_queue_draw (e, next);
224 		self = next;
225 	}
226 }
227 
228 static guint
get_recursive_length(HTMLObject * self)229 get_recursive_length (HTMLObject *self)
230 {
231 	return (*HTML_OBJECT_CLASS (parent_class)->get_recursive_length) (self) + (self->next ? 1 : 0);
232 }
233 
234 static gboolean
prev_flow_in_cluevs(HTMLObject * o)235 prev_flow_in_cluevs (HTMLObject *o)
236 {
237 	if (o->prev)
238 		return TRUE;
239 
240 	o = o->parent;
241 
242 	/* go up the tree to find cluev with prev */
243 	while (o && HTML_IS_CLUEV (o) && !o->prev)
244 		o = o->parent;
245 
246 	/* go down the tree to find last clueflow */
247 	if (o && o->prev) {
248 		o = o->prev;
249 
250 		while (o && HTML_IS_CLUEV (o))
251 			o = HTML_CLUE (o)->tail;
252 
253 		if (o && HTML_IS_CLUEFLOW (o))
254 			return TRUE;
255 	}
256 
257 	return FALSE;
258 }
259 
260 static HTMLObject *
op_helper(HTMLObject * self,HTMLEngine * e,GList * from,GList * to,GList * left,GList * right,guint * len,gboolean cut)261 op_helper (HTMLObject *self,
262            HTMLEngine *e,
263            GList *from,
264            GList *to,
265            GList *left,
266            GList *right,
267            guint *len,
268            gboolean cut)
269 {
270 	HTMLObject *o;
271 
272 	/* if (!from && to && HTML_IS_TABLE (to->data) && to->next && GPOINTER_TO_INT (to->next->data) == 0)
273 		return NULL;
274 	if (!to && from && HTML_IS_TABLE (from->data) && from->next && GPOINTER_TO_INT (from->next->data) == 1)
275 		return NULL;
276 	if (!from && (*len || !(self->prev && HTML_IS_CLUEFLOW (self->prev) && HTML_IS_TABLE (HTML_CLUE (self->prev)->tail))))
277 	(*len) ++; */
278 	if (!from && prev_flow_in_cluevs (self)) {
279 		(*len) ++;
280 		/* if (cut)
281 		 * e->cursor->position--; */
282 	}
283 	if (cut)
284 		html_clue_remove_text_slaves (HTML_CLUE (self));
285 	o = cut
286 		? (*HTML_OBJECT_CLASS (parent_class)->op_cut) (self, e, from, to, left, right, len)
287 		: (*HTML_OBJECT_CLASS (parent_class)->op_copy) (self, NULL, e, from, to, len);
288 
289 	if (!cut && o) {
290 		html_clue_remove_text_slaves (HTML_CLUE (o));
291 	}
292 
293 	return o;
294 }
295 
296 static HTMLObject *
op_copy(HTMLObject * self,HTMLObject * parent,HTMLEngine * e,GList * from,GList * to,guint * len)297 op_copy (HTMLObject *self,
298          HTMLObject *parent,
299          HTMLEngine *e,
300          GList *from,
301          GList *to,
302          guint *len)
303 {
304 	return op_helper (self, e, from, to, NULL, NULL, len, FALSE);
305 }
306 
307 static HTMLObject *
op_cut(HTMLObject * self,HTMLEngine * e,GList * from,GList * to,GList * left,GList * right,guint * len)308 op_cut (HTMLObject *self,
309         HTMLEngine *e,
310         GList *from,
311         GList *to,
312         GList *left,
313         GList *right,
314         guint *len)
315 {
316 	HTMLObject *rv, *prev, *next;
317 
318 	prev = self->prev;
319 	next = self->next;
320 
321 	rv = op_helper (self, e, from, to, left, right, len, TRUE);
322 
323 	if (prev && from) {
324 		update_item_number (prev, e);
325 		if (prev->next == self)
326 			update_item_number (self, e);
327 	}
328 	if (next && to) {
329 		if (next->prev == self)
330 			update_item_number (self, e);
331 		update_item_number (next, e);
332 	}
333 
334 	return rv;
335 }
336 
337 static void
set_head_size(HTMLObject * o)338 set_head_size (HTMLObject *o)
339 {
340 	if (o && HTML_CLUE (o)->head)
341 		HTML_CLUE (o)->head->change |= HTML_CHANGE_SIZE;
342 }
343 
344 static void
set_tail_size(HTMLObject * o)345 set_tail_size (HTMLObject *o)
346 {
347 	if (o && HTML_CLUE (o)->tail) {
348 		HTML_CLUE (o)->tail->change |= HTML_CHANGE_SIZE;
349 	}
350 }
351 
352 static void
set_around_size(HTMLObject * o)353 set_around_size (HTMLObject *o) {
354         if (o) {
355                 o->change |= HTML_CHANGE_SIZE;
356                 if (o->next)
357                         o->next->change |= HTML_CHANGE_SIZE;
358                 if (o->prev)
359                         o->prev->change |= HTML_CHANGE_SIZE;
360         }
361 }
362 
363 static void
split(HTMLObject * self,HTMLEngine * e,HTMLObject * child,gint offset,gint level,GList ** left,GList ** right)364 split (HTMLObject *self,
365        HTMLEngine *e,
366        HTMLObject *child,
367        gint offset,
368        gint level,
369        GList **left,
370        GList **right)
371 {
372 	set_around_size (child);
373 	html_clue_remove_text_slaves (HTML_CLUE (self));
374 	(*HTML_OBJECT_CLASS (parent_class)->split) (self, e, child, offset, level, left, right);
375 
376 	update_item_number (self, e);
377 }
378 
379 static gboolean
merge(HTMLObject * self,HTMLObject * with,HTMLEngine * e,GList ** left,GList ** right,HTMLCursor * cursor)380 merge (HTMLObject *self,
381        HTMLObject *with,
382        HTMLEngine *e,
383        GList **left,
384        GList **right,
385        HTMLCursor *cursor)
386 {
387 	HTMLClueFlow *cf1, *cf2;
388 	HTMLObject *cf2_next_relative;
389 	gboolean rv;
390 
391 	cf1 = HTML_CLUEFLOW (self);
392 	cf2 = HTML_CLUEFLOW (with);
393 
394 	html_clue_remove_text_slaves (HTML_CLUE (cf1));
395 	html_clue_remove_text_slaves (HTML_CLUE (cf2));
396 
397 	cf2_next_relative = get_next_relative_item (with);
398 
399 	set_tail_size (self);
400 	set_head_size (with);
401 
402 	if (html_clueflow_is_empty (cf1)) {
403 		self->x = with->x;
404 		self->y = with->y;
405 		self->width = with->width;
406 		self->ascent = with->ascent;
407 		self->descent = with->descent;
408 		HTML_CLUE  (cf1)->halign = HTML_CLUE (cf2)->halign;
409 		HTML_CLUE  (cf1)->valign = HTML_CLUE (cf2)->valign;
410 		html_object_copy_data_from_object (self, with);
411 		cf1->dir = cf2->dir;
412 	}
413 
414 	rv = (* HTML_OBJECT_CLASS (parent_class)->merge) (self, with, e, left, right, cursor);
415 
416 	if (rv) {
417 		if (is_item (cf1)) {
418 			/* cf2 will be removed, update item numbers around
419 			 * as if it was already removed - it has to have
420 			 * the same item style as cf1 to not break item lists*/
421 			g_byte_array_free (cf2->levels, TRUE);
422 			cf2->levels = html_clueflow_dup_levels (cf1);
423 			cf2->style = cf1->style;
424 			cf2->item_type = cf1->item_type;
425 
426 			update_item_number (self, e);
427 			cf1->item_number--;
428 			update_item_number (with, e);
429 			cf1->item_number++;
430 
431 			if (cf2_next_relative)
432 				update_item_number (cf2_next_relative, e);
433 		}
434 
435 	}
436 
437 	return rv;
438 }
439 
440 static guint
calc_padding(HTMLPainter * painter)441 calc_padding (HTMLPainter *painter)
442 {
443 	if (!HTML_IS_PLAIN_PAINTER (painter)) {
444 		return 2 * html_painter_get_space_width (painter, GTK_HTML_FONT_STYLE_SIZE_3, NULL);
445 	}
446 	return 0;
447 }
448 
449 static gboolean
is_cite(HTMLClueFlow * flow,gint level)450 is_cite (HTMLClueFlow *flow,
451          gint level)
452 {
453 	if (flow->levels->data[level] == HTML_LIST_TYPE_BLOCKQUOTE_CITE)
454 		return TRUE;
455 
456 	return FALSE;
457 }
458 
459 static gboolean
is_header(HTMLClueFlow * flow)460 is_header (HTMLClueFlow *flow)
461 {
462 	switch (flow->style) {
463 	case HTML_CLUEFLOW_STYLE_H1:
464 	case HTML_CLUEFLOW_STYLE_H2:
465 	case HTML_CLUEFLOW_STYLE_H3:
466 	case HTML_CLUEFLOW_STYLE_H4:
467 	case HTML_CLUEFLOW_STYLE_H5:
468 	case HTML_CLUEFLOW_STYLE_H6:
469 		return TRUE;
470 	default:
471 		return FALSE;
472 	}
473 }
474 
475 static gboolean
need_blockquote_padding(HTMLClueFlow * flow,HTMLClueFlow * prev)476 need_blockquote_padding (HTMLClueFlow *flow,
477                           HTMLClueFlow *prev)
478 {
479 	gint i = get_similar_depth (flow, prev);
480 
481 	/*
482 	 * If the levels don't match up the the current flow
483 	 * level the padding should be handled the other way.
484 	 */
485 	if (i < flow->levels->len || flow->levels->len == 0) {
486 		if (i < prev->levels->len)
487 			return TRUE;
488 		else
489 			return FALSE;
490 	}
491 
492 	i = prev->levels->len - i;
493 
494 	/*
495 	 * now we check each level greater than the current flow level
496 	 * and see if it is a blockquote and therefore needs padding
497 	 */
498 	while (i > 0) {
499 		HTMLListType type;
500 
501 		type = prev->levels->data[prev->levels->len - i];
502 
503 		if (is_blockquote (type)) {
504 			return TRUE;
505 		}
506 		i--;
507 	}
508 
509 	/*
510 	 * If all the levels were items we don't need padding
511 	 */
512 	return FALSE;
513 }
514 
515 static guint
get_pre_padding(HTMLClueFlow * flow,guint pad)516 get_pre_padding (HTMLClueFlow *flow,
517                  guint pad)
518 {
519 	HTMLObject *prev_object;
520 
521 	prev_object = HTML_OBJECT (flow)->prev;
522 	if (prev_object == NULL)
523 		return 0;
524 
525 	if (HTML_OBJECT_TYPE (prev_object) == HTML_TYPE_CLUEFLOW) {
526 		HTMLClueFlow *prev;
527 
528 		if (get_post_padding (HTML_CLUEFLOW (prev_object), 1))
529 			return 0;
530 
531 		prev = HTML_CLUEFLOW (prev_object);
532 
533 		if (!is_levels_equal (flow, prev)) {
534 			if (need_blockquote_padding (flow, prev))
535 				return pad;
536 			else
537 				return 0;
538 		}
539 
540 		if (flow->style == HTML_CLUEFLOW_STYLE_PRE
541 		    && prev->style != HTML_CLUEFLOW_STYLE_PRE
542 		    && !is_header (prev))
543 			return pad;
544 
545 		if (is_header (flow) && !is_header (prev))
546 			return pad;
547 
548 		return 0;
549 	}
550 
551 	if (!is_header (flow) && flow->levels->len == 0)
552 		return 0;
553 
554 	return pad;
555 }
556 
557 static guint
get_post_padding(HTMLClueFlow * flow,guint pad)558 get_post_padding (HTMLClueFlow *flow,
559                   guint pad)
560 {
561 	HTMLObject *next_object;
562 
563 	next_object = HTML_OBJECT (flow)->next;
564 	if (next_object == NULL)
565 		return 0;
566 
567 	if (HTML_OBJECT_TYPE (next_object) == HTML_TYPE_CLUEFLOW) {
568 		HTMLClueFlow *next;
569 
570 		next = HTML_CLUEFLOW (next_object);
571 
572 		if (!is_levels_equal (next, flow)) {
573 			if (need_blockquote_padding (flow, next))
574 				return pad;
575 			else
576 				return 0;
577 		}
578 
579 		if (flow->style == HTML_CLUEFLOW_STYLE_PRE
580 		    && next->style != HTML_CLUEFLOW_STYLE_PRE
581 		    && !is_header (next))
582 			return pad;
583 
584 		if (is_header (flow))
585 			return pad;
586 
587 		return 0;
588 	}
589 
590 	if (!is_header (flow) && flow->levels->len == 0)
591 		return 0;
592 
593 	return pad;
594 }
595 
596 static void
add_pre_padding(HTMLClueFlow * flow,guint pad)597 add_pre_padding (HTMLClueFlow *flow,
598                  guint pad)
599 {
600 	guint real_pad;
601 
602 	real_pad = get_pre_padding (flow, pad);
603 
604 	HTML_OBJECT (flow)->ascent += real_pad;
605 	HTML_OBJECT (flow)->y += real_pad;
606 }
607 
608 static void
add_post_padding(HTMLClueFlow * flow,guint pad)609 add_post_padding (HTMLClueFlow *flow,
610                   guint pad)
611 {
612 	guint real_pad;
613 
614 	real_pad = get_post_padding (flow, pad);
615 
616 	HTML_OBJECT (flow)->ascent += real_pad;
617 	HTML_OBJECT (flow)->y += real_pad;
618 }
619 
620 static guint
get_level_indent(HTMLClueFlow * flow,gint level,HTMLPainter * painter)621 get_level_indent (HTMLClueFlow *flow,
622                   gint level,
623                   HTMLPainter *painter)
624 {
625 	HTMLDirection dir = html_object_get_direction (HTML_OBJECT (flow));
626 	guint indent = 0;
627 	gint i = 0;
628 
629 	if (flow->levels->len > 0 || !is_item (flow)) {
630 		guint cite_width, indent_width;
631 
632 		cite_width = html_painter_get_block_cite_width (painter, GTK_HTML_FONT_STYLE_SIZE_3, NULL, dir)
633 			+ html_painter_get_space_width (painter, GTK_HTML_FONT_STYLE_SIZE_3, NULL);
634 		indent_width = html_painter_get_block_indent_width (painter, GTK_HTML_FONT_STYLE_SIZE_3, NULL);
635 
636 		while (i <= level) {
637 			switch (flow->levels->data[i]) {
638 			case HTML_LIST_TYPE_BLOCKQUOTE_CITE:
639 				indent += cite_width;
640 				break;
641 			case HTML_LIST_TYPE_GLOSSARY_DL:
642 				indent += 0;
643 				break;
644 			default:
645 				indent += indent_width;
646 				break;
647 			}
648 			i++;
649 		}
650 	} else {
651 		GtkHTMLFontStyle style;
652 		style = html_clueflow_get_default_font_style (flow);
653 
654 		indent = 4 * html_painter_get_space_width (painter, style, NULL);
655 	}
656 
657 	return indent;
658 }
659 
660 static void
set_painter(HTMLObject * o,HTMLPainter * painter)661 set_painter (HTMLObject *o,
662              HTMLPainter *painter)
663 {
664 	HTML_CLUEFLOW (o)->indent_width = -1;
665 }
666 
667 static guint
get_indent(HTMLClueFlow * flow,HTMLPainter * painter)668 get_indent (HTMLClueFlow *flow,
669             HTMLPainter *painter)
670 {
671 	if (flow->indent_width < 0 )
672 		flow->indent_width = get_level_indent (flow, flow->levels->len -1, painter);
673 
674 	return flow->indent_width;
675 }
676 
677 /* HTMLObject methods.  */
678 
679 static void
set_max_width(HTMLObject * o,HTMLPainter * painter,gint max_width)680 set_max_width (HTMLObject *o,
681                HTMLPainter *painter,
682                gint max_width)
683 {
684 	HTMLObject *obj;
685 	guint indent;
686 
687 	o->max_width = max_width;
688 
689 	indent = get_indent (HTML_CLUEFLOW (o), painter);
690 
691 	for (obj = HTML_CLUE (o)->head; obj != 0; obj = obj->next) {
692 		html_object_set_max_width (obj, painter, o->max_width - indent);
693 	}
694 }
695 
696 static gint
calc_min_width(HTMLObject * o,HTMLPainter * painter)697 calc_min_width (HTMLObject *o,
698                 HTMLPainter *painter)
699 {
700 	HTMLObject *cur;
701 	gint min_width = 0;
702 	gint aligned_min_width = 0;
703 	gint w = 0;
704 	gboolean add;
705 
706 	add = HTML_CLUEFLOW (o)->style == HTML_CLUEFLOW_STYLE_PRE && !HTML_IS_PRINTER (painter);
707 
708 	cur = HTML_CLUE (o)->head;
709 	while (cur) {
710 		if (cur->flags & HTML_OBJECT_FLAG_ALIGNED)
711 			aligned_min_width = MAX (aligned_min_width, html_object_calc_min_width (cur, painter));
712 		else {
713 			w += add
714 				? html_object_calc_preferred_width (cur, painter)
715 				: html_object_calc_min_width (cur, painter);
716 			if (!add || !cur->next) {
717 				if (min_width < w) min_width = w;
718 				w = 0;
719 			}
720 		}
721 		cur = cur->next;
722 	}
723 
724 	return MAX (aligned_min_width, min_width) + get_indent (HTML_CLUEFLOW (o), painter);
725 }
726 
727 static gint
pref_left_margin(HTMLPainter * p,HTMLObject * o,gint indent)728 pref_left_margin (HTMLPainter *p,
729                   HTMLObject *o,
730                   gint indent)
731 {
732 	gint margin = html_object_get_left_margin (o->parent, p, o->y, TRUE);
733 
734 	if (html_object_get_direction (o) == HTML_DIRECTION_RTL) {
735 		if ((HTML_CLUEFLOW (o)->style != HTML_CLUEFLOW_STYLE_PRE || HTML_IS_PRINTER (p)) && HTML_IS_PLAIN_PAINTER (p))
736 			return MAX (margin, o->width - (gint)(72 * (MAX (html_painter_get_space_width (p, GTK_HTML_FONT_STYLE_SIZE_3 | GTK_HTML_FONT_STYLE_FIXED, NULL),
737 									html_painter_get_e_width (p, GTK_HTML_FONT_STYLE_SIZE_3 | GTK_HTML_FONT_STYLE_FIXED, NULL)))));
738 	} else {
739 		if (indent > margin)
740 			margin = indent;
741 	}
742 
743 	return margin;
744 }
745 
746 static gint
pref_right_margin(HTMLPainter * p,HTMLObject * o,gint indent)747 pref_right_margin (HTMLPainter *p,
748                    HTMLObject *o,
749                    gint indent)
750 {
751 	gint margin = html_object_get_right_margin (o->parent, p, o->y, TRUE);
752 
753 	if (html_object_get_direction (o) != HTML_DIRECTION_RTL) {
754 		if ((HTML_CLUEFLOW (o)->style != HTML_CLUEFLOW_STYLE_PRE || HTML_IS_PRINTER (p)) && HTML_IS_PLAIN_PAINTER (p))
755 			return MIN (margin, 72 * (MAX (html_painter_get_space_width (p, GTK_HTML_FONT_STYLE_SIZE_3 | GTK_HTML_FONT_STYLE_FIXED, NULL),
756 						       html_painter_get_e_width (p, GTK_HTML_FONT_STYLE_SIZE_3 | GTK_HTML_FONT_STYLE_FIXED, NULL))));
757 	} else {
758 		if (indent > o->width - margin)
759 			margin = o->width - indent;
760 	}
761 
762 	return margin;
763 }
764 
765 static void
add_clear_area(GList ** changed_objs,HTMLObject * o,gint x,gint w)766 add_clear_area (GList **changed_objs,
767                 HTMLObject *o,
768                 gint x,
769                 gint w)
770 {
771 	HTMLObjectClearRectangle *cr;
772 
773 	cr = g_new (HTMLObjectClearRectangle, 1);
774 
775 	cr->object = o;
776 	cr->x = x;
777 	cr->y = 0;
778 	cr->width = w;
779 	cr->height = o->ascent + o->descent;
780 
781 	*changed_objs = g_list_prepend (*changed_objs, cr);
782 	/* NULL means: clear following rectangle */
783 	*changed_objs = g_list_prepend (*changed_objs, NULL);
784 }
785 
786 static void
calc_margins(HTMLObject * o,HTMLPainter * painter,gint indent,gint * lmargin,gint * rmargin)787 calc_margins (HTMLObject *o,
788               HTMLPainter *painter,
789               gint indent,
790               gint *lmargin,
791               gint *rmargin)
792 {
793 	*lmargin = pref_left_margin (painter, o, indent);
794 	*rmargin = pref_right_margin (painter, o, indent);
795 }
796 
797 static inline gint
width_left(HTMLObject * o,gint x,gint rmargin,gboolean printing)798 width_left (HTMLObject *o,
799             gint x,
800             gint rmargin,
801             gboolean printing)
802 {
803 	return (!printing && HTML_CLUEFLOW (o)->style == HTML_CLUEFLOW_STYLE_PRE) ? G_MAXINT : rmargin - x;
804 }
805 
806 static gint
object_nb_width(HTMLObject * o,HTMLPainter * painter,gboolean lineBegin)807 object_nb_width (HTMLObject *o,
808                  HTMLPainter *painter,
809                  gboolean lineBegin)
810 {
811 	if (HTML_IS_TEXT_SLAVE (o))
812 		return html_text_slave_get_nb_width (HTML_TEXT_SLAVE (o), painter, lineBegin);
813 
814 	return html_object_calc_min_width (o, painter);
815 }
816 
817 static inline gboolean
is_top_aligned(HTMLVAlignType valign)818 is_top_aligned (HTMLVAlignType valign)
819 {
820 	return valign == HTML_VALIGN_TOP;
821 }
822 
823 static inline void
update_leafs_children_changed_size(HTMLObject * o,gboolean * leaf_children_changed_size)824 update_leafs_children_changed_size (HTMLObject *o,
825                                     gboolean *leaf_children_changed_size)
826 {
827 	if (o && o->change & HTML_CHANGE_SIZE
828 	    && HTML_OBJECT_TYPE (o) != HTML_TYPE_TEXTSLAVE && !html_object_is_container (o))
829 		*leaf_children_changed_size = TRUE;
830 }
831 
832 static inline void
update_height(HTMLObject * o,HTMLVAlignType valign,gint * a,gint * d,gint * height,gboolean * top)833 update_height (HTMLObject *o,
834                HTMLVAlignType valign,
835                gint *a,
836                gint *d,
837                gint *height,
838                gboolean *top)
839 {
840 	switch (valign) {
841 	case HTML_VALIGN_TOP:
842 		*top = TRUE;
843 		*height = MAX (*height, o->ascent + o->descent);
844 		break;
845 	case HTML_VALIGN_NONE:
846 	case HTML_VALIGN_BOTTOM:
847 		*a = MAX (*a, o->ascent);
848 		*d = MAX (*d, o->descent);
849 		*height = MAX (*height, *a + *d);
850 		break;
851 	case HTML_VALIGN_MIDDLE: {
852 		gint h, h2;
853 
854 		h = o->ascent + o->descent;
855 		h2 = h / 2;
856 		*a = MAX (*a, h2);
857 		*d = MAX (*d, h - h2);
858 		*height = MAX (*height, *a + *d);
859 		break;
860 	}
861 	}
862 }
863 
864 static inline void
update_top_height(HTMLObject * begin,HTMLObject * end,gint * a,gint * d,gint * height)865 update_top_height (HTMLObject *begin,
866                    HTMLObject *end,
867                    gint *a,
868                    gint *d,
869                    gint *height)
870 {
871 	while (begin && begin != end) {
872 		if (html_object_get_valign (begin) == HTML_VALIGN_TOP) {
873 			gint rest = begin->ascent + begin->descent - *a;
874 
875 			if (rest > *d) {
876 				*d = rest;
877 				*height = MAX (*height, *a + *d);
878 			}
879 		}
880 		begin = begin->next;
881 	}
882 }
883 
884 inline HTMLHAlignType
html_clueflow_get_halignment(HTMLClueFlow * flow)885 html_clueflow_get_halignment (HTMLClueFlow *flow)
886 {
887 	g_return_val_if_fail (flow != NULL, HTML_HALIGN_NONE);
888 
889 	if (HTML_CLUE (flow)->halign == HTML_HALIGN_NONE) {
890 		HTMLHAlignType halign;
891 
892 		if (HTML_OBJECT (flow)->parent && HTML_IS_TABLE_CELL (HTML_OBJECT (flow)->parent))
893 			halign = HTML_CLUE (HTML_OBJECT (flow)->parent)->halign == HTML_HALIGN_NONE
894 				? HTML_TABLE_CELL (HTML_OBJECT (flow)->parent)->heading ? HTML_HALIGN_CENTER : HTML_HALIGN_NONE
895 				: HTML_CLUE (HTML_OBJECT (flow)->parent)->halign;
896 		else
897 			halign = HTML_CLUE (HTML_OBJECT (flow)->parent)->halign;
898 
899 		if (halign == HTML_HALIGN_NONE) {
900 			switch (html_object_get_direction (HTML_OBJECT (flow))) {
901 			case HTML_DIRECTION_LTR:
902 				halign = HTML_HALIGN_LEFT;
903 				break;
904 			case HTML_DIRECTION_RTL:
905 				halign = HTML_HALIGN_RIGHT;
906 				break;
907 			default:
908 				break;
909 			}
910 		}
911 
912 		return halign;
913 
914 	} else
915 		return HTML_CLUE (flow)->halign;
916 }
917 
918 static inline void
update_line_positions(HTMLObject * clue,HTMLObject * begin,HTMLObject * end,gint left,gint a,gint d,gint height)919 update_line_positions (HTMLObject *clue,
920                        HTMLObject *begin,
921                        HTMLObject *end,
922                        gint left,
923                        gint a,
924                        gint d,
925                        gint height)
926 {
927 	gint xinc = 0;
928 
929 	switch (html_clueflow_get_halignment (HTML_CLUEFLOW (clue))) {
930 	case HTML_HALIGN_NONE:
931 	case HTML_HALIGN_LEFT:
932 		xinc = 0;
933 		break;
934 	case HTML_HALIGN_CENTER:
935 		xinc = left / 2;
936 		break;
937 	case HTML_HALIGN_RIGHT:
938 		xinc = left;
939 		break;
940 	}
941 
942 	while (begin && begin != end) {
943 		begin->x += xinc;
944 
945 		switch (html_object_get_valign (begin)) {
946 		case HTML_VALIGN_NONE:
947 		case HTML_VALIGN_BOTTOM:
948 			begin->y = clue->ascent + a;
949 			break;
950 		case HTML_VALIGN_MIDDLE:
951 			begin->y = clue->ascent + (height - begin->ascent - begin->descent) / 2 + begin->ascent;
952 			break;
953 		case HTML_VALIGN_TOP:
954 			begin->y = clue->ascent + begin->ascent;
955 			break;
956 		}
957 		begin = begin->next;
958 	}
959 }
960 
961 static HTMLObject *
layout_line(HTMLObject * o,HTMLPainter * painter,HTMLObject * begin,GList ** changed_objs,gboolean * leaf_children_changed_size,gint * lmargin,gint * rmargin,gint indent)962 layout_line (HTMLObject *o,
963              HTMLPainter *painter,
964              HTMLObject *begin,
965              GList **changed_objs,
966              gboolean *leaf_children_changed_size,
967              gint *lmargin,
968              gint *rmargin,
969              gint indent)
970 {
971 	HTMLObject *cur;
972 	gboolean first = TRUE;
973 	gboolean top_align = FALSE;
974 	gboolean need_update_height = FALSE;
975 	gint old_y;
976 	gint x;
977 	gint start_lmargin;
978 	gint a, d, height;
979 	gint nb_width;
980 
981 	if (html_object_is_text (begin)) {
982 		update_leafs_children_changed_size (begin, leaf_children_changed_size);
983 		/* this ever succeeds and creates slaves */
984 		html_object_calc_size (begin, painter, changed_objs);
985 		html_object_fit_line (begin, painter, first, first, FALSE, 0);
986 		begin = begin->next;
987 	}
988 	cur = begin;
989 
990 	old_y = o->y;
991 	if (!HTML_IS_TEXT_SLAVE (begin) || HTML_IS_TEXT (begin->prev))
992 		html_object_calc_size (begin, painter, changed_objs);
993 
994 	a = d = height = 0;
995 	update_height (begin, html_object_get_valign (begin), &a, &d, &height, &top_align);
996 
997 	nb_width = object_nb_width (begin, painter, first);
998 	if (*rmargin - *lmargin < nb_width)
999 		html_clue_find_free_area (HTML_CLUE (o->parent), painter, o->y,
1000 					  nb_width, height,
1001 					  indent, &o->y, lmargin, rmargin);
1002 
1003 	x = start_lmargin = *lmargin;
1004 	o->ascent += o->y - old_y;
1005 
1006 	while (cur && !(cur->flags & HTML_OBJECT_FLAG_ALIGNED)) {
1007 		HTMLFitType fit;
1008 		HTMLVAlignType valign;
1009 
1010 		update_leafs_children_changed_size (cur, leaf_children_changed_size);
1011 
1012 		cur->x = x;
1013 		if (cur != begin)
1014 			html_object_calc_size (cur, painter, changed_objs);
1015 
1016 		valign = html_object_get_valign (cur);
1017 		if ((!is_top_aligned (valign) && (cur->ascent > a || cur->descent > d))
1018 		    || cur->ascent + cur->descent > height) {
1019 			nb_width = object_nb_width (cur, painter, first);
1020 			old_y = o->y;
1021 			html_clue_find_free_area (HTML_CLUE (o->parent), painter, o->y,
1022 						  nb_width, height,
1023 						  indent, &o->y, lmargin, rmargin);
1024 
1025 			/* is there enough space for this object? */
1026 			if ((HTML_IS_PRINTER (painter) || HTML_CLUEFLOW (o)->style != HTML_CLUEFLOW_STYLE_PRE) && o->y != old_y && *rmargin - x < nb_width)
1027 				break;
1028 			need_update_height = TRUE;
1029 		}
1030 
1031 		cur->y = o->ascent + a;
1032 		fit = html_object_fit_line (cur, painter, first, first, FALSE, width_left (o, x, *rmargin, HTML_IS_PRINTER (painter)));
1033 		first = FALSE;
1034 		if (fit == HTML_FIT_NONE)
1035 			break;
1036 
1037 		if (need_update_height)
1038 			update_height (cur, valign, &a, &d, &height, &top_align);
1039 		need_update_height = FALSE;
1040 		x += cur->width;
1041 		cur = cur->next;
1042 
1043 		if (fit == HTML_FIT_PARTIAL)
1044 			break;
1045 	}
1046 
1047 	if (top_align)
1048 		update_top_height (begin, cur, &a, &d, &height);
1049 	update_line_positions (o, begin, cur, MAX (0, *rmargin - start_lmargin - x), a, d, height);
1050 
1051 	o->y += height;
1052 	o->ascent += height;
1053 
1054 	calc_margins (o, painter, indent, lmargin, rmargin);
1055 
1056 	return cur;
1057 }
1058 
1059 static HTMLObject *
layout_aligned(HTMLObject * o,HTMLPainter * painter,HTMLObject * cur,GList ** changed_objs,gboolean * leaf_children_changed_size,gint * lmargin,gint * rmargin,gint indent,gboolean * changed)1060 layout_aligned (HTMLObject *o,
1061                 HTMLPainter *painter,
1062                 HTMLObject *cur,
1063                 GList **changed_objs,
1064                 gboolean *leaf_children_changed_size,
1065                 gint *lmargin,
1066                 gint *rmargin,
1067                 gint indent,
1068                 gboolean *changed)
1069 {
1070 	if (!html_clue_appended (HTML_CLUE (o->parent), HTML_CLUE (cur))) {
1071 		html_object_calc_size (cur, painter, changed_objs);
1072 
1073 		if (HTML_CLUE (cur)->halign == HTML_HALIGN_LEFT)
1074 			html_clue_append_left_aligned (HTML_CLUE (o->parent), painter,
1075 						       HTML_CLUE (cur), lmargin, rmargin, indent);
1076 		else
1077 			html_clue_append_right_aligned (HTML_CLUE (o->parent), painter,
1078 							HTML_CLUE (cur), lmargin, rmargin, indent);
1079 		*changed = TRUE;
1080 	}
1081 
1082 	return cur->next;
1083 }
1084 
1085 static gboolean
html_clue_flow_layout(HTMLObject * o,HTMLPainter * painter,GList ** changed_objs,gboolean * leaf_children_changed_size)1086 html_clue_flow_layout (HTMLObject *o,
1087                        HTMLPainter *painter,
1088                        GList **changed_objs,
1089                        gboolean *leaf_children_changed_size)
1090 {
1091 	HTMLClueFlow *cf = HTML_CLUEFLOW (o);
1092 	HTMLObject *cur = HTML_CLUE (o)->head;
1093 	gint indent, lmargin, rmargin;
1094 	gboolean changed = FALSE;
1095 
1096 	/* prepare margins */
1097 	indent = get_indent (cf, painter);
1098 	calc_margins (o, painter, indent, &lmargin, &rmargin);
1099 
1100 	while (cur) {
1101 		if (cur->flags & HTML_OBJECT_FLAG_ALIGNED)
1102 			cur = layout_aligned (o, painter, cur, changed_objs, leaf_children_changed_size,
1103 					      &lmargin, &rmargin, indent, &changed);
1104 		else
1105 			cur = layout_line (o, painter, cur, changed_objs, leaf_children_changed_size,
1106 					   &lmargin, &rmargin, indent);
1107 	}
1108 
1109 	return changed;
1110 }
1111 
1112 static gboolean
html_clue_flow_real_calc_size(HTMLObject * o,HTMLPainter * painter,GList ** changed_objs)1113 html_clue_flow_real_calc_size (HTMLObject *o,
1114                                HTMLPainter *painter,
1115                                GList **changed_objs)
1116 {
1117 	HTMLClueFlow *cf = HTML_CLUEFLOW (o);
1118 	gint oa, od, ow, padding;
1119 	gboolean leaf_children_changed_size = FALSE;
1120 	gboolean changed, changed_size = FALSE;
1121 
1122 	/* reset size */
1123 	oa = o->ascent;
1124 	od = o->descent;
1125 	ow = o->width;
1126 
1127 	cf->indent_width = -1;
1128 
1129 	o->ascent = 0;
1130 	o->descent = 0;
1131 	o->width = MAX (o->max_width, html_object_calc_min_width (o, painter));
1132 
1133 	/* calc size */
1134 	padding = calc_padding (painter);
1135 	add_pre_padding (cf, padding);
1136 	changed = html_clue_flow_layout (o, painter, changed_objs, &leaf_children_changed_size);
1137 	add_post_padding (cf, padding);
1138 
1139 	/* take care about changes */
1140 	if (o->ascent != oa || o->descent != od || o->width != ow)
1141 		changed = changed_size = TRUE;
1142 
1143 	if (changed_size || leaf_children_changed_size)
1144 		if (changed_objs) {
1145 			if (ow > o->max_width && o->width < ow)
1146 				add_clear_area (changed_objs, o, o->width, ow - o->width);
1147 			html_object_add_to_changed (changed_objs, o);
1148 		}
1149 
1150 	return changed;
1151 }
1152 
1153 static void
set_max_height(HTMLObject * o,HTMLPainter * painter,gint max_height)1154 set_max_height (HTMLObject *o,
1155                 HTMLPainter *painter,
1156                 gint max_height)
1157 {
1158 }
1159 
1160 static HTMLClearType
get_clear(HTMLObject * self)1161 get_clear (HTMLObject *self)
1162 {
1163 	return HTML_CLUEFLOW (self)->clear;
1164 }
1165 
1166 static gint
calc_preferred_width(HTMLObject * o,HTMLPainter * painter)1167 calc_preferred_width (HTMLObject *o,
1168                       HTMLPainter *painter)
1169 {
1170 	HTMLObject *obj, *next;
1171 	gint maxw = 0, w = 0;
1172 
1173 	next = NULL;
1174 
1175 	for (obj = HTML_CLUE (o)->head; obj != 0; obj = obj->next) {
1176 		w += html_object_calc_preferred_width (obj, painter);
1177 
1178 		if (!(next = html_object_next_not_slave (obj))) {
1179 
1180 			/* remove trailing space width on the end of line which is not on end of paragraph */
1181 			if (next && html_object_is_text (obj))
1182 				w -= html_text_trail_space_width (HTML_TEXT (obj), painter);
1183 
1184 			if (w > maxw)
1185 				maxw = w;
1186 			w = 0;
1187 		}
1188 	}
1189 
1190 	return maxw + get_indent (HTML_CLUEFLOW (o), painter);
1191 }
1192 
1193 static gchar *
get_alpha_value(gint value,gboolean lower)1194 get_alpha_value (gint value,
1195                  gboolean lower)
1196 {
1197 	GString *str;
1198 	gchar *rv;
1199 	gint add = lower ? 'a' : 'A';
1200 
1201 	str = g_string_new (". ");
1202 
1203 	do {
1204 		g_string_prepend_c (str, ((value - 1) % 26) + add);
1205 		value = (value - 1) / 26;
1206 	} while (value);
1207 
1208 	rv = str->str;
1209 	g_string_free (str, FALSE);
1210 
1211 	return rv;
1212 }
1213 
1214 #define BASES 7
1215 
1216 static gchar *
get_roman_value(gint value,gboolean lower)1217 get_roman_value (gint value,
1218                  gboolean lower)
1219 {
1220 	GString *str;
1221 	const gchar *base = "IVXLCDM";
1222 	gchar *rv;
1223 	gint b, r, add = lower ? 'a' - 'A' : 0;
1224 
1225 	if (value > 3999)
1226 		return g_strdup ("?. ");
1227 
1228 	str = g_string_new (". ");
1229 
1230 	for (b = 0; value > 0 && b < BASES - 1; b += 2, value /= 10) {
1231 		r = value % 10;
1232 		if (r != 0) {
1233 			if (r < 4) {
1234 				for (; r; r--)
1235 					g_string_prepend_c (str, base[b] + add);
1236 			} else if (r == 4) {
1237 				g_string_prepend_c (str, base[b + 1] + add);
1238 				g_string_prepend_c (str, base[b] + add);
1239 			} else if (r == 5) {
1240 				g_string_prepend_c (str, base[b + 1] + add);
1241 			} else if (r < 9) {
1242 				for (; r > 5; r--)
1243 					g_string_prepend_c (str, base[b] + add);
1244 				g_string_prepend_c (str, base[b + 1] + add);
1245 			} else if (r == 9) {
1246 				g_string_prepend_c (str, base[b + 2] + add);
1247 				g_string_prepend_c (str, base[b] + add);
1248 			}
1249 		}
1250 	}
1251 
1252 	rv = str->str;
1253 	g_string_free (str, FALSE);
1254 
1255 	return rv;
1256 }
1257 
1258 static gchar *
get_item_marker_str(HTMLClueFlow * flow,gboolean ascii_only)1259 get_item_marker_str (HTMLClueFlow *flow,
1260                      gboolean ascii_only)
1261 {
1262 	HTMLListType type = flow->item_type;
1263 
1264 	if (type == HTML_LIST_TYPE_BLOCKQUOTE && flow->levels->len > 0) {
1265 		gint i;
1266 
1267 		for (i = flow->levels->len - 1; i >= 0; i--) {
1268 			if (flow->levels->data[i] != HTML_LIST_TYPE_BLOCKQUOTE) {
1269 				type = flow->levels->data[i];
1270 				break;
1271 			}
1272 		}
1273 	}
1274 
1275 	switch (type) {
1276 	case HTML_LIST_TYPE_ORDERED_ARABIC:
1277 		return g_strdup_printf ("%d. ", flow->item_number);
1278 	case HTML_LIST_TYPE_ORDERED_LOWER_ALPHA:
1279 	case HTML_LIST_TYPE_ORDERED_UPPER_ALPHA:
1280 		return get_alpha_value (flow->item_number, flow->item_type == HTML_LIST_TYPE_ORDERED_LOWER_ALPHA);
1281 	case HTML_LIST_TYPE_ORDERED_LOWER_ROMAN:
1282 	case HTML_LIST_TYPE_ORDERED_UPPER_ROMAN:
1283 		return get_roman_value (flow->item_number, flow->item_type == HTML_LIST_TYPE_ORDERED_LOWER_ROMAN);
1284 	case HTML_LIST_TYPE_CIRCLE:
1285 		return g_strdup ("\342\227\213 "); /* U+25CB WHITE CIRCLE */
1286 	case HTML_LIST_TYPE_DISC:
1287 		return g_strdup ("\342\227\217 "); /* U+25CF BLACK CIRCLE */
1288 	case HTML_LIST_TYPE_SQUARE:
1289 		return g_strdup ("\342\226\240 "); /* U+25AA BLACK SQUARE */
1290 	case HTML_LIST_TYPE_UNORDERED:
1291 		if (ascii_only)
1292 			return g_strdup ("* ");
1293 		else if (flow->levels->len == 0 || flow->levels->len & 1)
1294 			return g_strdup ("\342\227\217 "); /* U+25CF BLACK CIRCLE */
1295 		else
1296 			return g_strdup ("\342\227\213 "); /* U+25CB WHITE CIRCLE */
1297 	default:
1298 		return NULL;
1299 	}
1300 }
1301 
1302 static void
draw_cite_line(HTMLObject * cur,HTMLPainter * p,const gchar * cite_str,gint offset,gint x,gint y)1303 draw_cite_line (HTMLObject *cur,
1304                 HTMLPainter *p,
1305                 const gchar *cite_str,
1306                 gint offset,
1307                 gint x,
1308                 gint y)
1309 {
1310 	gint cy, w, a, d;
1311 
1312 	/* FIXME: cache items and glyphs? */
1313 	html_painter_calc_text_size (p, cite_str, strlen (cite_str),
1314 				     &w, &a, &d);
1315 
1316 	cy = offset;
1317 	while (cy + a <= cur->ascent) {
1318 		/* FIXME: cache items and glyphs? */
1319 		html_painter_draw_text (p, x, y + cur->y - cy, cite_str, 1);
1320 		cy += a + d;
1321 	}
1322 
1323 	cy = - offset + a + d;
1324 	while (cy + d <= cur->descent) {
1325 		/* FIXME: cache items and glyphs? */
1326 		html_painter_draw_text (p, x, y + cur->y + cy, cite_str, 1);
1327 		cy += a + d;
1328 	}
1329 }
1330 
1331 static void
draw_quotes(HTMLObject * self,HTMLPainter * painter,gint x,gint y,gint width,gint height,gint tx,gint ty)1332 draw_quotes (HTMLObject *self,
1333              HTMLPainter *painter,
1334              gint x,
1335              gint y,
1336              gint width,
1337              gint height,
1338              gint tx,
1339              gint ty)
1340 {
1341 	HTMLClueFlow *flow;
1342 	GdkRectangle paint, area, clip;
1343 	gint i;
1344 	gint indent = 0;
1345 	gint last_indent = 0;
1346 	gint pixel_size = html_painter_get_pixel_size (painter);
1347 	gboolean is_plain = HTML_IS_PLAIN_PAINTER (painter);
1348 	HTMLDirection dir = html_object_get_direction (self);
1349 	HTMLEngine *e;
1350 
1351 	if (painter->widget && GTK_IS_HTML (painter->widget))
1352 		e = html_object_engine (self, GTK_HTML (painter->widget)->engine);
1353 	else
1354 		return;
1355 
1356 	flow = HTML_CLUEFLOW (self);
1357 
1358 	for (i = 0; i < flow->levels->len; i++, last_indent = indent) {
1359 		indent = get_level_indent (flow, i, painter);
1360 
1361 		html_painter_set_pen (painter, &html_colorset_get_color_allocated (e->settings->color_set,
1362 										   painter, HTMLLinkColor)->color);
1363 		if (is_cite (flow, i)) {
1364 			if (!is_plain) {
1365 				if (dir == HTML_DIRECTION_RTL)
1366 					area.x = self->x + self->width - indent + 3 * pixel_size;
1367 				else
1368 					area.x = self->x + indent - 5 * pixel_size;
1369 				area.width = 2 * pixel_size;
1370 				area.y = self->y - self->ascent;
1371 				area.height = self->ascent + self->descent;
1372 
1373 				clip.x = x;
1374 				clip.width = width;
1375 				clip.y = y;
1376 				clip.height = height;
1377 
1378 				if (!gdk_rectangle_intersect (&clip, &area, &paint))
1379 					return;
1380 
1381 				html_painter_fill_rect (painter,
1382 							paint.x + tx, paint.y + ty,
1383 							paint.width, paint.height);
1384 			} else {
1385 				HTMLObject *cur = HTML_CLUE (self)->head;
1386 				gint x_pos, baseline = 0;
1387 				const gchar *cite_str = dir == HTML_DIRECTION_RTL ? HTML_BLOCK_CITE_RTL : HTML_BLOCK_CITE_LTR;
1388 
1389 				while (cur) {
1390 					if (cur->y != 0) {
1391 						baseline = cur->y;
1392 						break;
1393 					}
1394 					cur = cur->next;
1395 				}
1396 
1397 				/* draw "> " quote characters in the plain case */
1398 				html_painter_set_font_style (painter, GTK_HTML_FONT_STYLE_SIZE_3 | GTK_HTML_FONT_STYLE_FIXED);
1399 				html_painter_set_font_face  (painter, NULL);
1400 
1401 				if (dir == HTML_DIRECTION_RTL)
1402 					x_pos = self->x + self->width - last_indent;
1403 				else
1404 					x_pos = self->x + last_indent;
1405 
1406 				draw_cite_line (self, painter, cite_str, self->ascent - baseline,
1407 						x_pos + tx, ty);
1408 			}
1409 		}
1410 	}
1411 }
1412 
1413 static void
draw_item(HTMLObject * self,HTMLPainter * painter,gint x,gint y,gint width,gint height,gint tx,gint ty)1414 draw_item (HTMLObject *self,
1415            HTMLPainter *painter,
1416            gint x,
1417            gint y,
1418            gint width,
1419            gint height,
1420            gint tx,
1421            gint ty)
1422 {
1423 	HTMLClueFlow *flow;
1424 	HTMLObject *first;
1425 	gchar *marker;
1426 	HTMLEngine *e;
1427 
1428 	if (painter->widget && GTK_IS_HTML (painter->widget))
1429 		e = html_object_engine (self, GTK_HTML (painter->widget)->engine);
1430 	else
1431 		return;
1432 
1433 	first = HTML_CLUE (self)->head;
1434 	if (html_object_is_text (first) && first->next)
1435 		first = first->next;
1436 
1437 	flow = HTML_CLUEFLOW (self);
1438 
1439 	if (flow->item_color) {
1440 		html_color_alloc (flow->item_color, painter);
1441 		html_painter_set_pen (painter, &flow->item_color->color);
1442 	} else
1443 		html_painter_set_pen (painter, &html_colorset_get_color_allocated (e->settings->color_set,
1444 										   painter, HTMLTextColor)->color);
1445 
1446 	marker = get_item_marker_str (flow, HTML_IS_PLAIN_PAINTER (painter));
1447 	if (marker) {
1448 		gint len, asc, dsc, x_pos, space_width;
1449 
1450 		len   = g_utf8_strlen (marker, -1);
1451 		html_painter_set_font_style (painter, html_clueflow_get_default_font_style (flow));
1452 		html_painter_set_font_face  (painter, NULL);
1453 		/* FIXME: cache items and glyphs? */
1454 		html_painter_calc_text_size (painter, marker, len, &width, &asc, &dsc);
1455 		space_width = html_painter_get_space_width (painter, html_clueflow_get_default_font_style (flow), NULL);
1456 		/* FIXME: cache items and glyphs? */
1457 		if (html_object_get_direction (self) == HTML_DIRECTION_RTL)
1458 			x_pos = self->x + first->x + first->width + space_width;
1459 		else
1460 			x_pos = self->x + first->x - width - space_width;
1461 		html_painter_draw_text (painter, x_pos + tx,
1462 					self->y - self->ascent + first->y + ty,
1463 					marker, len);
1464 	}
1465 	g_free (marker);
1466 }
1467 
1468 static void
draw(HTMLObject * self,HTMLPainter * painter,gint x,gint y,gint width,gint height,gint tx,gint ty)1469 draw (HTMLObject *self,
1470       HTMLPainter *painter,
1471       gint x,
1472       gint y,
1473       gint width,
1474       gint height,
1475       gint tx,
1476       gint ty)
1477 {
1478 	if (y > self->y + self->descent || y + height < self->y - self->ascent)
1479 		return;
1480 
1481 	if (HTML_CLUE (self)->head != NULL && is_item (HTML_CLUEFLOW (self)))
1482 		draw_item (self, painter, x, y, width, height, tx, ty);
1483 
1484 	if (HTML_CLUE (self)->head != NULL)
1485 		draw_quotes (self, painter, x, y, width, height, tx, ty);
1486 
1487 	(* HTML_OBJECT_CLASS (&html_clue_class)->draw) (self, painter, x, y, width, height, tx, ty);
1488 }
1489 
1490 static HTMLObject *
check_point(HTMLObject * self,HTMLPainter * painter,gint x,gint y,guint * offset_return,gboolean for_cursor)1491 check_point (HTMLObject *self,
1492              HTMLPainter *painter,
1493              gint x,
1494              gint y,
1495              guint *offset_return,
1496              gboolean for_cursor)
1497 {
1498 	HTMLObject *obj, *p, *pnext, *eol, *cur;
1499 	HTMLClue *clue;
1500 	gint line_ly = 0;
1501 	gint line_y;
1502 
1503 	if (x < self->x || x >= self->x + self->width
1504 	    || y < self->y - self->ascent || y >= self->y + self->descent)
1505 		return NULL;
1506 
1507 	clue = HTML_CLUE (self);
1508 
1509 	x = x - self->x;
1510 	y = y - self->y + self->ascent;
1511 
1512 	/*
1513 	 * shift any selection inside the indent block to the
1514 	 * left edge of the flow.
1515 	 */
1516 	if (for_cursor)
1517 		x = MAX (x, get_indent (HTML_CLUEFLOW (self), painter));
1518 
1519 	for (p = clue->head; p;) {
1520 
1521 		if (html_object_is_text (p))
1522 			p = p->next;
1523 		if (!p)
1524 			break;
1525 
1526 		line_y  = line_ly;
1527 		line_ly = p->y + p->descent;
1528 
1529 		for (eol = p; eol && (eol->y - eol->ascent < line_ly || eol->ascent + eol->y == line_y);) {
1530 			line_ly = MAX (line_ly, eol->y + eol->descent);
1531 			do
1532 				eol = eol->next;
1533 			while (eol && html_object_is_text (eol));
1534 		}
1535 
1536 		if (y >= line_y && y < line_ly)
1537 			for (cur = p; cur && cur != eol;) {
1538 				obj = html_object_check_point (cur, painter, x, for_cursor
1539 							       ? MIN (MAX (y, cur->y - cur->ascent),
1540 								      cur->y + cur->descent - 1) : y,
1541 							       offset_return, for_cursor);
1542 
1543 				if (obj != NULL)
1544 					return obj;
1545 				do
1546 					cur = cur->next;
1547 				while (cur && cur != eol && html_object_is_text (cur));
1548 			}
1549 		p = eol;
1550 	}
1551 
1552 	if (!for_cursor)
1553 		return NULL;
1554 
1555 	/* before */
1556 	p = clue->head;
1557 	if (p && html_object_is_text (p))
1558 		p = p->next;
1559 	if (p && (x < p->x || y < p->y - p->ascent)) {
1560 		obj = html_object_check_point (p, painter, p->x, p->y - p->ascent, offset_return, for_cursor);
1561 		if (obj != NULL)
1562 			return obj;
1563 	}
1564 	for (p = clue->head; p != NULL; p = pnext) {
1565 		if (html_object_is_text (p))
1566 			p = p->next;
1567 		if (!p)
1568 			break;
1569 		pnext = p->next;
1570 		while (pnext && html_object_is_text (pnext))
1571 			pnext = pnext->next;
1572 
1573 		/* new line */
1574 		if (pnext == NULL || (pnext->y - pnext->ascent >= p->y + p->descent
1575 				      && y >= p->y - p->ascent
1576 				      && y <  p->y + p->descent)) {
1577 			obj = html_object_check_point (p, painter, MAX (0, p->x + p->width - 1), p->y + p->descent - 1,
1578 						       offset_return, for_cursor);
1579 			if (obj != NULL)
1580 				return obj;
1581 		}
1582 	}
1583 
1584 	/* after */
1585 	p = clue->tail;
1586 	if (p && ((x >= p->x + p->width) || (y >= p->y + p->descent))) {
1587 		obj = html_object_check_point (p, painter, MAX (0, p->x + p->width - 1), p->y + p->descent - 1,
1588 					       offset_return, for_cursor);
1589 		if (obj != NULL)
1590 			return obj;
1591 	}
1592 	return NULL;
1593 }
1594 
1595 
1596 /* Saving support.  */
1597 
1598 static gboolean
write_indent(HTMLEngineSaveState * state,gint level)1599 write_indent (HTMLEngineSaveState *state,
1600               gint level)
1601 {
1602 	while (level > 0) {
1603 		if (!html_engine_save_output_string (state, "    "))
1604 			return FALSE;
1605 		level--;
1606 	}
1607 
1608 	return TRUE;
1609 }
1610 
1611 static gchar *
get_list_start_tag(HTMLClueFlow * self)1612 get_list_start_tag (HTMLClueFlow *self)
1613 {
1614 	switch (self->item_type) {
1615 	case HTML_LIST_TYPE_UNORDERED:
1616 	case HTML_LIST_TYPE_MENU:
1617 	case HTML_LIST_TYPE_DIR:
1618 		return g_strdup ("LI");
1619 	case HTML_LIST_TYPE_ORDERED_ARABIC:
1620 		return g_strdup_printf ("LI TYPE=1 VALUE=%d", HTML_CLUEFLOW (self)->item_number);
1621 	case HTML_LIST_TYPE_ORDERED_UPPER_ROMAN:
1622 		return g_strdup_printf ("LI TYPE=I VALUE=%d", HTML_CLUEFLOW (self)->item_number);
1623 	case HTML_LIST_TYPE_ORDERED_LOWER_ROMAN:
1624 		return g_strdup_printf ("LI TYPE=i VALUE=%d", HTML_CLUEFLOW (self)->item_number);
1625 	case HTML_LIST_TYPE_ORDERED_UPPER_ALPHA:
1626 		return g_strdup_printf ("LI TYPE=A VALUE=%d", HTML_CLUEFLOW (self)->item_number);
1627 	case HTML_LIST_TYPE_ORDERED_LOWER_ALPHA:
1628 		return g_strdup_printf ("LI TYPE=a VALUE=%d", HTML_CLUEFLOW (self)->item_number);
1629 	case HTML_LIST_TYPE_GLOSSARY_DL:
1630 		return g_strdup_printf ("DT");
1631 	case HTML_LIST_TYPE_GLOSSARY_DD:
1632 		return g_strdup_printf ("DD");
1633 	default:
1634 		return NULL;
1635 	}
1636 
1637 	return NULL;
1638 }
1639 
1640 static const gchar *
get_start_tag(HTMLClueFlow * self)1641 get_start_tag (HTMLClueFlow *self)
1642 {
1643 	switch (self->style) {
1644 	case HTML_CLUEFLOW_STYLE_H1:
1645 		return "H1";
1646 	case HTML_CLUEFLOW_STYLE_H2:
1647 		return "H2";
1648 	case HTML_CLUEFLOW_STYLE_H3:
1649 		return "H3";
1650 	case HTML_CLUEFLOW_STYLE_H4:
1651 		return "H4";
1652 	case HTML_CLUEFLOW_STYLE_H5:
1653 		return "H5";
1654 	case HTML_CLUEFLOW_STYLE_H6:
1655 		return "H6";
1656 	case HTML_CLUEFLOW_STYLE_ADDRESS:
1657 		return "ADDRESS";
1658 	case HTML_CLUEFLOW_STYLE_PRE:
1659 		return "PRE";
1660 	case HTML_CLUEFLOW_STYLE_LIST_ITEM:
1661 		g_warning ("This should not be reached");
1662 	case HTML_CLUEFLOW_STYLE_NORMAL:
1663 	default:
1664 		return NULL;
1665 	}
1666 }
1667 
1668 static const gchar *
get_start_indent_item(HTMLListType type)1669 get_start_indent_item (HTMLListType type)
1670 {
1671 	switch (type) {
1672 	case HTML_LIST_TYPE_UNORDERED:
1673 	case HTML_LIST_TYPE_MENU:
1674 	case HTML_LIST_TYPE_DIR:
1675 		return "UL";
1676 	case HTML_LIST_TYPE_ORDERED_LOWER_ALPHA:
1677 		return "OL TYPE=a";
1678 	case HTML_LIST_TYPE_ORDERED_UPPER_ALPHA:
1679 		return "OL TYPE=A";
1680 	case HTML_LIST_TYPE_ORDERED_LOWER_ROMAN:
1681 		return "OL TYPE=i";
1682 	case HTML_LIST_TYPE_ORDERED_UPPER_ROMAN:
1683 	return "OL TYPE=I";
1684 	case HTML_LIST_TYPE_ORDERED_ARABIC:
1685 		return "OL TYPE=1";
1686 	case HTML_LIST_TYPE_GLOSSARY_DD:
1687 	case HTML_LIST_TYPE_GLOSSARY_DL:
1688 		return "DL";
1689 	case HTML_LIST_TYPE_BLOCKQUOTE_CITE:
1690 		return "BLOCKQUOTE TYPE=CITE";
1691 	case HTML_LIST_TYPE_BLOCKQUOTE:
1692 		return "BLOCKQUOTE";
1693 	case HTML_LIST_TYPE_CIRCLE:
1694 		return "OL TYPE=CIRCLE";
1695 	case HTML_LIST_TYPE_DISC:
1696 		return "OL TYPE=DISC";
1697 	case HTML_LIST_TYPE_SQUARE:
1698 		return "OL TYPE=SQUARE";
1699 	}
1700 	return "";
1701 }
1702 
1703 static const gchar *
get_end_indent_item(HTMLListType type)1704 get_end_indent_item (HTMLListType type)
1705 {
1706 	switch (type) {
1707 	case HTML_LIST_TYPE_BLOCKQUOTE:
1708 	case HTML_LIST_TYPE_BLOCKQUOTE_CITE:
1709 		return "BLOCKQUOTE";
1710 	case HTML_LIST_TYPE_GLOSSARY_DD:
1711 	case HTML_LIST_TYPE_GLOSSARY_DL:
1712 		return "DL";
1713 	case HTML_LIST_TYPE_UNORDERED:
1714 	case HTML_LIST_TYPE_MENU:
1715 	case HTML_LIST_TYPE_DIR:
1716 		return "UL";
1717 	default:
1718 		return "OL";
1719 	}
1720 	return "";
1721 }
1722 
1723 static gint
get_similar_depth(HTMLClueFlow * self,HTMLClueFlow * neighbor)1724 get_similar_depth (HTMLClueFlow *self,
1725                    HTMLClueFlow *neighbor)
1726 {
1727 	gint i;
1728 	gint max_depth;
1729 
1730 	if (neighbor == NULL)
1731 		return 0;
1732 
1733 	max_depth = MIN (self->levels->len, neighbor->levels->len);
1734 
1735 	for (i = 0; i < max_depth; i++) {
1736 		if (self->levels->data[i] != neighbor->levels->data[i])
1737 			break;
1738 	}
1739 
1740 	return i;
1741 }
1742 
1743 static gboolean
save_indent_string(HTMLClueFlow * self,HTMLEngineSaveState * state,const gchar * format,...)1744 save_indent_string (HTMLClueFlow *self,
1745                     HTMLEngineSaveState *state,
1746                     const gchar *format,
1747                     ...)
1748 {
1749 	va_list args;
1750 	gboolean retval;
1751 
1752 	if (self->style != HTML_CLUEFLOW_STYLE_PRE)
1753 		if (!write_indent (state, self->levels->len))
1754 			return FALSE;
1755 
1756 	va_start (args, format);
1757 	retval = html_engine_save_output_stringv (state, format, args);
1758 	va_end (args);
1759 
1760 	return retval;
1761 }
1762 
1763 static const gchar *
get_p_str(HTMLClueFlow * self,HTMLEngineSaveState * state)1764 get_p_str (HTMLClueFlow *self,
1765            HTMLEngineSaveState *state)
1766 {
1767 	const gchar *p_str = NULL;
1768 
1769 	if (self->dir != html_object_get_direction (state->engine->clue)) {
1770 		switch (self->dir) {
1771 		case HTML_DIRECTION_RTL:
1772 			p_str = "<P DIR=RTL>\n";
1773 			break;
1774 		case HTML_DIRECTION_LTR:
1775 			if (html_object_get_direction (state->engine->clue) != HTML_DIRECTION_DERIVED)
1776 				p_str = "<P DIR=LTR>\n";
1777 			break;
1778 		default:
1779 			;
1780 		}
1781 	}
1782 
1783 	return p_str;
1784 }
1785 
1786 static gboolean
write_flow_tag(HTMLClueFlow * self,HTMLEngineSaveState * state)1787 write_flow_tag (HTMLClueFlow *self,
1788                 HTMLEngineSaveState *state)
1789 {
1790 	HTMLClueFlow *next = NULL;
1791 	HTMLClueFlow *prev = NULL;
1792 	HTMLHAlignType halign;
1793 	const gchar *br_str = "<BR>\n";
1794 	gboolean has_div_tag = FALSE;
1795 
1796 	if (HTML_IS_CLUEFLOW (HTML_OBJECT (self)->next))
1797 		next = HTML_CLUEFLOW (HTML_OBJECT (self)->next);
1798 
1799 	if (HTML_IS_CLUEFLOW (HTML_OBJECT (self)->prev))
1800 		prev = HTML_CLUEFLOW (HTML_OBJECT (self)->prev);
1801 
1802 	if (next && next->dir != html_object_get_direction (state->engine->clue)) {
1803 		switch (next->dir) {
1804 		case HTML_DIRECTION_RTL:
1805 			br_str = "<BR DIR=RTL>\n";
1806 			break;
1807 		case HTML_DIRECTION_LTR:
1808 			if (html_object_get_direction (state->engine->clue) != HTML_DIRECTION_DERIVED)
1809 				br_str = "<BR DIR=LTR>\n";
1810 			break;
1811 		default:
1812 			;
1813 		}
1814 	}
1815 
1816 	if (!prev) {
1817 		const gchar *p_str = get_p_str (self, state);
1818 
1819 		if (p_str) {
1820 			if (!html_engine_save_output_string (state, "%s", p_str))
1821 				return FALSE;
1822 		}
1823 	}
1824 
1825 	if (is_item (self)) {
1826 		gchar *li = get_list_start_tag (self);
1827 
1828 		if (li && !save_indent_string (self, state, "<%s>", li)) {
1829 			g_free (li);
1830 			return FALSE;
1831 		}
1832 	} else if (is_levels_equal (self, prev) && prev->style == self->style) {
1833 		if (!save_indent_string (self, state, ""))
1834 			return FALSE;
1835 	} else {
1836 		const gchar *start = get_start_tag (self);
1837 
1838 		if (start) {
1839 			if (!save_indent_string (self, state, "<%s>\n", start))
1840 				return FALSE;
1841 		} else {
1842 			if (!save_indent_string (self, state, ""))
1843 				return FALSE;
1844 		}
1845 	}
1846 
1847 	halign = HTML_CLUE (self)->halign;
1848 	/* Alignment tag.  */
1849 	has_div_tag = halign != HTML_HALIGN_NONE && halign != HTML_HALIGN_LEFT;
1850 	if (has_div_tag) {
1851 		if (!html_engine_save_output_string
1852 		    (state, "<DIV ALIGN=%s>",
1853 		     html_engine_save_get_paragraph_align (html_alignment_to_paragraph (halign))))
1854 			return FALSE;
1855 	}
1856 
1857 	if (!html_object_save_data (HTML_OBJECT (self), state))
1858 		return FALSE;
1859 
1860 	/* Paragraph's content.  */
1861 	if (!HTML_OBJECT_CLASS (&html_clue_class)->save (HTML_OBJECT (self), state))
1862 		return FALSE;
1863 
1864 	/* Close alignment tag.  */
1865 	if (has_div_tag) {
1866 		if (!html_engine_save_output_string (state, "</DIV>"))
1867 			return FALSE;
1868 	}
1869 
1870 	if (is_item (self)) {
1871 		if (!has_div_tag && next && is_levels_equal (self, next) && !is_item (next) && !html_clueflow_contains_table (self)) {
1872 			if (!html_engine_save_output_string (state, "%s", br_str))
1873 				return FALSE;
1874 		} else if (!html_engine_save_output_string (state, "\n"))
1875 			return FALSE;
1876 	} else if (is_levels_equal (self, next) && self->style == next->style) {
1877 		if (!has_div_tag && self->style != HTML_CLUEFLOW_STYLE_PRE && !html_clueflow_contains_table (self)) {
1878 			if (!html_engine_save_output_string (state, "%s", br_str))
1879 				return FALSE;
1880 		} else {
1881 			if (!html_engine_save_output_string (state, "\n"))
1882 				return FALSE;
1883 		}
1884 	} else {
1885 		const gchar *end = get_start_tag (self);
1886 
1887 		if (self->style != HTML_CLUEFLOW_STYLE_PRE) {
1888 			if (!has_div_tag && ((!html_clueflow_contains_table (self) && !end && next && self->style == next->style) || html_clueflow_is_empty (self))) {
1889 				if (!html_engine_save_output_string (state, "%s", br_str))
1890 					return FALSE;
1891 			} else {
1892 				if (!html_engine_save_output_string (state, "\n"))
1893 					return FALSE;
1894 			}
1895 		} else {
1896 			if (!html_engine_save_output_string (state, "\n"))
1897 				return FALSE;
1898 		}
1899 
1900 		if (end) {
1901 			if (!html_engine_save_output_string (state, "</%s>\n", end))
1902 				return FALSE;
1903 		}
1904 	}
1905 
1906 	if (!next) {
1907 		if (HTML_OBJECT (self)->parent && html_object_is_clue (HTML_OBJECT (self)->parent)) {
1908 			HTMLObject *head = HTML_CLUE (HTML_OBJECT (self)->parent)->head;
1909 
1910 			if (head && HTML_IS_CLUEFLOW (head)) {
1911 				const gchar *head_p_str = get_p_str (HTML_CLUEFLOW (head), state);
1912 
1913 				if (head_p_str) {
1914 					if (!html_engine_save_output_string (state, "</P>\n"))
1915 						return FALSE;
1916 				}
1917 			}
1918 		}
1919 	}
1920 
1921 	return TRUE;
1922 }
1923 
1924 static gboolean
save(HTMLObject * s,HTMLEngineSaveState * state)1925 save (HTMLObject *s,
1926       HTMLEngineSaveState *state)
1927 {
1928 	HTMLClueFlow *self = HTML_CLUEFLOW (s);
1929 	HTMLClueFlow *next = NULL;
1930 	HTMLClueFlow *prev = NULL;
1931 	gint d;
1932 	gint i;
1933 
1934 	if (HTML_IS_CLUEFLOW (HTML_OBJECT (self)->next))
1935 		next = HTML_CLUEFLOW (HTML_OBJECT (self)->next);
1936 
1937 	if (HTML_IS_CLUEFLOW (HTML_OBJECT (self)->prev))
1938 		prev = HTML_CLUEFLOW (HTML_OBJECT (self)->prev);
1939 
1940 	i = get_similar_depth (self, prev);
1941 	while (i < self->levels->len) {
1942 		const gchar *stag = get_start_indent_item (self->levels->data[i]);
1943 
1944 		if (!write_indent (state, i)
1945 		    || !html_engine_save_output_string (state, "<%s>\n", stag))
1946 			return FALSE;
1947 
1948 		i++;
1949 	}
1950 
1951 	if (!write_flow_tag (self, state))
1952 	    return FALSE;
1953 
1954 	i = self->levels->len - 1;
1955 	d = get_similar_depth (self, next);
1956 	while (i >= d) {
1957 		const gchar *stag = get_end_indent_item (self->levels->data[i]);
1958 
1959 		if (!write_indent (state, i)
1960 		    || !html_engine_save_output_string (state, "</%s>\n", stag))
1961 			return FALSE;
1962 
1963 		i--;
1964 	}
1965 
1966 	return TRUE;
1967 }
1968 
1969 static void
write_item_marker(GString * pad_string,HTMLClueFlow * flow)1970 write_item_marker (GString *pad_string,
1971                    HTMLClueFlow *flow)
1972 {
1973 	gchar *marker;
1974 
1975 	marker = get_item_marker_str (flow, TRUE);
1976 
1977 	if (marker) {
1978 		gint marker_len = strlen (marker);
1979 		gint len = pad_string->len - 1;
1980 		gchar *str = pad_string->str;
1981 
1982 		while (len > 0) {
1983 			if ((str[len - 1] != ' ') || (pad_string->len - len >= marker_len))
1984 				break;
1985 			else
1986 				len--;
1987 		}
1988 
1989 		if (len > 0)
1990 			g_string_truncate (pad_string, len);
1991 
1992 		g_string_append (pad_string, marker);
1993 		g_free (marker);
1994 	}
1995 }
1996 
1997 static gint
plain_padding(HTMLClueFlow * flow,GString * out,gboolean firstline)1998 plain_padding (HTMLClueFlow *flow,
1999                GString *out,
2000                gboolean firstline)
2001 {
2002 	GString *pad_string = NULL;
2003 	gint pad_len = 0;
2004 	gint i;
2005 
2006 	pad_string = g_string_new ("");
2007 
2008 #define APPEND_PLAIN(w) \
2009 	pad_len += strlen (w); \
2010 	if (out) g_string_append (pad_string, w);
2011 
2012 	if (flow->levels->len) {
2013 		for (i = 0; i < flow->levels->len; i++) {
2014 			switch (flow->levels->data[i]) {
2015 			case HTML_LIST_TYPE_BLOCKQUOTE_CITE:
2016 				APPEND_PLAIN (html_object_get_direction (HTML_OBJECT (flow)) == HTML_DIRECTION_RTL ? (" " HTML_BLOCK_CITE_RTL) : (HTML_BLOCK_CITE_LTR " "));
2017 				break;
2018 			case HTML_LIST_TYPE_GLOSSARY_DL:
2019 				break;
2020 			default:
2021 				APPEND_PLAIN (HTML_BLOCK_INDENT);
2022 				break;
2023 			}
2024 		}
2025 	} else if (is_item (flow)) {
2026 		/* item without a list block give it a little pading */
2027 		APPEND_PLAIN ("    ");
2028 	}
2029 
2030 	if (is_item (flow) && firstline) {
2031 		write_item_marker (pad_string, flow);
2032 	}
2033 
2034 	if (out)
2035 		g_string_append (out, pad_string->str);
2036 
2037 	g_string_free (pad_string, TRUE);
2038 	return pad_len;
2039 }
2040 
2041 static void
append_selection_string(HTMLObject * self,GString * buffer)2042 append_selection_string (HTMLObject *self,
2043                          GString *buffer)
2044 {
2045 	(*HTML_OBJECT_CLASS (parent_class)->append_selection_string) (self, buffer);
2046 
2047 	if (self->selected) {
2048 		g_string_append_c (buffer, '\n');
2049 		plain_padding (HTML_CLUEFLOW (self), buffer, TRUE);
2050 	}
2051 }
2052 
2053 /*
2054  * Calculate given UTF-8 string's "cell width", determined by
2055  * g_unichar_iswide() on each character.  (So it doesn't work on
2056  * zero-width or combined characters.)
2057  */
2058 static gint
utf8_width(const gchar * str,gint len)2059 utf8_width (const gchar *str,
2060             gint len)
2061 {
2062 	gunichar c;
2063 	gint width = 0;
2064 
2065 	while (len--) {
2066 		c = g_utf8_get_char (str);
2067 		width += g_unichar_iswide (c) ? 2 : 1;
2068 		str = g_utf8_next_char (str);
2069 	}
2070 	return width;
2071 }
2072 
2073 /*
2074  * Returns the length of string starting on the given string which is
2075  * not exceeding the given width.
2076  */
2077 static gint
utf8_length_in_width(const gchar * str,gint len,gint width)2078 utf8_length_in_width (const gchar *str,
2079                       gint len,
2080                       gint width)
2081 {
2082 	gunichar c;
2083 	gint l = 0;
2084 
2085 	while (len--) {
2086 		c = g_utf8_get_char (str);
2087 		width -= g_unichar_iswide (c) ? 2 : 1;
2088 		if (width < 0)
2089 			break;
2090 		str = g_utf8_next_char (str);
2091 		l++;
2092 	}
2093 
2094 	return l;
2095 }
2096 
2097 static gboolean
save_plain(HTMLObject * self,HTMLEngineSaveState * state,gint requested_width)2098 save_plain (HTMLObject *self,
2099             HTMLEngineSaveState *state,
2100             gint requested_width)
2101 {
2102 	HTMLClueFlow *flow;
2103 	HTMLEngineSaveState *buffer_state;
2104 	HTMLDirection dir = html_object_get_direction (self);
2105 	GString *out = g_string_new ("");
2106 	gint pad;
2107 	gint align_pad;
2108 	gboolean firstline = TRUE;
2109 	gint max_width;
2110 
2111 	flow = HTML_CLUEFLOW (self);
2112 
2113 	pad = plain_padding (flow, NULL, FALSE);
2114 	buffer_state = html_engine_save_buffer_new (state->engine,
2115 						    state->inline_frames);
2116 	max_width = MAX (requested_width - pad, 0);
2117 	/* buffer the paragraph's content into the save buffer */
2118 	if (HTML_OBJECT_CLASS (&html_clue_class)->save_plain (self,
2119 							      buffer_state,
2120 							      max_width)) {
2121 		guchar *s;
2122 		gint offset;
2123 
2124 		if (get_pre_padding (flow, calc_padding (state->engine->painter)) > 0) {
2125 			plain_padding (flow, out, FALSE);
2126 			g_string_append (out, "\n");
2127 		}
2128 
2129 		s = html_engine_save_buffer_peek_text (buffer_state);
2130 
2131 		if (*s == 0) {
2132 			plain_padding (flow, out, TRUE);
2133 			g_string_append (out, "\n");
2134 		} else {
2135 			PangoAttrList *attrs = pango_attr_list_new ();
2136 			gint bytes = html_engine_save_buffer_peek_text_bytes (buffer_state), slen = g_utf8_strlen ((gchar *) s, -1), i, clen, n_items;
2137 			GList *items_list, *cur;
2138 			PangoContext *pc = state->engine->painter->pango_context;
2139 			PangoLogAttr *lattrs;
2140 			PangoItem **items;
2141 			gint len, width, skip;
2142 
2143 			items_list = pango_itemize (pc, (gchar *) s, 0, bytes, attrs, NULL);
2144 			lattrs = g_new (PangoLogAttr, slen + 1);
2145 			n_items = g_list_length (items_list);
2146 			items = g_new (PangoItem *, n_items);
2147 			for (i = 0, cur = items_list; i < n_items; i++, cur = cur->next)
2148 				items[i] = (PangoItem *) cur->data;
2149 
2150 			offset = 0;
2151 			for (i = 0; i < n_items; i++) {
2152 				PangoItem tmp_item;
2153 				gint start_offset;
2154 
2155 				start_offset = offset;
2156 				offset += items[i]->num_chars;
2157 				tmp_item = *items[i];
2158 				while (i < n_items - 1) {
2159 					if (tmp_item.analysis.lang_engine == items[i + 1]->analysis.lang_engine) {
2160 						tmp_item.length += items[i + 1]->length;
2161 						tmp_item.num_chars += items[i + 1]->num_chars;
2162 						offset += items[i + 1]->num_chars;
2163 						i++;
2164 					} else
2165 						break;
2166 				}
2167 
2168 				pango_break ((gchar *) s + tmp_item.offset, tmp_item.length, &tmp_item.analysis, lattrs + start_offset, tmp_item.num_chars + 1);
2169 			}
2170 
2171 			html_text_remove_unwanted_line_breaks ((gchar *) s, slen, lattrs);
2172 
2173 			g_list_free (items_list);
2174 			for (i = 0; i < n_items; i++)
2175 				pango_item_free (items[i]);
2176 			g_free (items);
2177 			pango_attr_list_unref (attrs);
2178 
2179 			clen = 0;
2180 			while (*s) {
2181 				len = strcspn ((gchar *) s, "\n");
2182 				len = g_utf8_strlen ((gchar *) s, len);
2183 				width = utf8_width ((gchar *) s, len);
2184 				skip = 0;
2185 
2186 				if ((flow->style != HTML_CLUEFLOW_STYLE_PRE)
2187 				    && !HTML_IS_TABLE (HTML_CLUE (flow)->head)) {
2188 					if (width > max_width) {
2189 						gboolean look_backward = TRUE;
2190 						gint wmax;
2191 						gint wi, wl;
2192 
2193 						wmax = clen + utf8_length_in_width ((gchar *) s, len, max_width);
2194 						wl = wmax;
2195 
2196 						if (lattrs[wl].is_white) {
2197 
2198 							while (lattrs[wl].is_white && wl < slen)
2199 								wl++;
2200 
2201 							if (wl < slen && html_text_is_line_break (lattrs[wl]))
2202 								look_backward = FALSE;
2203 							else
2204 								wl = wmax;
2205 						}
2206 
2207 						if (look_backward) {
2208 							while (wl > 0) {
2209 								if (html_text_is_line_break (lattrs[wl]))
2210 									break;
2211 								wl--;
2212 							}
2213 						}
2214 
2215 						if (wl > clen && wl < slen && html_text_is_line_break (lattrs[wl])) {
2216 							wi = MIN (wl, wmax);
2217 							while (wi > clen && lattrs[wi - 1].is_white)
2218 								wi--;
2219 							len = wi - clen;
2220 							width = utf8_width ((gchar *) s, len);
2221 							skip = wl - wi;
2222 						}
2223 					}
2224 				}
2225 
2226 				/* FIXME plain padding doesn't work properly with tables aligment
2227 				 * at the moment.
2228 				 */
2229 				plain_padding (flow, out, firstline);
2230 
2231 				switch (html_clueflow_get_halignment (flow)) {
2232 				case HTML_HALIGN_RIGHT:
2233 					if (dir != HTML_DIRECTION_RTL)
2234 						align_pad = max_width - width;
2235 					else
2236 						align_pad = 0;
2237 					break;
2238 				case HTML_HALIGN_CENTER:
2239 					align_pad = (max_width - width) / 2;
2240 					break;
2241 				default:
2242 					if (dir != HTML_DIRECTION_RTL)
2243 						align_pad = 0;
2244 					else
2245 						align_pad = max_width - width;
2246 					break;
2247 				}
2248 
2249 				while (align_pad > 0) {
2250 					g_string_append_c (out, ' ');
2251 					align_pad--;
2252 				}
2253 
2254 				bytes = ((guchar *) g_utf8_offset_to_pointer ((gchar *) s, len)) - s;
2255 				html_engine_save_string_append_nonbsp (out, s, bytes);
2256 				s += bytes;
2257 				s = (guchar *) g_utf8_offset_to_pointer ((gchar *) s, skip);
2258 				clen += len + skip;
2259 
2260 				if (*s == '\n') {
2261 					s++;
2262 					clen++;
2263 				}
2264 
2265 				g_string_append_c (out, '\n');
2266 				firstline = FALSE;
2267 			}
2268 			g_free (lattrs);
2269 		}
2270 
2271 		if (get_post_padding (flow, calc_padding (state->engine->painter)) > 0) {
2272 			plain_padding (flow, out, FALSE);
2273 			g_string_append (out, "\n");
2274 		}
2275 	}
2276 	html_engine_save_buffer_free (buffer_state, TRUE);
2277 
2278 	if (!html_engine_save_output_string (state, "%s", out->str)) {
2279 		g_string_free (out, TRUE);
2280 		return FALSE;
2281 	}
2282 
2283 	g_string_free (out, TRUE);
2284 	return TRUE;
2285 }
2286 
2287 
2288 static GtkHTMLFontStyle
get_default_font_style(const HTMLClueFlow * self)2289 get_default_font_style (const HTMLClueFlow *self)
2290 {
2291 	GtkHTMLFontStyle style = 0;
2292 
2293 	if (HTML_OBJECT (self)->parent && HTML_IS_TABLE_CELL (HTML_OBJECT (self)->parent)
2294 	    && HTML_TABLE_CELL (HTML_OBJECT (self)->parent)->heading)
2295 		style = GTK_HTML_FONT_STYLE_BOLD;
2296 
2297 	switch (self->style) {
2298 	case HTML_CLUEFLOW_STYLE_NORMAL:
2299 	case HTML_CLUEFLOW_STYLE_LIST_ITEM:
2300 		return style | GTK_HTML_FONT_STYLE_SIZE_3;
2301 	case HTML_CLUEFLOW_STYLE_ADDRESS:
2302 		return style | GTK_HTML_FONT_STYLE_SIZE_3 | GTK_HTML_FONT_STYLE_ITALIC;
2303 	case HTML_CLUEFLOW_STYLE_PRE:
2304 		return style | GTK_HTML_FONT_STYLE_SIZE_3 | GTK_HTML_FONT_STYLE_FIXED;
2305 	case HTML_CLUEFLOW_STYLE_H1:
2306 		return style | GTK_HTML_FONT_STYLE_SIZE_6 | GTK_HTML_FONT_STYLE_BOLD;
2307 	case HTML_CLUEFLOW_STYLE_H2:
2308 		return style | GTK_HTML_FONT_STYLE_SIZE_5 | GTK_HTML_FONT_STYLE_BOLD;
2309 	case HTML_CLUEFLOW_STYLE_H3:
2310 		return style | GTK_HTML_FONT_STYLE_SIZE_4 | GTK_HTML_FONT_STYLE_BOLD;
2311 	case HTML_CLUEFLOW_STYLE_H4:
2312 		return style | GTK_HTML_FONT_STYLE_SIZE_3 | GTK_HTML_FONT_STYLE_BOLD;
2313 	case HTML_CLUEFLOW_STYLE_H5:
2314 		return style | GTK_HTML_FONT_STYLE_SIZE_2 | GTK_HTML_FONT_STYLE_BOLD;
2315 	case HTML_CLUEFLOW_STYLE_H6:
2316 		return style | GTK_HTML_FONT_STYLE_SIZE_1 | GTK_HTML_FONT_STYLE_BOLD;
2317 	default:
2318 		g_warning ("Unexpected HTMLClueFlow style %d", self->style);
2319 		return style | GTK_HTML_FONT_STYLE_DEFAULT;
2320 	}
2321 }
2322 
2323 static void
search_set_info(HTMLObject * cur,HTMLSearch * info,guchar * text,guint index,guint bytes)2324 search_set_info (HTMLObject *cur,
2325                  HTMLSearch *info,
2326                  guchar *text,
2327                  guint index,
2328                  guint bytes)
2329 {
2330 	guint text_bytes = 0;
2331 	guint cur_bytes;
2332 
2333 	info->found_bytes = bytes;
2334 
2335 	if (info->found) {
2336 		g_list_free (info->found);
2337 		info->found = NULL;
2338 	}
2339 
2340 	while (cur) {
2341 		if (html_object_is_text (cur)) {
2342 			cur_bytes = HTML_TEXT (cur)->text_bytes;
2343 			if (text_bytes + cur_bytes > index) {
2344 				if (!info->found) {
2345 					info->start_pos = g_utf8_pointer_to_offset ((gchar *) text + text_bytes,
2346 										    (gchar *) text + index);
2347 				}
2348 				info->found = g_list_append (info->found, cur);
2349 			}
2350 			text_bytes += cur_bytes;
2351 			if (text_bytes >= index + info->found_bytes) {
2352 				info->stop_pos = info->start_pos + g_utf8_pointer_to_offset ((gchar *) text + index,
2353 											     (gchar *) text + index + info->found_bytes);
2354 				info->last     = HTML_OBJECT (cur);
2355 				return;
2356 			}
2357 		} else if (HTML_OBJECT_TYPE (cur) != HTML_TYPE_TEXTSLAVE) {
2358 			break;
2359 		}
2360 		cur = cur->next;
2361 	}
2362 
2363 	g_assert_not_reached ();
2364 }
2365 
2366 /* search text objects ([TextMaster, LinkTextMaster], TextSlave*) */
2367 static gboolean
search_text(HTMLObject ** beg,HTMLSearch * info)2368 search_text (HTMLObject **beg,
2369              HTMLSearch *info)
2370 {
2371 	HTMLObject *cur = *beg;
2372 	HTMLObject *end = cur;
2373 	HTMLObject *head;
2374 	guchar *par, *pp;
2375 	guint text_bytes;
2376 	guint eq_bytes;
2377 	gint index;
2378 	gboolean retval = FALSE;
2379 
2380 	/* printf ("search flow look for \"text\" %s\n", info->text); */
2381 
2382 	/* first get flow text_bytes */
2383 	text_bytes = 0;
2384 	while (cur) {
2385 		if (html_object_is_text (cur)) {
2386 			text_bytes += HTML_TEXT (cur)->text_bytes;
2387 			end = cur;
2388 		} else if (HTML_OBJECT_TYPE (cur) != HTML_TYPE_TEXTSLAVE) {
2389 			break;
2390 		}
2391 		cur = (info->forward) ? cur->next : cur->prev;
2392 	}
2393 
2394 	if (text_bytes > 0) {
2395 		par = (guchar *) g_new (gchar, text_bytes + 1);
2396 		par[text_bytes] = 0;
2397 
2398 		pp = (info->forward) ? par : par + text_bytes;
2399 
2400 		/* now fill par with text */
2401 		head = (info->forward) ? *beg : end;
2402 		cur = *beg;
2403 		while (cur) {
2404 			if (html_object_is_text (cur)) {
2405 				if (!info->forward) {
2406 					pp -= HTML_TEXT (cur)->text_bytes;
2407 				}
2408 				strncpy ((gchar *) pp, HTML_TEXT (cur)->text, HTML_TEXT (cur)->text_bytes);
2409 				if (info->forward) {
2410 					pp += HTML_TEXT (cur)->text_bytes;
2411 				}
2412 			} else if (HTML_OBJECT_TYPE (cur) != HTML_TYPE_TEXTSLAVE) {
2413 				break;
2414 			}
2415 			cur = (info->forward) ? cur->next : cur->prev;
2416 		}
2417 
2418 		/* set eq_bytes and pos counters */
2419 		eq_bytes = 0;
2420 		if (info->found) {
2421 			if (info->start_pos > 0)
2422 				index = ((guchar *) g_utf8_offset_to_pointer ((gchar *) par, info->start_pos + ((info->forward) ? 0 : -1))) - par;
2423 			else
2424 				index = info->forward ? ((guchar *) g_utf8_offset_to_pointer ((gchar *) par, info->start_pos) - par) : -1;
2425 		} else {
2426 			index = (info->forward) ? 0 : text_bytes;
2427 		}
2428 
2429 		/* make shorter text instead */
2430 		if (!info->forward && index + info->text_bytes < text_bytes) {
2431 			guchar * tmp = (guchar *)(par + index + info->text_bytes);
2432 			*tmp = '\0';
2433 		}
2434 
2435 		if ((info->forward && index < text_bytes)
2436 		    || (!info->forward && index >= 0)) {
2437 			if (info->reb) {
2438 				/* regex search */
2439 				gint rv;
2440 #ifndef HAVE_GNU_REGEX
2441 				regmatch_t match;
2442 				/* guchar *p=par+pos; */
2443 
2444 				/* FIXME UTF8
2445 				 * replace &nbsp;'s with spaces
2446 				while (*p) {
2447 					if (*p == ENTITY_NBSP) {
2448 						*p = ' ';
2449 					}
2450 					p += (info->forward) ? 1 : -1;
2451 					} */
2452 
2453 				while ((info->forward && index < text_bytes)
2454 				       || (!info->forward && index >= 0)) {
2455 					rv = regexec (info->reb,
2456 						      (gchar *) par + index,
2457 						      1, &match, 0);
2458 					if (rv == 0) {
2459 						search_set_info (head, info, par,
2460 								 index + match.rm_so, match.rm_eo - match.rm_so);
2461 						retval = TRUE;
2462 						break;
2463 					}
2464 					index += (info->forward)
2465 						? (((guchar *) g_utf8_next_char ((gchar *) par + index)) - par - index)
2466 						: (((guchar *) g_utf8_prev_char ((gchar *) par + index)) - par - index);
2467 				}
2468 #else
2469 				rv = re_search (info->reb, par, text_bytes, index,
2470 						(info->forward) ? text_bytes - index : -index, NULL);
2471 				if (rv >= 0) {
2472 					guint found_index = rv;
2473 					rv = re_match (info->reb, par, text_bytes, found_index, NULL);
2474 					if (rv < 0) {
2475 						g_warning ("re_match (...) error");
2476 					}
2477 					search_set_info (head, info, par, found_index, rv);
2478 					retval = TRUE;
2479 				} else {
2480 					if (rv < -1) {
2481 						g_warning ("re_search (...) error");
2482 					}
2483 				}
2484 #endif
2485 			} else {
2486 				/* substring search - simple one - could be improved
2487 				 * go thru par and look for info->text */
2488 				while ((info->forward && par[index])
2489 				       || (!info->forward && index >= 0)) {
2490 					gunichar unicode_info, unicode_par;
2491 
2492 					unicode_info = g_utf8_get_char (info->text + eq_bytes);
2493 					unicode_par = g_utf8_get_char ((gchar *) par + index);
2494 					if (!info->case_sensitive) {
2495 					     unicode_info = g_unichar_toupper (unicode_info);
2496 					     unicode_par  = g_unichar_toupper (unicode_par);
2497 					}
2498 
2499 					if (unicode_info == unicode_par) {
2500 						eq_bytes += (guchar *) g_utf8_next_char (par + index) - par - index;
2501 
2502 						if (eq_bytes == info->text_bytes) {
2503 							/* The above par + index is always at the beginning of the last character matched */
2504 							index = (guchar *) g_utf8_next_char ((gchar *) par + index) - par - eq_bytes;
2505 							search_set_info (head, info, par, index, info->text_bytes);
2506 							retval = TRUE;
2507 							break;
2508 						}
2509 					} else {
2510 						index -= eq_bytes;
2511 						eq_bytes = 0;
2512 					}
2513 					index += (info->forward || unicode_info == unicode_par)
2514 						? (((guchar *) g_utf8_next_char (par + index)) - par - index)
2515 						: (index
2516 							? (((guchar *) g_utf8_prev_char ((gchar *) par + index)) - par - index)
2517 							: -1);
2518 				}
2519 			}
2520 		}
2521 		g_free (par);
2522 	}
2523 
2524 	*beg = cur;
2525 
2526 	return retval;
2527 }
2528 
2529 static gboolean
search(HTMLObject * obj,HTMLSearch * info)2530 search (HTMLObject *obj,
2531         HTMLSearch *info)
2532 {
2533 	HTMLClue *clue = HTML_CLUE (obj);
2534 	HTMLObject *cur;
2535 	gboolean next = FALSE;
2536 
2537 	/* does last search end here? */
2538 	if (info->found) {
2539 		cur  = HTML_OBJECT (info->found->data);
2540 		next = TRUE;
2541 	} else {
2542 		/* search_next? */
2543 		if (html_search_child_on_stack (info, obj)) {
2544 			cur  = html_search_pop (info);
2545 			cur  = (info->forward) ? cur->next : cur->prev;
2546 			next = TRUE;
2547 		} else {
2548 			/* normal search */
2549 			cur  = (info->forward) ? clue->head : clue->tail;
2550 		}
2551 	}
2552 	while (cur) {
2553 		gboolean found = FALSE;
2554 		gboolean is_text;
2555 
2556 		is_text = html_object_is_text (cur);
2557 
2558 		if (is_text) {
2559 			if (search_text (&cur, info))
2560 				return TRUE;
2561 		}
2562 
2563 		if (info->found) {
2564 			g_list_free (info->found);
2565 			info->found = NULL;
2566 			info->start_pos = 0;
2567 			found = TRUE;
2568 		}
2569 
2570 		if (!is_text) {
2571 			if (!found || (info->start_pos < 0 && info->forward) || (info->start_pos >= 0 && !info->forward)) {
2572 				html_search_push (info, cur);
2573 				if (html_object_search (cur, info))
2574 					return TRUE;
2575 				html_search_pop (info);
2576 			}
2577 			cur = (info->forward) ? cur->next : cur->prev;
2578 		}
2579 	}
2580 
2581 	if (next) {
2582 		return html_search_next_parent (info);
2583 	}
2584 
2585 	return FALSE;
2586 }
2587 
2588 static gboolean
search_next(HTMLObject * obj,HTMLSearch * info)2589 search_next (HTMLObject *obj,
2590              HTMLSearch *info)
2591 {
2592 	return FALSE;
2593 }
2594 
2595 static gboolean
relayout(HTMLObject * self,HTMLEngine * engine,HTMLObject * child)2596 relayout (HTMLObject *self,
2597           HTMLEngine *engine,
2598           HTMLObject *child)
2599 {
2600 	gint mw;
2601 
2602 	mw = html_object_calc_min_width (self, engine->painter);
2603 	if (mw <= self->max_width)
2604 		return (*HTML_OBJECT_CLASS (parent_class)->relayout) (self, engine, child);
2605 	html_engine_calc_size (engine, FALSE);
2606 	html_engine_draw (engine, engine->x_offset, engine->y_offset, engine->width, engine->height);
2607 
2608 	return TRUE;
2609 }
2610 
2611 
2612 void
html_clueflow_type_init(void)2613 html_clueflow_type_init (void)
2614 {
2615 	html_clueflow_class_init (&html_clueflow_class, HTML_TYPE_CLUEFLOW, sizeof (HTMLClueFlow));
2616 }
2617 
2618 static HTMLDirection
html_clueflow_real_get_direction(HTMLObject * o)2619 html_clueflow_real_get_direction (HTMLObject *o)
2620 {
2621 	HTMLDirection dir = HTML_CLUEFLOW (o)->dir;
2622 
2623 	if (dir == HTML_DIRECTION_DERIVED && o->parent) {
2624 		dir = html_object_get_direction (o->parent);
2625 	}
2626 
2627 	if (dir == HTML_DIRECTION_DERIVED) {
2628 		HTMLObject *child;
2629 		for (child = HTML_CLUE (o)->head; child; child = child->next) {
2630 			if (HTML_IS_TEXT (child)) {
2631 				/*if (child == HTML_CLUE (o)->head && html_object_get_length (child) == 0 && o->prev)
2632 					return html_object_get_direction (o->prev);*/
2633 
2634 				dir = html_text_direction_pango_to_html (html_text_get_pango_direction (HTML_TEXT (child)));
2635 
2636 				break;
2637 			}
2638 		}
2639 	}
2640 
2641 	return dir;
2642 }
2643 
2644 void
html_clueflow_class_init(HTMLClueFlowClass * klass,HTMLType type,guint size)2645 html_clueflow_class_init (HTMLClueFlowClass *klass,
2646                           HTMLType type,
2647                           guint size)
2648 {
2649 	HTMLClueClass *clue_class;
2650 	HTMLObjectClass *object_class;
2651 
2652 	clue_class = HTML_CLUE_CLASS (klass);
2653 	object_class = HTML_OBJECT_CLASS (klass);
2654 
2655 	html_clue_class_init (clue_class, type, size);
2656 
2657 	object_class->destroy = destroy;
2658 	object_class->copy = copy;
2659 	object_class->op_cut = op_cut;
2660 	object_class->op_copy = op_copy;
2661 	object_class->split = split;
2662 	object_class->merge = merge;
2663 	object_class->calc_size = html_clue_flow_real_calc_size;
2664 	object_class->set_max_width = set_max_width;
2665 	object_class->set_max_height = set_max_height;
2666 	object_class->calc_min_width = calc_min_width;
2667 	object_class->calc_preferred_width = calc_preferred_width;
2668 	object_class->draw = draw;
2669 	object_class->save = save;
2670 	object_class->save_plain = save_plain;
2671 	object_class->check_point = check_point;
2672 	object_class->append_selection_string = append_selection_string;
2673 	object_class->search = search;
2674 	object_class->search_next = search_next;
2675 	object_class->relayout = relayout;
2676 	object_class->get_recursive_length = get_recursive_length;
2677 	object_class->get_clear = get_clear;
2678 	object_class->set_painter = set_painter;
2679 	object_class->get_direction = html_clueflow_real_get_direction;
2680 
2681 	klass->get_default_font_style = get_default_font_style;
2682 
2683 	parent_class = &html_clue_class;
2684 }
2685 
2686 void
html_clueflow_init(HTMLClueFlow * clueflow,HTMLClueFlowClass * klass,HTMLClueFlowStyle style,GByteArray * levels,HTMLListType item_type,gint item_number,HTMLClearType clear)2687 html_clueflow_init (HTMLClueFlow *clueflow,
2688                     HTMLClueFlowClass *klass,
2689                     HTMLClueFlowStyle style,
2690                     GByteArray *levels,
2691                     HTMLListType item_type,
2692                     gint item_number,
2693                     HTMLClearType clear)
2694 {
2695 	HTMLObject *object;
2696 	HTMLClue *clue;
2697 
2698 	object = HTML_OBJECT (clueflow);
2699 	clue = HTML_CLUE (clueflow);
2700 
2701 	html_clue_init (clue, HTML_CLUE_CLASS (klass));
2702 
2703 	object->flags &= ~HTML_OBJECT_FLAG_FIXEDWIDTH;
2704 
2705 	clue->valign = HTML_VALIGN_BOTTOM;
2706 	clue->halign = HTML_HALIGN_NONE;
2707 	clueflow->dir = HTML_DIRECTION_DERIVED;
2708 
2709 	clueflow->style = style;
2710 	clueflow->levels = levels;
2711 	clueflow->indent_width = -1;
2712 
2713 	clueflow->item_type   = item_type;
2714 	clueflow->item_number = item_number;
2715 	clueflow->item_color = NULL;
2716 
2717 	clueflow->clear = clear;
2718 }
2719 
2720 HTMLObject *
html_clueflow_new(HTMLClueFlowStyle style,GByteArray * levels,HTMLListType item_type,gint item_number,HTMLClearType clear)2721 html_clueflow_new (HTMLClueFlowStyle style,
2722                    GByteArray *levels,
2723                    HTMLListType item_type,
2724                    gint item_number,
2725                    HTMLClearType clear)
2726 {
2727 	HTMLClueFlow *clueflow;
2728 
2729 	clueflow = g_new (HTMLClueFlow, 1);
2730 	html_clueflow_init (clueflow, &html_clueflow_class, style, levels, item_type, item_number, clear);
2731 
2732 	return HTML_OBJECT (clueflow);
2733 }
2734 
2735 HTMLObject *
html_clueflow_new_from_flow(HTMLClueFlow * flow)2736 html_clueflow_new_from_flow (HTMLClueFlow *flow)
2737 {
2738 	HTMLObject *o;
2739 
2740 	o = html_clueflow_new (flow->style, html_clueflow_dup_levels (flow),
2741 			       flow->item_type, flow->item_number, flow->clear);
2742 	html_object_copy_data_from_object (o, HTML_OBJECT (flow));
2743 
2744 	return o;
2745 }
2746 
2747 
2748 /* Virtual methods.  */
2749 
2750 GtkHTMLFontStyle
html_clueflow_get_default_font_style(const HTMLClueFlow * self)2751 html_clueflow_get_default_font_style (const HTMLClueFlow *self)
2752 {
2753 	g_return_val_if_fail (self != NULL, GTK_HTML_FONT_STYLE_DEFAULT);
2754 
2755 	return (* HCF_CLASS (self)->get_default_font_style) (self);
2756 }
2757 
2758 
2759 /* Clue splitting (for editing).  */
2760 
2761 /**
2762  * html_clue_split:
2763  * @clue:
2764  * @child:
2765  *
2766  * Remove @child and its successors from @clue, and create a new clue
2767  * containing them.  The new clue has the same properties as the original clue.
2768  *
2769  * Return value: A pointer to the new clue.
2770  **/
2771 HTMLClueFlow *
html_clueflow_split(HTMLClueFlow * clue,HTMLObject * child)2772 html_clueflow_split (HTMLClueFlow *clue,
2773                      HTMLObject *child)
2774 {
2775 	HTMLClueFlow *new;
2776 	HTMLObject *prev;
2777 
2778 	g_return_val_if_fail (clue != NULL, NULL);
2779 	g_return_val_if_fail (child != NULL, NULL);
2780 
2781 	/* Create the new clue.  */
2782 
2783 	new = HTML_CLUEFLOW (html_clueflow_new_from_flow (clue));
2784 
2785 	/* Remove the children from the original clue.  */
2786 
2787 	prev = child->prev;
2788 	if (prev != NULL) {
2789 		prev->next = NULL;
2790 		HTML_CLUE (clue)->tail = prev;
2791 	} else {
2792 		HTML_CLUE (clue)->head = NULL;
2793 		HTML_CLUE (clue)->tail = NULL;
2794 	}
2795 
2796 	child->prev = NULL;
2797 	html_object_change_set (HTML_OBJECT (clue), HTML_CHANGE_ALL_CALC);
2798 
2799 	/* Put the children into the new clue.  */
2800 
2801 	html_clue_append (HTML_CLUE (new), child);
2802 
2803 	/* Return the new clue.  */
2804 
2805 	return new;
2806 }
2807 
2808 
2809 static void
relayout_and_draw(HTMLObject * object,HTMLEngine * engine)2810 relayout_and_draw (HTMLObject *object,
2811                    HTMLEngine *engine)
2812 {
2813 	if (engine == NULL)
2814 		return;
2815 
2816 	html_object_relayout (object, engine, NULL);
2817 	html_engine_queue_draw (engine, object);
2818 }
2819 
2820 /* This performs a relayout of the object when the indentation level
2821  * has changed.  In this case, we need to relayout the previous
2822  * paragraph and the following one, because their padding might change
2823  * after the level change. */
2824 static void
relayout_with_siblings(HTMLClueFlow * flow,HTMLEngine * engine)2825 relayout_with_siblings (HTMLClueFlow *flow,
2826                         HTMLEngine *engine)
2827 {
2828 	if (engine == NULL)
2829 		return;
2830 
2831 	/* FIXME this is ugly and inefficient.  */
2832 
2833 	if (HTML_OBJECT (flow)->prev != NULL)
2834 		relayout_and_draw (HTML_OBJECT (flow)->prev, engine);
2835 
2836 	relayout_and_draw (HTML_OBJECT (flow), engine);
2837 
2838 	if (HTML_OBJECT (flow)->next != NULL)
2839 		relayout_and_draw (HTML_OBJECT (flow)->next, engine);
2840 }
2841 
2842 
2843 void
html_clueflow_set_style(HTMLClueFlow * flow,HTMLEngine * engine,HTMLClueFlowStyle style)2844 html_clueflow_set_style (HTMLClueFlow *flow,
2845                          HTMLEngine *engine,
2846                          HTMLClueFlowStyle style)
2847 {
2848 	g_return_if_fail (flow != NULL);
2849 	g_return_if_fail (engine != NULL);
2850 	g_return_if_fail (HTML_IS_ENGINE (engine));
2851 
2852 	html_object_change_set_down (HTML_OBJECT (flow), HTML_CHANGE_ALL);
2853 	flow->style = style;
2854 	if (style != HTML_CLUEFLOW_STYLE_LIST_ITEM)
2855 		flow->item_number = 0;
2856 
2857 	html_engine_schedule_update (engine);
2858 	/* FIXME - make it more effective: relayout_with_siblings (flow, engine); */
2859 }
2860 
2861 GByteArray *
html_clueflow_dup_levels(HTMLClueFlow * flow)2862 html_clueflow_dup_levels (HTMLClueFlow *flow)
2863 {
2864 	GByteArray *levels;
2865 
2866 	levels = g_byte_array_new ();
2867 	copy_levels (levels, flow->levels);
2868 
2869 	return levels;
2870 }
2871 
2872 void
html_clueflow_set_levels(HTMLClueFlow * flow,HTMLEngine * engine,GByteArray * levels)2873 html_clueflow_set_levels (HTMLClueFlow *flow,
2874                           HTMLEngine *engine,
2875                           GByteArray *levels)
2876 {
2877 	HTMLObject *next_relative;
2878 
2879 	next_relative = get_next_relative_item (HTML_OBJECT (flow));
2880 	copy_levels (flow->levels, levels);
2881 
2882 	update_item_number (HTML_OBJECT (flow), engine);
2883 	if (next_relative)
2884 		update_item_number (next_relative, engine);
2885 
2886 	relayout_with_siblings (flow, engine);
2887 }
2888 
2889 void
html_clueflow_set_item_type(HTMLClueFlow * flow,HTMLEngine * engine,HTMLListType item_type)2890 html_clueflow_set_item_type (HTMLClueFlow *flow,
2891                              HTMLEngine *engine,
2892                              HTMLListType item_type)
2893 {
2894 	g_return_if_fail (flow != NULL);
2895 	g_return_if_fail (engine != NULL);
2896 	g_return_if_fail (HTML_IS_ENGINE (engine));
2897 
2898 	html_object_change_set (HTML_OBJECT (flow), HTML_CHANGE_ALL);
2899 
2900 	if ((is_blockquote (item_type) != is_blockquote (flow->item_type)) && flow->levels->len)
2901 		flow->levels->data[flow->levels->len - 1] = item_type;
2902 
2903 	flow->item_type = item_type;
2904 
2905 	update_item_number (HTML_OBJECT (flow), engine);
2906 	if (!items_are_relative (HTML_OBJECT (flow), HTML_OBJECT (flow)->next) && HTML_OBJECT (flow)->next)
2907 		update_item_number (HTML_OBJECT (flow)->next, engine);
2908 
2909 	html_engine_schedule_update (engine);
2910 	/* FIXME - make it more effective: relayout_with_siblings (flow, engine); */
2911 }
2912 
2913 HTMLClueFlowStyle
html_clueflow_get_style(HTMLClueFlow * flow)2914 html_clueflow_get_style (HTMLClueFlow *flow)
2915 {
2916 	g_return_val_if_fail (flow != NULL, HTML_CLUEFLOW_STYLE_NORMAL);
2917 
2918 	return flow->style;
2919 }
2920 
2921 HTMLListType
html_clueflow_get_item_type(HTMLClueFlow * flow)2922 html_clueflow_get_item_type (HTMLClueFlow *flow)
2923 {
2924 	g_return_val_if_fail (flow != NULL, HTML_LIST_TYPE_BLOCKQUOTE);
2925 
2926 	return flow->item_type;
2927 }
2928 
2929 void
html_clueflow_set_halignment(HTMLClueFlow * flow,HTMLEngine * engine,HTMLHAlignType alignment)2930 html_clueflow_set_halignment (HTMLClueFlow *flow,
2931                               HTMLEngine *engine,
2932                               HTMLHAlignType alignment)
2933 {
2934 	g_return_if_fail (flow != NULL);
2935 	g_return_if_fail (engine != NULL);
2936 	g_return_if_fail (HTML_IS_ENGINE (engine));
2937 
2938 	HTML_CLUE (flow)->halign = alignment;
2939 
2940 	relayout_and_draw (HTML_OBJECT (flow), engine);
2941 }
2942 
2943 void
html_clueflow_modify_indentation_by_delta(HTMLClueFlow * flow,HTMLEngine * engine,gint indentation_delta,guint8 * indentation_levels)2944 html_clueflow_modify_indentation_by_delta (HTMLClueFlow *flow,
2945                                            HTMLEngine *engine,
2946                                            gint indentation_delta,
2947                                            guint8 *indentation_levels)
2948 {
2949 	HTMLObject *next_relative;
2950 	gint indentation;
2951 	g_return_if_fail (flow != NULL);
2952 	g_return_if_fail (engine != NULL);
2953 	g_return_if_fail (HTML_IS_ENGINE (engine));
2954 
2955 	next_relative = get_next_relative_item (HTML_OBJECT (flow));
2956 
2957 	indentation = flow->levels->len + indentation_delta;
2958 	indentation = indentation < 0 ? 0 : indentation;
2959 
2960 	if (indentation_delta > 0)
2961 		g_byte_array_append (flow->levels, indentation_levels, indentation_delta);
2962 	else {
2963 		g_byte_array_set_size (flow->levels, indentation);
2964 		if (is_item (flow) && indentation < 1 && indentation_delta < 0) {
2965 			html_clueflow_set_style (flow, engine, HTML_CLUEFLOW_STYLE_NORMAL);
2966 			html_clueflow_set_item_type (flow, engine, HTML_LIST_TYPE_BLOCKQUOTE);
2967 			html_object_change_set_down (HTML_OBJECT (flow), HTML_CHANGE_ALL);
2968 		}
2969 	}
2970 
2971 	update_item_number (HTML_OBJECT (flow), engine);
2972 	if (next_relative)
2973 		update_item_number (next_relative, engine);
2974 	relayout_with_siblings (flow, engine);
2975 }
2976 
2977 void
html_clueflow_set_indentation(HTMLClueFlow * flow,HTMLEngine * engine,gint indentation,guint8 * indentation_levels)2978 html_clueflow_set_indentation (HTMLClueFlow *flow,
2979                                HTMLEngine *engine,
2980                                gint indentation,
2981                                guint8 *indentation_levels)
2982 {
2983 	HTMLObject *next_relative;
2984 	gint i;
2985 	g_return_if_fail (flow != NULL);
2986 	g_return_if_fail (engine != NULL);
2987 	g_return_if_fail (HTML_IS_ENGINE (engine));
2988 
2989 	if (indentation < 0)
2990 		indentation = 0;
2991 
2992 	next_relative = get_next_relative_item (HTML_OBJECT (flow));
2993 
2994 	g_byte_array_set_size (flow->levels, indentation);
2995 
2996 	i = indentation;
2997 	while (i--)
2998 		flow->levels->data[i] = indentation_levels[i];
2999 
3000 	update_item_number (HTML_OBJECT (flow), engine);
3001 	if (next_relative)
3002 		update_item_number (next_relative, engine);
3003 	relayout_with_siblings (flow, engine);
3004 }
3005 
3006 guint8
html_clueflow_get_indentation(HTMLClueFlow * flow)3007 html_clueflow_get_indentation (HTMLClueFlow *flow)
3008 {
3009 	g_return_val_if_fail (flow != NULL, 0);
3010 
3011 	/* FIXME levels */
3012 	return flow->levels->len;
3013 }
3014 
3015 #if 0
3016 void
3017 html_clueflow_set_properties (HTMLClueFlow *flow,
3018                               HTMLEngine *engine,
3019                               HTMLClueFlowStyle style,
3020                               guint8 indentation,
3021                               HTMLHAlignType alignment)
3022 {
3023 	g_return_if_fail (flow != NULL);
3024 	g_return_if_fail (engine != NULL);
3025 	g_return_if_fail (HTML_IS_ENGINE (engine));
3026 
3027 	HTML_CLUE (flow)->halign = alignment;
3028 
3029 	flow->style = style;
3030 	html_clueflow_set_indentation (flow, engine, indentation);
3031 
3032 	relayout_and_draw (HTML_OBJECT (flow), engine);
3033 }
3034 
3035 void
3036 html_clueflow_get_properties (HTMLClueFlow *flow,
3037                               HTMLClueFlowStyle *style_return,
3038                               guint8 *indentation_return,
3039                               HTMLHAlignType *alignment_return)
3040 {
3041 	g_return_if_fail (flow != NULL);
3042 
3043 	if (style_return != NULL)
3044 		*style_return = flow->style;
3045 	if (indentation_return != NULL)
3046 		/* FIXME levels */
3047 		*indentation_return = flow->levels->len;
3048 	if (alignment_return != NULL)
3049 		*alignment_return = HTML_CLUE (flow)->halign;
3050 }
3051 #endif
3052 /* spell checking */
3053 
3054 #include "htmlinterval.h"
3055 
3056 static guint
get_text_bytes(HTMLClue * clue,HTMLInterval * i)3057 get_text_bytes (HTMLClue *clue,
3058                 HTMLInterval *i)
3059 {
3060 	HTMLObject *obj;
3061 	guint bytes;
3062 
3063 	g_assert (i);
3064 	g_assert (i->from.object);
3065 	g_assert (i->to.object);
3066 
3067 	bytes = 0;
3068 	obj = html_interval_get_head (i, HTML_OBJECT (clue));
3069 	while (obj) {
3070 		bytes += html_interval_get_bytes (i, obj);
3071 		if (obj == i->to.object)
3072 			break;
3073 		obj = html_object_next_not_slave (obj);
3074 	}
3075 
3076 	return bytes;
3077 }
3078 
3079 static gchar *
get_text(HTMLClue * clue,HTMLInterval * i)3080 get_text (HTMLClue *clue,
3081           HTMLInterval *i)
3082 {
3083 	HTMLObject *obj;
3084 	guint cb, bytes = 0;
3085 	gchar *text, *ct;
3086 
3087 	bytes        = get_text_bytes (clue, i);
3088 	ct           = text = g_malloc (bytes + 1);
3089 	text[bytes] = 0;
3090 
3091 	obj = html_interval_get_head (i, HTML_OBJECT (clue));
3092 	while (obj) {
3093 		cb = html_interval_get_bytes (i, obj);
3094 		if (html_object_is_text (obj))
3095 			strncpy (ct, HTML_TEXT (obj)->text + html_interval_get_start_index (i, obj), cb);
3096 		else
3097 			if (cb == 1) *ct = ' ';
3098 			else memset (ct, ' ', cb);
3099 		ct += cb;
3100 		if (obj == i->to.object)
3101 			break;
3102 		obj = html_object_next_not_slave (obj);
3103 	}
3104 
3105 	/* printf ("get_text: %d \"%s\"\n", bytes, text); */
3106 
3107 	return text;
3108 }
3109 
3110 static HTMLObject *
next_obj_and_clear(HTMLObject * obj,guint * off,gboolean * is_text,HTMLInterval * i)3111 next_obj_and_clear (HTMLObject *obj,
3112                     guint *off,
3113                     gboolean *is_text,
3114                     HTMLInterval *i)
3115 {
3116 	*off += html_object_get_length (obj) - html_interval_get_start (i, obj);
3117 	obj = obj->next;
3118 	if (obj && (*is_text = html_object_is_text (obj)))
3119 		html_text_spell_errors_clear_interval (HTML_TEXT (obj), i);
3120 
3121 	return obj;
3122 }
3123 
3124 static HTMLObject *
spell_check_word_mark(HTMLObject * obj,const gchar * text,const gchar * word,guint * off,HTMLInterval * i)3125 spell_check_word_mark (HTMLObject *obj,
3126                        const gchar *text,
3127                        const gchar *word,
3128                        guint *off,
3129                        HTMLInterval *i)
3130 {
3131 	guint w_off, ioff;
3132 	guint len = g_utf8_strlen (word, -1);
3133 	gboolean is_text;
3134 
3135 	/* printf ("[not in dictionary word off: %d off: %d]\n", word - text, *off); */
3136 	is_text = html_object_is_text (obj);
3137 	w_off   = g_utf8_pointer_to_offset (text, word);
3138 	while (obj && (!is_text || (is_text && *off + html_interval_get_length (i, obj) <= w_off)))
3139 		obj = next_obj_and_clear (obj, off, &is_text, i);
3140 
3141 	/* printf ("is_text: %d len: %d obj: %p off: %d\n", is_text, len, obj, *off); */
3142 	if (obj && is_text) {
3143 		gchar *t;
3144 		guint tlen;
3145 		guint toff;
3146 
3147 		while (len) {
3148 			toff  = w_off - *off;
3149 			ioff  = html_interval_get_start (i, obj);
3150 			tlen  = MIN (HTML_TEXT (obj)->text_len - toff - ioff, len);
3151 			t     = HTML_TEXT (obj)->text;
3152 			g_assert (!strncmp (word, g_utf8_offset_to_pointer (t, toff + ioff),
3153 					    g_utf8_offset_to_pointer (t, toff + ioff + tlen)
3154 					    - g_utf8_offset_to_pointer (t, toff + ioff)));
3155 			/* printf ("add spell error - word: %s off: %d beg: %s len: %d\n",
3156 			 * word, *off, HTML_TEXT (obj)->text + toff, tlen); */
3157 			html_text_spell_errors_add (HTML_TEXT (obj),
3158 						    ioff + toff, tlen);
3159 			len     -= tlen;
3160 			w_off   += tlen;
3161 			word     = g_utf8_offset_to_pointer (word, tlen);
3162 			if (len)
3163 				do obj = next_obj_and_clear (obj, off, &is_text, i); while (obj && !is_text);
3164 			/* printf ("off: %d\n", *off); */
3165 			g_assert (!len || obj);
3166 		}
3167 	}
3168 
3169 	return obj;
3170 }
3171 
3172 static gchar *
begin_of_word(gchar * text,gchar * ct,gboolean * cited)3173 begin_of_word (gchar *text,
3174                gchar *ct,
3175                gboolean *cited)
3176 {
3177 	gunichar uc;
3178 
3179 	*cited = FALSE;
3180 	do
3181 		uc = g_utf8_get_char (ct);
3182 	while (!html_selection_spell_word (uc, cited) && (ct = g_utf8_next_char (ct)) && *ct);
3183 
3184 	return ct;
3185 }
3186 
3187 static gchar *
end_of_word(gchar * ct,gboolean cited)3188 end_of_word (gchar *ct,
3189              gboolean cited)
3190 {
3191 	gunichar uc, ucn;
3192 	gchar *cn;
3193 	gboolean cited2;
3194 
3195 	cited2 = FALSE;
3196 	while (*ct
3197 	       && (uc = g_utf8_get_char (ct))
3198 	       && (cn = g_utf8_next_char (ct))
3199 	       && (html_selection_spell_word (uc, &cited2)
3200 		   || (!cited && cited2)
3201 		   || (cited && cited2 && (ucn = g_utf8_get_char (cn)) && g_unichar_isalpha (ucn)))) {
3202 		ct = cn;
3203 		cited2 = FALSE;
3204 	}
3205 
3206 	return ct;
3207 }
3208 
3209 static void
queue_draw(HTMLObject * o,HTMLEngine * e,HTMLInterval * i)3210 queue_draw (HTMLObject *o,
3211             HTMLEngine *e,
3212             HTMLInterval *i)
3213 {
3214 	if (html_object_is_text (o))
3215 		html_text_queue_draw (HTML_TEXT (o), e, html_interval_get_start (i, o), html_interval_get_length (i, o));
3216 }
3217 
3218 void
html_clueflow_spell_check(HTMLClueFlow * flow,HTMLEngine * e,HTMLInterval * interval)3219 html_clueflow_spell_check (HTMLClueFlow *flow,
3220                            HTMLEngine *e,
3221                            HTMLInterval *interval)
3222 {
3223 	HTMLObject *obj;
3224 	HTMLClue *clue;
3225 	guint off;
3226 	gchar *text, *ct, *word;
3227 	HTMLInterval *new_interval = NULL;
3228 
3229 	g_return_if_fail (flow != NULL);
3230 	g_return_if_fail (HTML_OBJECT_TYPE (flow) == HTML_TYPE_CLUEFLOW);
3231 
3232 	/* if (interval)
3233 	 * printf ("html_clueflow_spell_check %p %p %d %d\n", i->from, i->to, i->from_offset, i->to_offset); */
3234 
3235 	clue = HTML_CLUE (flow);
3236 	if (!e->widget->editor_api || !gtk_html_get_inline_spelling (e->widget) || !clue || !clue->tail)
3237 		return;
3238 
3239 	off  = 0;
3240 
3241 	if (!interval) {
3242 		new_interval = html_interval_new (clue->head, clue->tail, 0, html_object_get_length (clue->tail));
3243 		interval = new_interval;
3244 	}
3245 
3246 	text = get_text (clue, interval);
3247 	obj  = html_interval_get_head (interval, HTML_OBJECT (flow));
3248 	if (obj && html_object_is_text (obj))
3249 		html_text_spell_errors_clear_interval (HTML_TEXT (obj), interval);
3250 
3251 	if (text) {
3252 		ct = text;
3253 		while (*ct) {
3254 			gboolean cited;
3255 
3256 			word = ct = begin_of_word (text, ct, &cited);
3257 			ct        =   end_of_word (ct, cited);
3258 
3259 			/* test if we have found word */
3260 			if (word != ct) {
3261 				gint result;
3262 				gchar bak;
3263 
3264 				bak = *ct;
3265 				*ct = 0;
3266 				/* printf ("off %d going to test word: \"%s\"\n", off, word); */
3267 				result = (*e->widget->editor_api->check_word) (e->widget, word, e->widget->editor_data);
3268 
3269 				if (result == 1) {
3270 					gboolean is_text = (obj) ? html_object_is_text (obj) : FALSE;
3271 					while (obj && (!is_text
3272 						       || (off + html_interval_get_length (interval, obj)
3273 							   < g_utf8_pointer_to_offset (text, ct))))
3274 						obj = next_obj_and_clear (obj, &off, &is_text, interval);
3275 				} else if (obj)
3276 						obj = spell_check_word_mark (obj, text, word, &off, interval);
3277 
3278 				*ct = bak;
3279 				if (*ct)
3280 					ct = g_utf8_next_char (ct);
3281 			}
3282 		}
3283 		g_free (text);
3284 
3285 		if (!html_engine_frozen (e)) {
3286 			/* html_engine_queue_clear (e); */
3287 			html_interval_forall (interval, e, (HTMLObjectForallFunc) queue_draw, interval);
3288 			html_engine_flush_draw_queue (e);
3289 		}
3290 		html_interval_destroy (new_interval);
3291 	}
3292 }
3293 
3294 gboolean
html_clueflow_is_empty(HTMLClueFlow * flow)3295 html_clueflow_is_empty (HTMLClueFlow *flow)
3296 {
3297 	HTMLClue *clue;
3298 	g_return_val_if_fail (HTML_IS_CLUEFLOW (flow), TRUE);
3299 
3300 	clue = HTML_CLUE (flow);
3301 
3302 	if (!clue->head
3303 	    || (clue->head && html_object_is_text (clue->head)
3304 		&& HTML_TEXT (clue->head)->text_len == 0 && !html_object_next_not_slave (clue->head)))
3305 		return TRUE;
3306 	return FALSE;
3307 }
3308 
3309 gboolean
html_clueflow_contains_table(HTMLClueFlow * flow)3310 html_clueflow_contains_table (HTMLClueFlow *flow)
3311 {
3312 	HTMLClue *clue;
3313 	g_return_val_if_fail (HTML_IS_CLUEFLOW (flow), FALSE);
3314 
3315 	clue = HTML_CLUE (flow);
3316 
3317 	if (clue->head && HTML_IS_TABLE (clue->head))
3318 		return TRUE;
3319 
3320 	return FALSE;
3321 }
3322 
3323 gint
html_clueflow_get_line_offset(HTMLClueFlow * flow,HTMLPainter * painter,HTMLObject * child)3324 html_clueflow_get_line_offset (HTMLClueFlow *flow,
3325                                HTMLPainter *painter,
3326                                HTMLObject *child)
3327 {
3328 	HTMLObject *o, *head;
3329 	gint line_offset;
3330 
3331 	g_assert (HTML_IS_CLUEFLOW (flow));
3332 
3333 	if (!html_clueflow_tabs (flow, painter))
3334 		return -1;
3335 
3336 	line_offset = 0;
3337 
3338 	/* find head */
3339 	o = head = child;
3340 	while (o) {
3341 		o = head->prev;
3342 		if (o) {
3343 			if (o->y + o->descent - 1 < child->y - child->ascent)
3344 				break;
3345 			else
3346 				head = o;
3347 		}
3348 	}
3349 
3350 	if (HTML_IS_TEXT_SLAVE (head)) {
3351 		HTMLTextSlave *bol = HTML_TEXT_SLAVE (head);
3352 
3353 		html_text_text_line_length (html_text_get_text (bol->owner, bol->posStart),
3354 					    &line_offset, bol->owner->text_len - bol->posStart, NULL);
3355 		head = html_object_next_not_slave (head);
3356 	}
3357 
3358 	while (head) {
3359 		if (head == child)
3360 			break;
3361 		line_offset += html_object_get_line_length (head, painter, line_offset);
3362 		head = html_object_next_not_slave (head);
3363 	}
3364 	/* printf ("lo: %d\n", line_offset); */
3365 	return line_offset;
3366 }
3367 
3368 gboolean
html_clueflow_tabs(HTMLClueFlow * flow,HTMLPainter * p)3369 html_clueflow_tabs (HTMLClueFlow *flow,
3370                     HTMLPainter *p)
3371 {
3372 	return (flow && HTML_IS_CLUEFLOW (flow) && flow->style == HTML_CLUEFLOW_STYLE_PRE) || HTML_IS_PLAIN_PAINTER (p)
3373 		? TRUE : FALSE;
3374 }
3375 
3376 void
html_clueflow_set_item_color(HTMLClueFlow * flow,HTMLColor * color)3377 html_clueflow_set_item_color (HTMLClueFlow *flow,
3378                               HTMLColor *color)
3379 {
3380 	if (flow->item_color)
3381 		html_color_unref (flow->item_color);
3382 	if (color)
3383 		html_color_ref (color);
3384 	flow->item_color = color;
3385 }
3386 
3387 gboolean
html_clueflow_style_equals(HTMLClueFlow * cf1,HTMLClueFlow * cf2)3388 html_clueflow_style_equals (HTMLClueFlow *cf1,
3389                             HTMLClueFlow *cf2)
3390 {
3391 	if (!cf1 || !cf2
3392 	    || !HTML_IS_CLUEFLOW (cf1) || !HTML_IS_CLUEFLOW (cf2)
3393 	    || cf1->style != cf2->style
3394 	    || (cf1->style == HTML_CLUEFLOW_STYLE_LIST_ITEM && cf1->item_type != cf2->item_type)
3395 	    || !is_levels_equal (cf1, cf2))
3396 		return FALSE;
3397 	return TRUE;
3398 }
3399