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 '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