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 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 | #include <config.h> |
---|
25 | #include <stdio.h> |
---|
26 | #include <string.h> |
---|
27 | |
---|
28 | #include "htmltextslave.h" |
---|
29 | #include "htmlclue.h" |
---|
30 | #include "htmlclueflow.h" |
---|
31 | #include "htmlcursor.h" |
---|
32 | #include "htmlcolor.h" |
---|
33 | #include "htmlcolorset.h" |
---|
34 | #include "htmlpainter.h" |
---|
35 | #include "htmlobject.h" |
---|
36 | #include "htmlprinter.h" |
---|
37 | #include "htmlplainpainter.h" |
---|
38 | #include "htmlgdkpainter.h" |
---|
39 | #include "htmlsettings.h" |
---|
40 | #include "gtkhtml.h" |
---|
41 | |
---|
42 | |
---|
43 | /* #define HTML_TEXT_SLAVE_DEBUG */ |
---|
44 | |
---|
45 | HTMLTextSlaveClass html_text_slave_class; |
---|
46 | static HTMLObjectClass *parent_class = NULL; |
---|
47 | |
---|
48 | static GList * get_glyphs (HTMLTextSlave *slave, HTMLPainter *painter); |
---|
49 | static GList * get_glyphs_part (HTMLTextSlave *slave, HTMLPainter *painter, guint offset, guint len); |
---|
50 | static void clear_glyphs (HTMLTextSlave *slave); |
---|
51 | |
---|
52 | char * |
---|
53 | html_text_slave_get_text (HTMLTextSlave *slave) |
---|
54 | { |
---|
55 | if (!slave->charStart) |
---|
56 | slave->charStart = html_text_get_text (slave->owner, slave->posStart); |
---|
57 | |
---|
58 | return slave->charStart; |
---|
59 | } |
---|
60 | |
---|
61 | /* Split this TextSlave at the specified offset. */ |
---|
62 | static void |
---|
63 | split (HTMLTextSlave *slave, guint offset, char *start_pointer) |
---|
64 | { |
---|
65 | HTMLObject *obj; |
---|
66 | HTMLObject *new; |
---|
67 | |
---|
68 | g_return_if_fail (offset >= 0); |
---|
69 | g_return_if_fail (offset < slave->posLen); |
---|
70 | |
---|
71 | obj = HTML_OBJECT (slave); |
---|
72 | |
---|
73 | new = html_text_slave_new (slave->owner, |
---|
74 | slave->posStart + offset, |
---|
75 | slave->posLen - offset); |
---|
76 | |
---|
77 | HTML_TEXT_SLAVE (new)->charStart = start_pointer; |
---|
78 | |
---|
79 | html_clue_append_after (HTML_CLUE (obj->parent), new, obj); |
---|
80 | |
---|
81 | slave->posLen = offset; |
---|
82 | } |
---|
83 | |
---|
84 | |
---|
85 | /* HTMLObject methods. */ |
---|
86 | |
---|
87 | static void |
---|
88 | copy (HTMLObject *self, |
---|
89 | HTMLObject *dest) |
---|
90 | { |
---|
91 | (* HTML_OBJECT_CLASS (parent_class)->copy) (self, dest); |
---|
92 | |
---|
93 | /* FIXME it does not make much sense to me to share the owner. */ |
---|
94 | HTML_TEXT_SLAVE (dest)->owner = HTML_TEXT_SLAVE (self)->owner; |
---|
95 | HTML_TEXT_SLAVE (dest)->posStart = HTML_TEXT_SLAVE (self)->posStart; |
---|
96 | HTML_TEXT_SLAVE (dest)->posLen = HTML_TEXT_SLAVE (self)->posLen; |
---|
97 | } |
---|
98 | |
---|
99 | static inline gint |
---|
100 | html_text_slave_get_start_byte_offset (HTMLTextSlave *slave) |
---|
101 | { |
---|
102 | return html_text_slave_get_text (slave) - slave->owner->text; |
---|
103 | } |
---|
104 | |
---|
105 | static guint |
---|
106 | hts_calc_width (HTMLTextSlave *slave, HTMLPainter *painter, gint *asc, gint *dsc) |
---|
107 | { |
---|
108 | HTMLText *text = slave->owner; |
---|
109 | gint line_offset, tabs = 0, width = 0; |
---|
110 | |
---|
111 | line_offset = html_text_slave_get_line_offset (slave, 0, painter); |
---|
112 | if (line_offset != -1) |
---|
113 | width += (html_text_text_line_length (html_text_slave_get_text (slave), &line_offset, slave->posLen, &tabs) - slave->posLen)* |
---|
114 | html_painter_get_space_width (painter, html_text_get_font_style (text), text->face); |
---|
115 | |
---|
116 | width += html_text_calc_part_width (text, painter, html_text_slave_get_text (slave), slave->posStart, slave->posLen, asc, dsc); |
---|
117 | |
---|
118 | return width; |
---|
119 | } |
---|
120 | |
---|
121 | inline static void |
---|
122 | glyphs_destroy (GList *glyphs) |
---|
123 | { |
---|
124 | GList *l; |
---|
125 | |
---|
126 | for (l = glyphs; l; l = l->next->next) |
---|
127 | pango_glyph_string_free ((PangoGlyphString *) l->data); |
---|
128 | g_list_free (glyphs); |
---|
129 | } |
---|
130 | |
---|
131 | static gboolean |
---|
132 | html_text_slave_real_calc_size (HTMLObject *self, HTMLPainter *painter, GList **changed_objs) |
---|
133 | { |
---|
134 | HTMLText *owner; |
---|
135 | HTMLTextSlave *slave; |
---|
136 | GtkHTMLFontStyle font_style; |
---|
137 | gint new_ascent, new_descent, new_width; |
---|
138 | gboolean changed; |
---|
139 | |
---|
140 | slave = HTML_TEXT_SLAVE (self); |
---|
141 | owner = HTML_TEXT (slave->owner); |
---|
142 | font_style = html_text_get_font_style (owner); |
---|
143 | |
---|
144 | new_width = MAX (1, hts_calc_width (slave, painter, &new_ascent, &new_descent)); |
---|
145 | |
---|
146 | /* handle sub & super script */ |
---|
147 | if (font_style & GTK_HTML_FONT_STYLE_SUBSCRIPT || font_style & GTK_HTML_FONT_STYLE_SUPERSCRIPT) { |
---|
148 | gint shift = (new_ascent + new_descent) >> 1; |
---|
149 | |
---|
150 | if (font_style & GTK_HTML_FONT_STYLE_SUBSCRIPT) { |
---|
151 | new_descent += shift; |
---|
152 | new_ascent -= shift; |
---|
153 | } else { |
---|
154 | new_descent -= shift; |
---|
155 | new_ascent += shift; |
---|
156 | } |
---|
157 | } |
---|
158 | |
---|
159 | changed = FALSE; |
---|
160 | |
---|
161 | if (new_ascent != self->ascent) { |
---|
162 | self->ascent = new_ascent; |
---|
163 | changed = TRUE; |
---|
164 | } |
---|
165 | |
---|
166 | if (new_descent != self->descent) { |
---|
167 | self->descent = new_descent; |
---|
168 | changed = TRUE; |
---|
169 | } |
---|
170 | |
---|
171 | if (new_width != self->width) { |
---|
172 | self->width = new_width; |
---|
173 | changed = TRUE; |
---|
174 | } |
---|
175 | |
---|
176 | return changed; |
---|
177 | } |
---|
178 | |
---|
179 | #ifdef HTML_TEXT_SLAVE_DEBUG |
---|
180 | static void |
---|
181 | debug_print (HTMLFitType rv, gchar *text, gint len) |
---|
182 | { |
---|
183 | gint i; |
---|
184 | |
---|
185 | printf ("Split text"); |
---|
186 | switch (rv) { |
---|
187 | case HTML_FIT_PARTIAL: |
---|
188 | printf (" (Partial): `"); |
---|
189 | break; |
---|
190 | case HTML_FIT_NONE: |
---|
191 | printf (" (NoFit): `"); |
---|
192 | break; |
---|
193 | case HTML_FIT_COMPLETE: |
---|
194 | printf (" (Complete): `"); |
---|
195 | break; |
---|
196 | } |
---|
197 | |
---|
198 | for (i = 0; i < len; i++) |
---|
199 | putchar (text [i]); |
---|
200 | |
---|
201 | printf ("'\n"); |
---|
202 | } |
---|
203 | #endif |
---|
204 | |
---|
205 | gint |
---|
206 | html_text_slave_get_line_offset (HTMLTextSlave *slave, gint offset, HTMLPainter *p) |
---|
207 | { |
---|
208 | HTMLObject *head = HTML_OBJECT (slave->owner)->next; |
---|
209 | |
---|
210 | g_assert (HTML_IS_TEXT_SLAVE (head)); |
---|
211 | |
---|
212 | if (!html_clueflow_tabs (HTML_CLUEFLOW (HTML_OBJECT (slave)->parent), p)) |
---|
213 | return -1; |
---|
214 | |
---|
215 | if (head->y + head->descent - 1 < HTML_OBJECT (slave)->y - HTML_OBJECT (slave)->ascent) { |
---|
216 | HTMLObject *prev; |
---|
217 | HTMLTextSlave *bol; |
---|
218 | gint line_offset = 0; |
---|
219 | |
---|
220 | prev = html_object_prev (HTML_OBJECT (slave)->parent, HTML_OBJECT (slave)); |
---|
221 | while (prev->y + prev->descent - 1 >= HTML_OBJECT (slave)->y - HTML_OBJECT (slave)->ascent) |
---|
222 | prev = html_object_prev (HTML_OBJECT (slave)->parent, HTML_OBJECT (prev)); |
---|
223 | |
---|
224 | bol = HTML_TEXT_SLAVE (prev->next); |
---|
225 | return html_text_text_line_length (html_text_slave_get_text (bol), |
---|
226 | &line_offset, slave->posStart + offset - bol->posStart, NULL); |
---|
227 | } else |
---|
228 | return html_text_get_line_offset (slave->owner, p, slave->posStart + offset); |
---|
229 | } |
---|
230 | |
---|
231 | static gboolean |
---|
232 | could_remove_leading_space (HTMLTextSlave *slave, gboolean lineBegin) |
---|
233 | { |
---|
234 | HTMLObject *o = HTML_OBJECT (slave->owner); |
---|
235 | |
---|
236 | if (lineBegin && (HTML_OBJECT (slave)->prev != o || o->prev)) |
---|
237 | return TRUE; |
---|
238 | |
---|
239 | if (!o->prev) |
---|
240 | return FALSE; |
---|
241 | |
---|
242 | while (o->prev && HTML_OBJECT_TYPE (o->prev) == HTML_TYPE_CLUEALIGNED) |
---|
243 | o = o->prev; |
---|
244 | |
---|
245 | return o->prev ? FALSE : TRUE; |
---|
246 | } |
---|
247 | |
---|
248 | gchar * |
---|
249 | html_text_slave_remove_leading_space (HTMLTextSlave *slave, HTMLPainter *painter, gboolean lineBegin) |
---|
250 | { |
---|
251 | gchar *begin; |
---|
252 | |
---|
253 | begin = html_text_slave_get_text (slave); |
---|
254 | if (*begin == ' ' && could_remove_leading_space (slave, lineBegin)) { |
---|
255 | begin = g_utf8_next_char (begin); |
---|
256 | slave->charStart = begin; |
---|
257 | slave->posStart ++; |
---|
258 | slave->posLen --; |
---|
259 | } |
---|
260 | |
---|
261 | return begin; |
---|
262 | } |
---|
263 | |
---|
264 | gint |
---|
265 | html_text_slave_get_nb_width (HTMLTextSlave *slave, HTMLPainter *painter, gboolean lineBegin) |
---|
266 | { |
---|
267 | html_text_slave_remove_leading_space (slave, painter, lineBegin); |
---|
268 | |
---|
269 | return html_object_calc_min_width (HTML_OBJECT (slave), painter); |
---|
270 | } |
---|
271 | |
---|
272 | static gboolean |
---|
273 | update_lb (HTMLTextSlave *slave, HTMLPainter *painter, gint widthLeft, gint offset, gchar *s, gint ii, gint io, gint line_offset, |
---|
274 | gint *w, gint *ltw, gint *lwl, gint *lbw, gint *lbo, gchar **lbsp, gboolean *force_fit) |
---|
275 | { |
---|
276 | gint new_ltw, new_lwl, aw; |
---|
277 | |
---|
278 | new_ltw = html_text_tail_white_space (slave->owner, painter, offset, ii, io, &new_lwl, line_offset, s); |
---|
279 | if (HTML_IS_GDK_PAINTER (painter) || HTML_IS_PLAIN_PAINTER (painter)) { |
---|
280 | aw = *w - new_ltw; |
---|
281 | } else { |
---|
282 | gint lo = html_text_get_line_offset (slave->owner, painter, *lbo); |
---|
283 | gint width; |
---|
284 | /* printf ("s: %s l: %d\n", html_text_get_text (slave->owner, lbo - lwl), offset - new_lwl - lbo + lwl); */ |
---|
285 | html_text_calc_text_size (slave->owner, painter, html_text_get_text (slave->owner, *lbo) - slave->owner->text, |
---|
286 | offset - *lbo, NULL, NULL, &lo, |
---|
287 | html_text_get_font_style (slave->owner), slave->owner->face, |
---|
288 | &width, NULL, NULL); |
---|
289 | *w += width; |
---|
290 | aw = *w - new_ltw; |
---|
291 | } |
---|
292 | if (aw <= widthLeft || *force_fit) { |
---|
293 | *ltw = new_ltw; |
---|
294 | *lwl = new_lwl; |
---|
295 | *lbw = aw; |
---|
296 | *lbo = offset; |
---|
297 | *lbsp = s; |
---|
298 | if (*force_fit && *lbw >= widthLeft) |
---|
299 | return TRUE; |
---|
300 | *force_fit = FALSE; |
---|
301 | } else |
---|
302 | return TRUE; |
---|
303 | |
---|
304 | return FALSE; |
---|
305 | } |
---|
306 | |
---|
307 | static HTMLFitType |
---|
308 | hts_fit_line (HTMLObject *o, HTMLPainter *painter, |
---|
309 | gboolean lineBegin, gboolean firstRun, gboolean next_to_floating, gint widthLeft) |
---|
310 | { |
---|
311 | HTMLTextSlave *slave = HTML_TEXT_SLAVE (o); |
---|
312 | gint lbw, w, lbo, ltw, lwl, offset; |
---|
313 | gint ii, io, line_offset; |
---|
314 | gchar *s, *lbsp; |
---|
315 | HTMLFitType rv = HTML_FIT_NONE; |
---|
316 | HTMLTextPangoInfo *pi = html_text_get_pango_info (slave->owner, painter); |
---|
317 | gboolean force_fit = lineBegin; |
---|
318 | |
---|
319 | if (rv == HTML_FIT_COMPLETE) |
---|
320 | return rv; |
---|
321 | |
---|
322 | lbw = ltw = lwl = w = 0; |
---|
323 | offset = lbo = slave->posStart; |
---|
324 | ii = html_text_get_item_index (slave->owner, painter, offset, &io); |
---|
325 | |
---|
326 | line_offset = html_text_get_line_offset (slave->owner, painter, offset); |
---|
327 | lbsp = s = html_text_slave_get_text (slave); |
---|
328 | |
---|
329 | while ((force_fit || widthLeft > lbw) && offset < slave->posStart + slave->posLen) { |
---|
330 | if (offset > slave->posStart && offset > lbo && html_text_is_line_break (pi->attrs [offset])) |
---|
331 | if (update_lb (slave, painter, widthLeft, offset, s, ii, io, line_offset, &w, <w, &lwl, &lbw, &lbo, &lbsp, &force_fit)) |
---|
332 | break; |
---|
333 | |
---|
334 | if (*s == '\t') { |
---|
335 | gint skip = 8 - (line_offset % 8); |
---|
336 | if (HTML_IS_GDK_PAINTER (painter) || HTML_IS_PLAIN_PAINTER (painter)) |
---|
337 | w += skip*PANGO_PIXELS (pi->entries [ii].widths [io]); |
---|
338 | line_offset += skip; |
---|
339 | } else { |
---|
340 | if (HTML_IS_GDK_PAINTER (painter) || HTML_IS_PLAIN_PAINTER (painter)) |
---|
341 | w += PANGO_PIXELS (pi->entries [ii].widths [io]); |
---|
342 | line_offset ++; |
---|
343 | } |
---|
344 | |
---|
345 | s = g_utf8_next_char (s); |
---|
346 | offset ++; |
---|
347 | |
---|
348 | html_text_pi_forward (pi, &ii, &io); |
---|
349 | } |
---|
350 | |
---|
351 | if (!HTML_IS_GDK_PAINTER (painter) && !HTML_IS_PLAIN_PAINTER (painter)) { |
---|
352 | gint aw; |
---|
353 | gint lo = html_text_get_line_offset (slave->owner, painter, lbo); |
---|
354 | |
---|
355 | /* printf ("s: %s l: %d\n", html_text_get_text (slave->owner, lbo - lwl), offset - lbo + lwl); */ |
---|
356 | html_text_calc_text_size (slave->owner, painter, html_text_get_text (slave->owner, lbo) - slave->owner->text, |
---|
357 | offset - lbo, NULL, NULL, &lo, |
---|
358 | html_text_get_font_style (slave->owner), slave->owner->face, |
---|
359 | &aw, NULL, NULL); |
---|
360 | w += aw; |
---|
361 | } |
---|
362 | |
---|
363 | if (offset == slave->posStart + slave->posLen && (widthLeft >= w || force_fit)) { |
---|
364 | rv = HTML_FIT_COMPLETE; |
---|
365 | if (slave->posLen) |
---|
366 | o->width = w; |
---|
367 | } else if (lbo > slave->posStart) { |
---|
368 | split (slave, lbo - slave->posStart, lbsp); |
---|
369 | rv = HTML_FIT_PARTIAL; |
---|
370 | o->width = lbw; |
---|
371 | slave->posLen -= lwl; |
---|
372 | } |
---|
373 | |
---|
374 | return rv; |
---|
375 | } |
---|
376 | |
---|
377 | static gboolean |
---|
378 | select_range (HTMLObject *self, |
---|
379 | HTMLEngine *engine, |
---|
380 | guint start, gint length, |
---|
381 | gboolean queue_draw) |
---|
382 | { |
---|
383 | return FALSE; |
---|
384 | } |
---|
385 | |
---|
386 | static guint |
---|
387 | get_length (HTMLObject *self) |
---|
388 | { |
---|
389 | return 0; |
---|
390 | } |
---|
391 | |
---|
392 | |
---|
393 | /* HTMLObject::draw() implementation. */ |
---|
394 | |
---|
395 | static gint |
---|
396 | get_ys (HTMLText *text, HTMLPainter *p) |
---|
397 | { |
---|
398 | if (text->font_style & GTK_HTML_FONT_STYLE_SUBSCRIPT || text->font_style & GTK_HTML_FONT_STYLE_SUPERSCRIPT) { |
---|
399 | gint height2; |
---|
400 | |
---|
401 | height2 = (HTML_OBJECT (text)->ascent + HTML_OBJECT (text)->descent) / 2; |
---|
402 | /* FIX2? (html_painter_calc_ascent (p, text->font_style, text->face) |
---|
403 | + html_painter_calc_descent (p, text->font_style, text->face)) >> 1; */ |
---|
404 | return (text->font_style & GTK_HTML_FONT_STYLE_SUBSCRIPT) ? height2 : -height2; |
---|
405 | |
---|
406 | } else |
---|
407 | return 0; |
---|
408 | } |
---|
409 | |
---|
410 | static void |
---|
411 | draw_spell_errors (HTMLTextSlave *slave, HTMLPainter *p, gint tx, gint ty) |
---|
412 | { |
---|
413 | GList *cur = HTML_TEXT (slave->owner)->spell_errors; |
---|
414 | HTMLObject *obj = HTML_OBJECT (slave); |
---|
415 | SpellError *se; |
---|
416 | guint ma, mi; |
---|
417 | gint x_off = 0; |
---|
418 | gint last_off = 0; |
---|
419 | gint line_offset = html_text_slave_get_line_offset (slave, 0, p); |
---|
420 | gchar *text = html_text_slave_get_text (slave); |
---|
421 | HTMLEngine *e; |
---|
422 | |
---|
423 | if (p->widget && GTK_IS_HTML (p->widget)) |
---|
424 | e = GTK_HTML (p->widget)->engine; |
---|
425 | else |
---|
426 | return; |
---|
427 | |
---|
428 | while (cur) { |
---|
429 | |
---|
430 | se = (SpellError *) cur->data; |
---|
431 | ma = MAX (se->off, slave->posStart); |
---|
432 | mi = MIN (se->off + se->len, slave->posStart + slave->posLen); |
---|
433 | if (ma < mi) { |
---|
434 | GList *glyphs; |
---|
435 | guint off = ma - slave->posStart; |
---|
436 | guint len = mi - ma; |
---|
437 | gint lo, width, asc, dsc; |
---|
438 | |
---|
439 | html_painter_set_pen (p, &html_colorset_get_color_allocated (e->settings->color_set, |
---|
440 | p, HTMLSpellErrorColor)->color); |
---|
441 | /* printf ("spell error: %s\n", html_text_get_text (slave->owner, off)); */ |
---|
442 | lo = line_offset; |
---|
443 | |
---|
444 | glyphs = get_glyphs_part (slave, p, last_off, off - last_off); |
---|
445 | html_text_calc_text_size (slave->owner, p, text - slave->owner->text, |
---|
446 | off - last_off, html_text_get_pango_info (slave->owner, p), glyphs, |
---|
447 | &line_offset, |
---|
448 | p->font_style, |
---|
449 | p->font_face, &width, &asc, &dsc); |
---|
450 | glyphs_destroy (glyphs); |
---|
451 | x_off += width; |
---|
452 | text = g_utf8_offset_to_pointer (text, off - last_off); |
---|
453 | glyphs = get_glyphs_part (slave, p, off, len); |
---|
454 | x_off += html_painter_draw_spell_error (p, obj->x + tx + x_off, |
---|
455 | obj->y + ty + get_ys (slave->owner, p), |
---|
456 | text, len, html_text_get_pango_info (slave->owner, p), glyphs, text - slave->owner->text); |
---|
457 | glyphs_destroy (glyphs); |
---|
458 | last_off = off + len; |
---|
459 | if (line_offset != -1) |
---|
460 | line_offset += len; |
---|
461 | text = g_utf8_offset_to_pointer (text, len); |
---|
462 | } |
---|
463 | if (se->off > slave->posStart + slave->posLen) |
---|
464 | break; |
---|
465 | cur = cur->next; |
---|
466 | } |
---|
467 | } |
---|
468 | |
---|
469 | static inline GList * |
---|
470 | get_glyphs_base_text (GList *glyphs, PangoItem *item, gint ii, const gchar *text, gint bytes) |
---|
471 | { |
---|
472 | PangoGlyphString *str; |
---|
473 | |
---|
474 | str = pango_glyph_string_new (); |
---|
475 | pango_shape (text, bytes, &item->analysis, str); |
---|
476 | glyphs = g_list_prepend (glyphs, str); |
---|
477 | glyphs = g_list_prepend (glyphs, GINT_TO_POINTER (ii)); |
---|
478 | |
---|
479 | return glyphs; |
---|
480 | } |
---|
481 | |
---|
482 | GList * |
---|
483 | html_get_glyphs_non_tab (GList *glyphs, PangoItem *item, gint ii, const gchar *text, gint bytes, gint len) |
---|
484 | { |
---|
485 | gchar *tab; |
---|
486 | |
---|
487 | while ((tab = memchr (text, (unsigned char) '\t', bytes))) { |
---|
488 | gint c_bytes = tab - text; |
---|
489 | if (c_bytes > 0) |
---|
490 | glyphs = get_glyphs_base_text (glyphs, item, ii, text, c_bytes); |
---|
491 | text += c_bytes + 1; |
---|
492 | bytes -= c_bytes + 1; |
---|
493 | } |
---|
494 | |
---|
495 | if (bytes > 0) |
---|
496 | glyphs = get_glyphs_base_text (glyphs, item, ii, text, bytes); |
---|
497 | |
---|
498 | return glyphs; |
---|
499 | } |
---|
500 | |
---|
501 | static GList * |
---|
502 | get_glyphs_part (HTMLTextSlave *slave, HTMLPainter *painter, guint offset, guint len) |
---|
503 | { |
---|
504 | GList *glyphs = NULL; |
---|
505 | HTMLTextPangoInfo *pi; |
---|
506 | |
---|
507 | pi = html_text_get_pango_info (slave->owner, painter); |
---|
508 | if (pi) { |
---|
509 | PangoItem *item; |
---|
510 | gint index, c_len; |
---|
511 | gint byte_offset, ii; |
---|
512 | const gchar *text, *owner_text; |
---|
513 | gchar *end; |
---|
514 | |
---|
515 | owner_text = slave->owner->text; |
---|
516 | text = g_utf8_offset_to_pointer (html_text_slave_get_text (slave), offset); |
---|
517 | byte_offset = text - owner_text; |
---|
518 | |
---|
519 | ii = html_text_pango_info_get_index (pi, byte_offset, 0); |
---|
520 | index = 0; |
---|
521 | while (index < len) { |
---|
522 | item = pi->entries [ii].item; |
---|
523 | c_len = MIN (item->num_chars - g_utf8_pointer_to_offset (owner_text + item->offset, text), len - index); |
---|
524 | |
---|
525 | end = g_utf8_offset_to_pointer (text, c_len); |
---|
526 | glyphs = html_get_glyphs_non_tab (glyphs, item, ii, text, end - text, c_len); |
---|
527 | text = end; |
---|
528 | index += c_len; |
---|
529 | ii ++; |
---|
530 | } |
---|
531 | glyphs = g_list_reverse (glyphs); |
---|
532 | } |
---|
533 | |
---|
534 | return glyphs; |
---|
535 | } |
---|
536 | |
---|
537 | static GList * |
---|
538 | get_glyphs (HTMLTextSlave *slave, HTMLPainter *painter) |
---|
539 | { |
---|
540 | if (!slave->glyphs || (HTML_OBJECT (slave)->change & HTML_CHANGE_RECALC_PI)) { |
---|
541 | clear_glyphs (slave); |
---|
542 | |
---|
543 | HTML_OBJECT (slave)->change &= ~HTML_CHANGE_RECALC_PI; |
---|
544 | slave->glyphs = get_glyphs_part (slave, painter, 0, slave->posLen); |
---|
545 | } |
---|
546 | |
---|
547 | return slave->glyphs; |
---|
548 | } |
---|
549 | |
---|
550 | |
---|
551 | static void |
---|
552 | draw_normal (HTMLTextSlave *self, |
---|
553 | HTMLPainter *p, |
---|
554 | GtkHTMLFontStyle font_style, |
---|
555 | gint x, gint y, |
---|
556 | gint width, gint height, |
---|
557 | gint tx, gint ty) |
---|
558 | { |
---|
559 | HTMLObject *obj; |
---|
560 | HTMLText *text = self->owner; |
---|
561 | gchar *str; |
---|
562 | |
---|
563 | obj = HTML_OBJECT (self); |
---|
564 | |
---|
565 | str = html_text_slave_get_text (self); |
---|
566 | if (*str) { |
---|
567 | GList *glyphs; |
---|
568 | PangoAttrList *attrs = NULL; |
---|
569 | |
---|
570 | if (self->posStart > 0) |
---|
571 | glyphs = get_glyphs_part (self, p, 0, self->posLen); |
---|
572 | else |
---|
573 | glyphs = get_glyphs (self, p); |
---|
574 | |
---|
575 | if (HTML_IS_PRINTER (p)) { |
---|
576 | HTMLClueFlow *flow = NULL; |
---|
577 | HTMLEngine *e = NULL; |
---|
578 | |
---|
579 | gchar *text = html_text_slave_get_text (self); |
---|
580 | gint start_index, end_index; |
---|
581 | |
---|
582 | start_index = text - self->owner->text; |
---|
583 | end_index = g_utf8_offset_to_pointer (text, self->posLen) - self->owner->text; |
---|
584 | |
---|
585 | attrs = html_text_get_attr_list (self->owner, start_index, end_index); |
---|
586 | |
---|
587 | if (p->widget && GTK_IS_HTML (p->widget)) |
---|
588 | e = GTK_HTML (p->widget)->engine; |
---|
589 | |
---|
590 | if (HTML_OBJECT (self)->parent && HTML_IS_CLUEFLOW (HTML_OBJECT (self)->parent)) |
---|
591 | flow = HTML_CLUEFLOW (HTML_OBJECT (self)->parent); |
---|
592 | |
---|
593 | if (flow && e) |
---|
594 | html_text_change_attrs (attrs, html_clueflow_get_default_font_style (flow), GTK_HTML (p->widget)->engine, |
---|
595 | start_index, end_index, TRUE); |
---|
596 | } |
---|
597 | |
---|
598 | html_painter_draw_text (p, obj->x + tx, obj->y + ty + get_ys (text, p), |
---|
599 | str, self->posLen, html_text_get_pango_info (text, p), attrs, glyphs, |
---|
600 | str - self->owner->text, html_text_slave_get_line_offset (self, 0, p)); |
---|
601 | if (attrs) |
---|
602 | pango_attr_list_unref (attrs); |
---|
603 | |
---|
604 | if (self->posStart > 0) |
---|
605 | glyphs_destroy (glyphs); |
---|
606 | } |
---|
607 | } |
---|
608 | |
---|
609 | static void |
---|
610 | draw_focus (HTMLPainter *painter, GdkRectangle *box) |
---|
611 | { |
---|
612 | HTMLGdkPainter *p; |
---|
613 | GdkGCValues values; |
---|
614 | gchar dash [2]; |
---|
615 | HTMLEngine *e; |
---|
616 | |
---|
617 | if (painter->widget && GTK_IS_HTML (painter->widget)) |
---|
618 | e = GTK_HTML (painter->widget)->engine; |
---|
619 | else |
---|
620 | return; |
---|
621 | |
---|
622 | if (HTML_IS_PRINTER (painter)) |
---|
623 | return; |
---|
624 | |
---|
625 | p = HTML_GDK_PAINTER (painter); |
---|
626 | /* printf ("draw_text_focus\n"); */ |
---|
627 | |
---|
628 | gdk_gc_set_foreground (p->gc, &html_colorset_get_color_allocated (e->settings->color_set, |
---|
629 | painter, HTMLTextColor)->color); |
---|
630 | gdk_gc_get_values (p->gc, &values); |
---|
631 | |
---|
632 | dash [0] = 1; |
---|
633 | dash [1] = 1; |
---|
634 | gdk_gc_set_line_attributes (p->gc, 1, GDK_LINE_ON_OFF_DASH, values.cap_style, values.join_style); |
---|
635 | gdk_gc_set_dashes (p->gc, 2, dash, 2); |
---|
636 | gdk_draw_rectangle (p->pixmap, p->gc, 0, box->x - p->x1, box->y - p->y1, box->width - 1, box->height - 1); |
---|
637 | gdk_gc_set_line_attributes (p->gc, 1, values.line_style, values.cap_style, values.join_style); |
---|
638 | } |
---|
639 | |
---|
640 | static void |
---|
641 | draw (HTMLObject *o, |
---|
642 | HTMLPainter *p, |
---|
643 | gint x, gint y, |
---|
644 | gint width, gint height, |
---|
645 | gint tx, gint ty) |
---|
646 | { |
---|
647 | HTMLTextSlave *textslave; |
---|
648 | HTMLText *owner; |
---|
649 | HTMLText *ownertext; |
---|
650 | GtkHTMLFontStyle font_style; |
---|
651 | guint end; |
---|
652 | GdkRectangle paint; |
---|
653 | |
---|
654 | /* printf ("slave draw %p\n", o); */ |
---|
655 | |
---|
656 | textslave = HTML_TEXT_SLAVE (o); |
---|
657 | if (!html_object_intersect (o, &paint, x, y, width, height) || textslave->posLen == 0) |
---|
658 | return; |
---|
659 | |
---|
660 | owner = textslave->owner; |
---|
661 | ownertext = HTML_TEXT (owner); |
---|
662 | font_style = html_text_get_font_style (ownertext); |
---|
663 | |
---|
664 | end = textslave->posStart + textslave->posLen; |
---|
665 | draw_normal (textslave, p, font_style, x, y, width, height, tx, ty); |
---|
666 | |
---|
667 | if (owner->spell_errors) |
---|
668 | draw_spell_errors (textslave, p, tx ,ty); |
---|
669 | |
---|
670 | if (HTML_OBJECT (owner)->draw_focused) { |
---|
671 | GdkRectangle rect; |
---|
672 | Link *link = html_text_get_link_at_offset (owner, owner->focused_link_offset); |
---|
673 | |
---|
674 | if (link && MAX (link->start_offset, textslave->posStart) < MIN (link->end_offset, textslave->posStart + textslave->posLen)) { |
---|
675 | gint bw = 0; |
---|
676 | html_object_get_bounds (o, &rect); |
---|
677 | if (textslave->posStart < link->start_offset) |
---|
678 | bw = html_text_calc_part_width (owner, p, html_text_slave_get_text (textslave), textslave->posStart, link->start_offset - textslave->posStart, NULL, NULL); |
---|
679 | rect.x += tx + bw; |
---|
680 | rect.width -= bw; |
---|
681 | if (textslave->posStart + textslave->posLen > link->end_offset) |
---|
682 | rect.width -= html_text_calc_part_width (owner, p, owner->text + link->end_index, link->end_offset, textslave->posStart + textslave->posLen - link->end_offset, NULL, NULL); |
---|
683 | rect.y += ty; |
---|
684 | draw_focus (p, &rect); |
---|
685 | } |
---|
686 | } |
---|
687 | } |
---|
688 | |
---|
689 | static gint |
---|
690 | calc_min_width (HTMLObject *o, |
---|
691 | HTMLPainter *painter) |
---|
692 | { |
---|
693 | return 0; |
---|
694 | } |
---|
695 | |
---|
696 | static gint |
---|
697 | calc_preferred_width (HTMLObject *o, |
---|
698 | HTMLPainter *painter) |
---|
699 | { |
---|
700 | return 0; |
---|
701 | } |
---|
702 | |
---|
703 | static const gchar * |
---|
704 | get_url (HTMLObject *o, gint offset) |
---|
705 | { |
---|
706 | HTMLTextSlave *slave; |
---|
707 | |
---|
708 | slave = HTML_TEXT_SLAVE (o); |
---|
709 | return html_object_get_url (HTML_OBJECT (slave->owner), offset); |
---|
710 | } |
---|
711 | |
---|
712 | static gint |
---|
713 | calc_offset (HTMLTextSlave *slave, HTMLPainter *painter, gint x, gint y) |
---|
714 | { |
---|
715 | HTMLText *owner; |
---|
716 | GtkHTMLFontStyle font_style; |
---|
717 | gint line_offset; |
---|
718 | guint width, prev_width; |
---|
719 | gchar *text; |
---|
720 | guint upper; |
---|
721 | guint len; |
---|
722 | guint lower; |
---|
723 | GList *glyphs; |
---|
724 | gint lo; |
---|
725 | gint asc, dsc; |
---|
726 | |
---|
727 | g_assert (slave->posLen > 1); |
---|
728 | |
---|
729 | width = 0; |
---|
730 | prev_width = 0; |
---|
731 | lower = 0; |
---|
732 | upper = slave->posLen; |
---|
733 | len = 0; |
---|
734 | |
---|
735 | text = html_text_slave_get_text (slave); |
---|
736 | line_offset = html_text_slave_get_line_offset (slave, 0, painter); |
---|
737 | owner = HTML_TEXT (slave->owner); |
---|
738 | font_style = html_text_get_font_style (owner); |
---|
739 | |
---|
740 | while (upper - lower > 1) { |
---|
741 | lo = line_offset; |
---|
742 | prev_width = width; |
---|
743 | |
---|
744 | if (width > x) |
---|
745 | upper = len; |
---|
746 | else |
---|
747 | lower = len; |
---|
748 | |
---|
749 | len = (lower + upper + 1) / 2; |
---|
750 | |
---|
751 | if (len) { |
---|
752 | glyphs = get_glyphs_part (slave, painter, 0, len); |
---|
753 | html_text_calc_text_size (slave->owner, painter, text - slave->owner->text, len, html_text_get_pango_info (owner, painter), glyphs, |
---|
754 | &lo, font_style, owner->face, &width, &asc, &dsc); |
---|
755 | glyphs_destroy (glyphs); |
---|
756 | } else { |
---|
757 | width = 0; |
---|
758 | } |
---|
759 | } |
---|
760 | |
---|
761 | if ((width + prev_width) / 2 >= x) |
---|
762 | len--; |
---|
763 | |
---|
764 | return len; |
---|
765 | } |
---|
766 | |
---|
767 | static guint |
---|
768 | get_offset_for_pointer (HTMLTextSlave *slave, HTMLPainter *painter, gint x, gint y) |
---|
769 | { |
---|
770 | g_return_val_if_fail (slave != NULL, 0); |
---|
771 | |
---|
772 | x -= HTML_OBJECT (slave)->x; |
---|
773 | |
---|
774 | if (x <= 0) |
---|
775 | return 0; |
---|
776 | |
---|
777 | if (x >= HTML_OBJECT (slave)->width - 1) |
---|
778 | return slave->posLen; |
---|
779 | |
---|
780 | if (slave->posLen > 1) |
---|
781 | return calc_offset (slave, painter, x, y); |
---|
782 | else |
---|
783 | return x > HTML_OBJECT (slave)->width / 2 ? 1 : 0; |
---|
784 | } |
---|
785 | |
---|
786 | static HTMLObject * |
---|
787 | check_point (HTMLObject *self, |
---|
788 | HTMLPainter *painter, |
---|
789 | gint x, gint y, |
---|
790 | guint *offset_return, |
---|
791 | gboolean for_cursor) |
---|
792 | { |
---|
793 | if (x >= self->x |
---|
794 | && x < self->x + MAX (1, self->width) |
---|
795 | && y >= self->y - self->ascent |
---|
796 | && y < self->y + self->descent) { |
---|
797 | HTMLTextSlave *slave = HTML_TEXT_SLAVE (self); |
---|
798 | |
---|
799 | if (offset_return != NULL) |
---|
800 | *offset_return = slave->posStart |
---|
801 | + get_offset_for_pointer (slave, painter, x, y); |
---|
802 | |
---|
803 | return HTML_OBJECT (slave->owner); |
---|
804 | } |
---|
805 | |
---|
806 | return NULL; |
---|
807 | } |
---|
808 | |
---|
809 | static void |
---|
810 | clear_glyphs (HTMLTextSlave *slave) |
---|
811 | { |
---|
812 | if (slave->glyphs) { |
---|
813 | glyphs_destroy (slave->glyphs); |
---|
814 | slave->glyphs = NULL; |
---|
815 | } |
---|
816 | } |
---|
817 | |
---|
818 | static void |
---|
819 | destroy (HTMLObject *obj) |
---|
820 | { |
---|
821 | HTMLTextSlave *slave = HTML_TEXT_SLAVE (obj); |
---|
822 | |
---|
823 | clear_glyphs (slave); |
---|
824 | |
---|
825 | HTML_OBJECT_CLASS (parent_class)->destroy (obj); |
---|
826 | } |
---|
827 | |
---|
828 | void |
---|
829 | html_text_slave_type_init (void) |
---|
830 | { |
---|
831 | html_text_slave_class_init (&html_text_slave_class, HTML_TYPE_TEXTSLAVE, sizeof (HTMLTextSlave)); |
---|
832 | } |
---|
833 | |
---|
834 | void |
---|
835 | html_text_slave_class_init (HTMLTextSlaveClass *klass, |
---|
836 | HTMLType type, |
---|
837 | guint object_size) |
---|
838 | { |
---|
839 | HTMLObjectClass *object_class; |
---|
840 | |
---|
841 | object_class = HTML_OBJECT_CLASS (klass); |
---|
842 | |
---|
843 | html_object_class_init (object_class, type, object_size); |
---|
844 | |
---|
845 | object_class->select_range = select_range; |
---|
846 | object_class->copy = copy; |
---|
847 | object_class->destroy = destroy; |
---|
848 | object_class->draw = draw; |
---|
849 | object_class->calc_size = html_text_slave_real_calc_size; |
---|
850 | object_class->fit_line = hts_fit_line; |
---|
851 | object_class->calc_min_width = calc_min_width; |
---|
852 | object_class->calc_preferred_width = calc_preferred_width; |
---|
853 | object_class->get_url = get_url; |
---|
854 | object_class->get_length = get_length; |
---|
855 | object_class->check_point = check_point; |
---|
856 | |
---|
857 | parent_class = &html_object_class; |
---|
858 | } |
---|
859 | |
---|
860 | void |
---|
861 | html_text_slave_init (HTMLTextSlave *slave, |
---|
862 | HTMLTextSlaveClass *klass, |
---|
863 | HTMLText *owner, |
---|
864 | guint posStart, |
---|
865 | guint posLen) |
---|
866 | { |
---|
867 | HTMLText *owner_text; |
---|
868 | HTMLObject *object; |
---|
869 | |
---|
870 | object = HTML_OBJECT (slave); |
---|
871 | owner_text = HTML_TEXT (owner); |
---|
872 | |
---|
873 | html_object_init (object, HTML_OBJECT_CLASS (klass)); |
---|
874 | |
---|
875 | object->ascent = HTML_OBJECT (owner)->ascent; |
---|
876 | object->descent = HTML_OBJECT (owner)->descent; |
---|
877 | |
---|
878 | slave->posStart = posStart; |
---|
879 | slave->posLen = posLen; |
---|
880 | slave->owner = owner; |
---|
881 | slave->charStart = NULL; |
---|
882 | slave->pi = NULL; |
---|
883 | slave->glyphs = NULL; |
---|
884 | |
---|
885 | /* text slaves have always min_width 0 */ |
---|
886 | object->min_width = 0; |
---|
887 | object->change &= ~HTML_CHANGE_MIN_WIDTH; |
---|
888 | } |
---|
889 | |
---|
890 | HTMLObject * |
---|
891 | html_text_slave_new (HTMLText *owner, guint posStart, guint posLen) |
---|
892 | { |
---|
893 | HTMLTextSlave *slave; |
---|
894 | |
---|
895 | slave = g_new (HTMLTextSlave, 1); |
---|
896 | html_text_slave_init (slave, &html_text_slave_class, owner, posStart, posLen); |
---|
897 | |
---|
898 | return HTML_OBJECT (slave); |
---|
899 | } |
---|