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