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., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, 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 void clear_glyph_items (HTMLTextSlave *slave);
49
50 gchar *
html_text_slave_get_text(HTMLTextSlave * slave)51 html_text_slave_get_text (HTMLTextSlave *slave)
52 {
53 if (!slave->charStart)
54 slave->charStart = html_text_get_text (slave->owner, slave->posStart);
55
56 return slave->charStart;
57 }
58
59 /* Split this TextSlave at the specified offset. */
60 static void
split(HTMLTextSlave * slave,guint offset,gint skip,gchar * start_pointer)61 split (HTMLTextSlave *slave,
62 guint offset,
63 gint skip,
64 gchar *start_pointer)
65 {
66 HTMLObject *obj;
67 HTMLObject *new;
68
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 + skip,
75 slave->posLen - (offset + skip));
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
copy(HTMLObject * self,HTMLObject * dest)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 HTML_TEXT_SLAVE (dest)->glyph_items = NULL;
98 }
99
100 static inline gint
html_text_slave_get_start_byte_offset(HTMLTextSlave * slave)101 html_text_slave_get_start_byte_offset (HTMLTextSlave *slave)
102 {
103 return html_text_slave_get_text (slave) - slave->owner->text;
104 }
105
106 static void
hts_update_asc_dsc(HTMLPainter * painter,PangoItem * item,gint * asc,gint * dsc)107 hts_update_asc_dsc (HTMLPainter *painter,
108 PangoItem *item,
109 gint *asc,
110 gint *dsc)
111 {
112 PangoFontMetrics *pfm;
113
114 pfm = pango_font_get_metrics (item->analysis.font, item->analysis.language);
115 if (asc)
116 *asc = MAX (*asc, pango_font_metrics_get_ascent (pfm));
117 if (dsc)
118 *dsc = MAX (*dsc, pango_font_metrics_get_descent (pfm));
119 pango_font_metrics_unref (pfm);
120 }
121
122 static gint
hts_calc_width(HTMLTextSlave * slave,HTMLPainter * painter,gint * asc,gint * dsc)123 hts_calc_width (HTMLTextSlave *slave,
124 HTMLPainter *painter,
125 gint *asc,
126 gint *dsc)
127 {
128 /*HTMLText *text = slave->owner;
129 gint line_offset, tabs = 0, width = 0;
130 *
131 line_offset = html_text_slave_get_line_offset (slave, 0, painter);
132 if (line_offset != -1)
133 width += (html_text_text_line_length (html_text_slave_get_text (slave), &line_offset, slave->posLen, &tabs) - slave->posLen)*
134 html_painter_get_space_width (painter, html_text_get_font_style (text), text->face);
135 *
136 width += html_text_calc_part_width (text, painter, html_text_slave_get_text (slave), slave->posStart, slave->posLen, asc, dsc); */
137
138 GSList *cur, *gilist = html_text_slave_get_glyph_items (slave, painter);
139 PangoLanguage *language = NULL;
140 PangoFont *font = NULL;
141 gint width = 0;
142
143 if (asc)
144 *asc = html_painter_engine_to_pango (painter,
145 html_painter_get_space_asc (painter, html_text_get_font_style (slave->owner), slave->owner->face));
146 if (dsc)
147 *dsc = html_painter_engine_to_pango (painter,
148 html_painter_get_space_dsc (painter, html_text_get_font_style (slave->owner), slave->owner->face));
149
150 for (cur = gilist; cur; cur = cur->next) {
151 HTMLTextSlaveGlyphItem *sgi = (HTMLTextSlaveGlyphItem *) cur->data;
152 PangoRectangle log_rect;
153 PangoItem *item = sgi->glyph_item.item;
154
155 pango_glyph_string_extents (sgi->glyph_item.glyphs, sgi->glyph_item.item->analysis.font, NULL, &log_rect);
156 width += log_rect.width;
157
158 if (item->analysis.font != font || item->analysis.language != language)
159 hts_update_asc_dsc (painter, item, asc, dsc);
160 }
161
162 if (asc)
163 *asc = html_painter_pango_to_engine (painter, *asc);
164 if (dsc)
165 *dsc = html_painter_pango_to_engine (painter, *dsc);
166
167 width = html_painter_pango_to_engine (painter, width);
168
169 /* printf ("hts width: %d\n", width); */
170
171 return width;
172 }
173
174 inline static void
glyphs_destroy(GList * glyphs)175 glyphs_destroy (GList *glyphs)
176 {
177 GList *l;
178
179 for (l = glyphs; l; l = l->next->next)
180 pango_glyph_string_free ((PangoGlyphString *) l->data);
181 g_list_free (glyphs);
182 }
183
184 inline static void
glyph_items_destroy(GSList * glyph_items)185 glyph_items_destroy (GSList *glyph_items)
186 {
187 GSList *l;
188
189 for (l = glyph_items; l; l = l->next) {
190 HTMLTextSlaveGlyphItem *gi = (HTMLTextSlaveGlyphItem *) l->data;
191
192 if (gi->type == HTML_TEXT_SLAVE_GLYPH_ITEM_CREATED) {
193 if (gi->glyph_item.item)
194 pango_item_free (gi->glyph_item.item);
195 if (gi->glyph_item.glyphs)
196 pango_glyph_string_free (gi->glyph_item.glyphs);
197 g_free (gi->widths);
198 }
199
200 g_free (gi);
201 }
202 g_slist_free (glyph_items);
203 }
204
205 static gboolean
html_text_slave_real_calc_size(HTMLObject * self,HTMLPainter * painter,GList ** changed_objs)206 html_text_slave_real_calc_size (HTMLObject *self,
207 HTMLPainter *painter,
208 GList **changed_objs)
209 {
210 HTMLText *owner;
211 HTMLTextSlave *slave;
212 GtkHTMLFontStyle font_style;
213 gint new_ascent, new_descent, new_width;
214 gboolean changed;
215
216 slave = HTML_TEXT_SLAVE (self);
217 owner = HTML_TEXT (slave->owner);
218 font_style = html_text_get_font_style (owner);
219
220 new_width = MAX (1, hts_calc_width (slave, painter, &new_ascent, &new_descent));
221
222 /* handle sub & super script */
223 if (font_style & GTK_HTML_FONT_STYLE_SUBSCRIPT || font_style & GTK_HTML_FONT_STYLE_SUPERSCRIPT) {
224 gint shift = (new_ascent + new_descent) >> 1;
225
226 if (font_style & GTK_HTML_FONT_STYLE_SUBSCRIPT) {
227 new_descent += shift;
228 new_ascent -= shift;
229 } else {
230 new_descent -= shift;
231 new_ascent += shift;
232 }
233 }
234
235 changed = FALSE;
236
237 if (new_ascent != self->ascent) {
238 self->ascent = new_ascent;
239 changed = TRUE;
240 }
241
242 if (new_descent != self->descent) {
243 self->descent = new_descent;
244 changed = TRUE;
245 }
246
247 if (new_width != self->width) {
248 self->width = new_width;
249 changed = TRUE;
250 }
251
252 return changed;
253 }
254
255 #ifdef HTML_TEXT_SLAVE_DEBUG
256 static void
debug_print(HTMLFitType rv,gchar * text,gint len)257 debug_print (HTMLFitType rv,
258 gchar *text,
259 gint len)
260 {
261 gint i;
262
263 printf ("Split text");
264 switch (rv) {
265 case HTML_FIT_PARTIAL:
266 printf (" (Partial): `");
267 break;
268 case HTML_FIT_NONE:
269 printf (" (NoFit): `");
270 break;
271 case HTML_FIT_COMPLETE:
272 printf (" (Complete): `");
273 break;
274 }
275
276 for (i = 0; i < len; i++)
277 putchar (text[i]);
278
279 printf ("'\n");
280 }
281 #endif
282
283 gint
html_text_slave_get_line_offset(HTMLTextSlave * slave,gint offset,HTMLPainter * p)284 html_text_slave_get_line_offset (HTMLTextSlave *slave,
285 gint offset,
286 HTMLPainter *p)
287 {
288 HTMLObject *head = HTML_OBJECT (slave->owner)->next;
289
290 g_assert (HTML_IS_TEXT_SLAVE (head));
291
292 if (!html_clueflow_tabs (HTML_CLUEFLOW (HTML_OBJECT (slave)->parent), p))
293 return -1;
294
295 if (head->y + head->descent - 1 < HTML_OBJECT (slave)->y - HTML_OBJECT (slave)->ascent) {
296 HTMLObject *prev;
297 HTMLTextSlave *bol;
298 gint line_offset = 0;
299
300 prev = html_object_prev (HTML_OBJECT (slave)->parent, HTML_OBJECT (slave));
301 while (prev->y + prev->descent - 1 >= HTML_OBJECT (slave)->y - HTML_OBJECT (slave)->ascent)
302 prev = html_object_prev (HTML_OBJECT (slave)->parent, HTML_OBJECT (prev));
303
304 bol = HTML_TEXT_SLAVE (prev->next);
305 return html_text_text_line_length (html_text_slave_get_text (bol),
306 &line_offset, slave->posStart + offset - bol->posStart, NULL);
307 } else
308 return html_text_get_line_offset (slave->owner, p, slave->posStart + offset);
309 }
310
311 static gboolean
could_remove_leading_space(HTMLTextSlave * slave,gboolean lineBegin)312 could_remove_leading_space (HTMLTextSlave *slave,
313 gboolean lineBegin)
314 {
315 HTMLObject *o = HTML_OBJECT (slave->owner);
316
317 if (lineBegin && (HTML_OBJECT (slave)->prev != o || o->prev))
318 return TRUE;
319
320 if (!o->prev)
321 return FALSE;
322
323 while (o->prev && HTML_OBJECT_TYPE (o->prev) == HTML_TYPE_CLUEALIGNED)
324 o = o->prev;
325
326 return o->prev ? FALSE : TRUE;
327 }
328
329 gchar *
html_text_slave_remove_leading_space(HTMLTextSlave * slave,HTMLPainter * painter,gboolean lineBegin)330 html_text_slave_remove_leading_space (HTMLTextSlave *slave,
331 HTMLPainter *painter,
332 gboolean lineBegin)
333 {
334 gchar *begin;
335
336 begin = html_text_slave_get_text (slave);
337 if (*begin == ' ' && could_remove_leading_space (slave, lineBegin)) {
338 begin = g_utf8_next_char (begin);
339 slave->charStart = begin;
340 slave->posStart++;
341 slave->posLen--;
342 }
343
344 return begin;
345 }
346
347 gint
html_text_slave_get_nb_width(HTMLTextSlave * slave,HTMLPainter * painter,gboolean lineBegin)348 html_text_slave_get_nb_width (HTMLTextSlave *slave,
349 HTMLPainter *painter,
350 gboolean lineBegin)
351 {
352 html_text_slave_remove_leading_space (slave, painter, lineBegin);
353
354 return html_object_calc_min_width (HTML_OBJECT (slave), painter);
355 }
356
357 /*
358 * offset: current position - gchar offset
359 * s: current position - string pointer
360 * ii: current position - index of item
361 * io: current position - offset within item
362 * line_offset:
363 * w: width for current position
364 * lwl: last-whitespacing-length (in characters)
365 * lbw: last-break-width
366 * lbo: last-break-offset (into text)
367 * lbsp: last-break-string-pointer
368 *
369 * Checks to see if breaking the text at the given position would result in it fitting
370 * into the remaining width. If so, updates the last-break information (lwl,lbw,lbo,lbsp).
371 * We'll actually break the item at the last break position that still fits.
372 */
373 static gboolean
update_lb(HTMLTextSlave * slave,HTMLPainter * painter,gint widthLeft,gint offset,gchar * s,gint ii,gint io,gint line_offset,gint w,gint * lwl,gint * lbw,gint * lbo,gchar ** lbsp,gboolean * force_fit)374 update_lb (HTMLTextSlave *slave,
375 HTMLPainter *painter,
376 gint widthLeft,
377 gint offset,
378 gchar *s,
379 gint ii,
380 gint io,
381 gint line_offset,
382 gint w,
383 gint *lwl,
384 gint *lbw,
385 gint *lbo,
386 gchar **lbsp,
387 gboolean *force_fit)
388 {
389 gint new_ltw, new_lwl, aw;
390
391 new_ltw = html_text_tail_white_space (slave->owner, painter, offset, ii, io, &new_lwl, line_offset, s);
392 aw = w - new_ltw;
393
394 if (aw <= widthLeft || *force_fit) {
395 *lwl = new_lwl;
396 *lbw = aw;
397 *lbo = offset;
398 *lbsp = s;
399 if (*force_fit && *lbw >= widthLeft)
400 return TRUE;
401 *force_fit = FALSE;
402 } else
403 return TRUE;
404
405 return FALSE;
406 }
407
408 static HTMLFitType
hts_fit_line(HTMLObject * o,HTMLPainter * painter,gboolean lineBegin,gboolean firstRun,gboolean next_to_floating,gint widthLeft)409 hts_fit_line (HTMLObject *o,
410 HTMLPainter *painter,
411 gboolean lineBegin,
412 gboolean firstRun,
413 gboolean next_to_floating,
414 gint widthLeft)
415 {
416 HTMLTextSlave *slave = HTML_TEXT_SLAVE (o);
417 gint lbw, w, lbo, lwl, offset;
418 gint ii, io, line_offset;
419 gchar *s, *lbsp;
420 HTMLFitType rv = HTML_FIT_NONE;
421 HTMLTextPangoInfo *pi = html_text_get_pango_info (slave->owner, painter);
422 gboolean force_fit = lineBegin;
423
424 if (slave->posLen == 0)
425 return HTML_FIT_COMPLETE;
426
427 widthLeft = html_painter_engine_to_pango (painter, widthLeft);
428
429 lbw = lwl = w = 0;
430 offset = lbo = slave->posStart;
431 ii = html_text_get_item_index (slave->owner, painter, offset, &io);
432
433 line_offset = html_text_get_line_offset (slave->owner, painter, offset);
434 lbsp = s = html_text_slave_get_text (slave);
435
436 while ((force_fit || widthLeft > lbw) && offset < slave->posStart + slave->posLen) {
437 if (offset > slave->posStart && offset > lbo && html_text_is_line_break (pi->attrs[offset]))
438 if (update_lb (slave, painter, widthLeft, offset, s, ii, io, line_offset, w, &lwl, &lbw, &lbo, &lbsp, &force_fit))
439 break;
440
441 if (io == 0 && slave->owner->text[pi->entries[ii].glyph_item.item->offset] == '\t') {
442 GtkHTMLFontStyle font_style;
443 gchar *face;
444 gint skip = 8 - (line_offset % 8);
445
446 if (HTML_IS_PLAIN_PAINTER (painter)) {
447 font_style = GTK_HTML_FONT_STYLE_FIXED;
448 face = NULL;
449 } else {
450 font_style = html_text_get_font_style (slave->owner);
451 face = slave->owner->face;
452 }
453
454 pi->entries[ii].glyph_item.glyphs->glyphs[0].geometry.width = pi->entries[ii].widths[io]
455 = skip * html_painter_get_space_width (painter, font_style, face) * PANGO_SCALE;
456 line_offset += skip;
457 } else {
458 line_offset++;
459 }
460 w += pi->entries[ii].widths[io];
461
462 html_text_pi_forward (pi, &ii, &io);
463 s = g_utf8_next_char (s);
464 offset++;
465 }
466
467 if (offset == slave->posStart + slave->posLen && (widthLeft >= w || force_fit)) {
468 rv = HTML_FIT_COMPLETE;
469 if (slave->posLen)
470 o->width = html_painter_pango_to_engine (painter, w);
471 } else if (lbo > slave->posStart) {
472 split (slave, lbo - slave->posStart - lwl, lwl, lbsp);
473 rv = HTML_FIT_PARTIAL;
474 o->width = html_painter_pango_to_engine (painter, lbw);
475 o->change |= HTML_CHANGE_RECALC_PI;
476 }
477
478 return rv;
479 }
480
481 static gboolean
select_range(HTMLObject * self,HTMLEngine * engine,guint start,gint length,gboolean queue_draw)482 select_range (HTMLObject *self,
483 HTMLEngine *engine,
484 guint start,
485 gint length,
486 gboolean queue_draw)
487 {
488 return FALSE;
489 }
490
491 static guint
get_length(HTMLObject * self)492 get_length (HTMLObject *self)
493 {
494 return 0;
495 }
496
497
498 /* HTMLObject::draw() implementation. */
499
500 static gint
get_ys(HTMLText * text,HTMLPainter * p)501 get_ys (HTMLText *text,
502 HTMLPainter *p)
503 {
504 if (text->font_style & GTK_HTML_FONT_STYLE_SUBSCRIPT || text->font_style & GTK_HTML_FONT_STYLE_SUPERSCRIPT) {
505 gint height2;
506
507 height2 = (HTML_OBJECT (text)->ascent + HTML_OBJECT (text)->descent) / 2;
508 /* FIX2? (html_painter_calc_ascent (p, text->font_style, text->face)
509 * + html_painter_calc_descent (p, text->font_style, text->face)) >> 1; */
510 return (text->font_style & GTK_HTML_FONT_STYLE_SUBSCRIPT) ? height2 : -height2;
511
512 } else
513 return 0;
514 }
515
516 static inline GList *
get_glyphs_base_text(GList * glyphs,PangoItem * item,gint ii,const gchar * text,gint bytes)517 get_glyphs_base_text (GList *glyphs,
518 PangoItem *item,
519 gint ii,
520 const gchar *text,
521 gint bytes)
522 {
523 PangoGlyphString *str;
524
525 str = pango_glyph_string_new ();
526 pango_shape (text, bytes, &item->analysis, str);
527 glyphs = g_list_prepend (glyphs, str);
528 glyphs = g_list_prepend (glyphs, GINT_TO_POINTER (ii));
529
530 return glyphs;
531 }
532
533 GList *
html_get_glyphs_non_tab(GList * glyphs,PangoItem * item,gint ii,const gchar * text,gint bytes,gint len)534 html_get_glyphs_non_tab (GList *glyphs,
535 PangoItem *item,
536 gint ii,
537 const gchar *text,
538 gint bytes,
539 gint len)
540 {
541 gchar *tab;
542
543 while ((tab = memchr (text, (guchar) '\t', bytes))) {
544 gint c_bytes = tab - text;
545 if (c_bytes > 0)
546 glyphs = get_glyphs_base_text (glyphs, item, ii, text, c_bytes);
547 text += c_bytes + 1;
548 bytes -= c_bytes + 1;
549 }
550
551 if (bytes > 0)
552 glyphs = get_glyphs_base_text (glyphs, item, ii, text, bytes);
553
554 return glyphs;
555 }
556
557 /*
558 * NB: This implement the exact same algorithm as
559 * reorder-items.c:pango_reorder_items().
560 */
561
562 static GSList *
reorder_glyph_items(GSList * glyph_items,gint n_items)563 reorder_glyph_items (GSList *glyph_items,
564 gint n_items)
565 {
566 GSList *tmp_list, *level_start_node;
567 gint i, level_start_i;
568 gint min_level = G_MAXINT;
569 GSList *result = NULL;
570
571 if (n_items == 0)
572 return NULL;
573
574 tmp_list = glyph_items;
575 for (i = 0; i < n_items; i++) {
576 HTMLTextSlaveGlyphItem *gi = tmp_list->data;
577
578 min_level = MIN (min_level, gi->glyph_item.item->analysis.level);
579
580 tmp_list = tmp_list->next;
581 }
582
583 level_start_i = 0;
584 level_start_node = glyph_items;
585 tmp_list = glyph_items;
586 for (i = 0; i < n_items; i++) {
587 HTMLTextSlaveGlyphItem *gi= tmp_list->data;
588
589 if (gi->glyph_item.item->analysis.level == min_level) {
590 if (min_level % 2) {
591 if (i > level_start_i)
592 result = g_slist_concat (reorder_glyph_items (level_start_node, i - level_start_i), result);
593 result = g_slist_prepend (result, gi);
594 } else {
595 if (i > level_start_i)
596 result = g_slist_concat (result, reorder_glyph_items (level_start_node, i - level_start_i));
597 result = g_slist_append (result, gi);
598 }
599
600 level_start_i = i + 1;
601 level_start_node = tmp_list->next;
602 }
603
604 tmp_list = tmp_list->next;
605 }
606
607 if (min_level % 2) {
608 if (i > level_start_i)
609 result = g_slist_concat (reorder_glyph_items (level_start_node, i - level_start_i), result);
610 } else {
611 if (i > level_start_i)
612 result = g_slist_concat (result, reorder_glyph_items (level_start_node, i - level_start_i));
613 }
614
615 return result;
616 }
617
618 static GSList *
get_glyph_items_in_range(HTMLTextSlave * slave,HTMLPainter * painter,gint start_offset,gint len)619 get_glyph_items_in_range (HTMLTextSlave *slave,
620 HTMLPainter *painter,
621 gint start_offset,
622 gint len)
623 {
624 HTMLTextPangoInfo *pi = html_text_get_pango_info (slave->owner, painter);
625 gint i, offset, end_offset, n_items = 0;
626 GSList *glyph_items = NULL;
627
628 start_offset += slave->posStart;
629 end_offset = start_offset + len;
630
631 for (offset = 0, i = 0; i < pi->n; i++) {
632 PangoItem *item = pi->entries[i].glyph_item.item;
633
634 /* do item and slave overlap? */
635 if (MAX (offset, start_offset) < MIN (offset + item->num_chars, end_offset)) {
636 HTMLTextSlaveGlyphItem *glyph_item = g_new (HTMLTextSlaveGlyphItem, 1);
637
638 /* use text glyph item */
639 glyph_item->type = HTML_TEXT_SLAVE_GLYPH_ITEM_PARENTAL;
640 glyph_item->glyph_item = pi->entries[i].glyph_item;
641 glyph_item->widths = pi->entries[i].widths;
642
643 if (start_offset > offset) {
644 /* need to cut the beginning of current glyph item */
645 PangoGlyphItem *tmp_gi;
646 gint split_index;
647
648 glyph_item->type = HTML_TEXT_SLAVE_GLYPH_ITEM_CREATED;
649 glyph_item->widths = NULL;
650 glyph_item->glyph_item.item = pango_item_copy (glyph_item->glyph_item.item);
651 glyph_item->glyph_item.glyphs = pango_glyph_string_copy (glyph_item->glyph_item.glyphs);
652
653 split_index = g_utf8_offset_to_pointer (slave->owner->text + item->offset, start_offset - offset)
654 - (slave->owner->text + item->offset);
655 tmp_gi = pango_glyph_item_split (&glyph_item->glyph_item, slave->owner->text, split_index);
656
657 /* free the beginning we don't need */
658 pango_glyph_item_free (tmp_gi);
659
660 }
661
662 if (offset + item->num_chars > end_offset) {
663 /* need to cut the ending of current glyph item */
664 PangoGlyphItem tmp_gi1, *tmp_gi2;
665 gint split_index;
666
667 if (glyph_item->type == HTML_TEXT_SLAVE_GLYPH_ITEM_PARENTAL) {
668 tmp_gi1.item = pango_item_copy (glyph_item->glyph_item.item);
669 tmp_gi1.glyphs = pango_glyph_string_copy (glyph_item->glyph_item.glyphs);
670 } else
671 tmp_gi1 = glyph_item->glyph_item;
672
673 split_index = g_utf8_offset_to_pointer (slave->owner->text + tmp_gi1.item->offset, end_offset - MAX (start_offset, offset))
674 - (slave->owner->text + tmp_gi1.item->offset);
675 tmp_gi2 = pango_glyph_item_split (&tmp_gi1, slave->owner->text, split_index);
676
677 glyph_item->glyph_item = *tmp_gi2;
678 tmp_gi2->item = NULL;
679 tmp_gi2->glyphs = NULL;
680
681 /* free the tmp1 content and tmp2 container, but not the content */
682 pango_item_free (tmp_gi1.item);
683 pango_glyph_string_free (tmp_gi1.glyphs);
684 pango_glyph_item_free (tmp_gi2);
685
686 glyph_item->type = HTML_TEXT_SLAVE_GLYPH_ITEM_CREATED;
687 glyph_item->widths = NULL;
688 }
689
690 glyph_items = g_slist_prepend (glyph_items, glyph_item);
691 n_items++;
692 }
693
694 if (offset + item->num_chars >= end_offset)
695 break;
696 offset += item->num_chars;
697 }
698
699 if (glyph_items) {
700 GSList *reversed;
701
702 reversed = g_slist_reverse (glyph_items);
703 glyph_items = reorder_glyph_items (reversed, n_items);
704 g_slist_free (reversed);
705 }
706
707 return glyph_items;
708 }
709
710 GSList *
html_text_slave_get_glyph_items(HTMLTextSlave * slave,HTMLPainter * painter)711 html_text_slave_get_glyph_items (HTMLTextSlave *slave,
712 HTMLPainter *painter)
713 {
714 if (painter && (!slave->glyph_items || (HTML_OBJECT (slave)->change & HTML_CHANGE_RECALC_PI))) {
715 clear_glyph_items (slave);
716
717 HTML_OBJECT (slave)->change &= ~HTML_CHANGE_RECALC_PI;
718 slave->glyph_items = get_glyph_items_in_range (slave, painter, 0, slave->posLen);
719 }
720
721 return slave->glyph_items;
722 }
723
724 static gboolean
calc_glyph_range_size(HTMLText * text,PangoGlyphItem * glyph_item,gint start_index,gint end_index,gint * x_offset,gint * width,gint * asc,gint * height)725 calc_glyph_range_size (HTMLText *text,
726 PangoGlyphItem *glyph_item,
727 gint start_index,
728 gint end_index,
729 gint *x_offset,
730 gint *width,
731 gint *asc,
732 gint *height)
733 {
734 gint isect_start, isect_end;
735
736 isect_start = MAX (glyph_item->item->offset, start_index);
737 isect_end = MIN (glyph_item->item->offset + glyph_item->item->length, end_index);
738
739 isect_start -= glyph_item->item->offset;
740 isect_end -= glyph_item->item->offset;
741
742 /* printf ("calc_glyph_range_size isect start %d end %d (end_index %d)\n", isect_start, isect_end, end_index); */
743
744 if (isect_start <= isect_end) {
745 PangoRectangle log_rect;
746 gint start_x, end_x;
747
748 pango_glyph_string_index_to_x (glyph_item->glyphs,
749 text->text + glyph_item->item->offset,
750 glyph_item->item->length,
751 &glyph_item->item->analysis,
752 isect_start,
753 FALSE, &start_x);
754
755 if (isect_start < isect_end)
756 pango_glyph_string_index_to_x (glyph_item->glyphs,
757 text->text + glyph_item->item->offset,
758 glyph_item->item->length,
759 &glyph_item->item->analysis,
760 isect_end,
761 FALSE, &end_x);
762 else
763 end_x = start_x;
764
765 if (asc || height)
766 /* this call is used only to get ascent and height */
767 pango_glyph_string_extents (glyph_item->glyphs, glyph_item->item->analysis.font, NULL, &log_rect);
768
769 /* printf ("selection_start_index %d selection_end_index %d isect_start %d isect_end %d start_x %d end_x %d cwidth %d width %d\n",
770 * selection_start_index, selection_end_index, isect_start, isect_end,
771 * html_painter_pango_to_engine (p, start_x), html_painter_pango_to_engine (p, end_x),
772 * html_painter_pango_to_engine (p, start_x < end_x ? (end_x - start_x) : (start_x - end_x)),
773 * html_painter_pango_to_engine (p, log_rect.width)); */
774
775 if (x_offset)
776 *x_offset = MIN (start_x, end_x);
777 if (width)
778 *width = start_x < end_x ? (end_x - start_x) : (start_x - end_x);
779 if (asc)
780 *asc = PANGO_ASCENT (log_rect);
781 if (height)
782 *height = log_rect.height;
783
784 return TRUE;
785 }
786
787 return FALSE;
788 }
789
790 static void
draw_text(HTMLTextSlave * self,HTMLPainter * p,GtkHTMLFontStyle font_style,gint x,gint y,gint width,gint height,gint tx,gint ty)791 draw_text (HTMLTextSlave *self,
792 HTMLPainter *p,
793 GtkHTMLFontStyle font_style,
794 gint x,
795 gint y,
796 gint width,
797 gint height,
798 gint tx,
799 gint ty)
800 {
801 HTMLObject *obj;
802 HTMLText *text = self->owner;
803 GSList *cur;
804 gint run_width;
805 gint selection_start_index = 0;
806 gint selection_end_index = 0;
807 gint isect_start, isect_end;
808 gboolean selection;
809 GdkColor selection_fg, selection_bg;
810 HTMLEngine *e = NULL;
811
812 obj = HTML_OBJECT (self);
813
814 isect_start = MAX (text->select_start, self->posStart);
815 isect_end = MIN (text->select_start + text->select_length, self->posStart + self->posLen);
816 selection = isect_start < isect_end;
817
818 if (p->widget && GTK_IS_HTML (p->widget))
819 e = html_object_engine (HTML_OBJECT (self->owner), GTK_HTML (p->widget)->engine);
820
821 if (selection) {
822 gchar *end;
823 gchar *start;
824
825 start = html_text_get_text (text, isect_start);
826 end = g_utf8_offset_to_pointer (start, isect_end - isect_start);
827
828 selection_start_index = start - text->text;
829 selection_end_index = end - text->text;
830
831 if (e) {
832 selection_fg = html_colorset_get_color_allocated
833 (e->settings->color_set, p,
834 p->focus ? HTMLHighlightTextColor : HTMLHighlightTextNFColor)->color;
835 selection_bg = html_colorset_get_color_allocated
836 (e->settings->color_set, p,
837 p->focus ? HTMLHighlightColor : HTMLHighlightNFColor)->color;
838 }
839 }
840
841 /* printf ("draw_text %d %d %d\n", selection_bg.red, selection_bg.green, selection_bg.blue); */
842
843 run_width = 0;
844 for (cur = html_text_slave_get_glyph_items (self, p); cur; cur = cur->next) {
845 HTMLTextSlaveGlyphItem *gi = (HTMLTextSlaveGlyphItem *) cur->data;
846 GList *cur_se;
847 gint cur_width;
848
849 if (e)
850 html_painter_set_pen (p, &html_colorset_get_color_allocated (e->settings->color_set,
851 e->painter, HTMLTextColor)->color);
852 cur_width = html_painter_draw_glyphs (p, obj->x + tx + html_painter_pango_to_engine (p, run_width),
853 obj->y + ty + get_ys (text, p), gi->glyph_item.item, gi->glyph_item.glyphs, NULL, NULL);
854
855 if (selection) {
856 gint start_x, width, asc, height;
857 gint cx, cy, cw, ch;
858
859 if (calc_glyph_range_size (text, &gi->glyph_item, selection_start_index, selection_end_index, &start_x, &width, &asc, &height) && width > 0) {
860 html_painter_get_clip_rectangle (p, &cx, &cy, &cw, &ch);
861 /* printf ("run_width; %d start_x %d index %d\n", run_width, start_x, selection_start_index); */
862 html_painter_set_clip_rectangle (p,
863 obj->x + tx + html_painter_pango_to_engine (p, run_width + start_x),
864 obj->y + ty + get_ys (text, p) - html_painter_pango_to_engine (p, asc),
865 html_painter_pango_to_engine (p, width),
866 html_painter_pango_to_engine (p, height));
867
868 /* printf ("draw selection %d %d %d at %d, %d\n", selection_bg.red, selection_bg.green, selection_bg.blue,
869 * obj->x + tx + run_width, obj->y + ty + get_ys (text, p)); */
870 html_painter_draw_glyphs (p, obj->x + tx + html_painter_pango_to_engine (p, run_width),
871 obj->y + ty + get_ys (text, p), gi->glyph_item.item, gi->glyph_item.glyphs,
872 &selection_fg, &selection_bg);
873 html_painter_set_clip_rectangle (p, cx, cy, cw, ch);
874 }
875 }
876
877 for (cur_se = text->spell_errors; e && cur_se; cur_se = cur_se->next) {
878 SpellError *se;
879 guint ma, mi;
880
881 se = (SpellError *) cur_se->data;
882 ma = MAX (se->off, self->posStart);
883 mi = MIN (se->off + se->len, self->posStart + self->posLen);
884
885 if (ma < mi) {
886 gint width, height, asc, start_x;
887
888 gchar *end;
889 gchar *start;
890 gint se_start_index, se_end_index;
891
892 start = html_text_get_text (text, ma);
893 end = g_utf8_offset_to_pointer (start, mi - ma);
894
895 se_start_index = start - text->text;
896 se_end_index = end - text->text;
897
898 if (calc_glyph_range_size (text, &gi->glyph_item, se_start_index, se_end_index, &start_x, &width, &asc, &height)) {
899 html_painter_set_pen (p, &html_colorset_get_color_allocated (e->settings->color_set,
900 p, HTMLSpellErrorColor)->color);
901 /* printf ("spell error: %s\n", html_text_get_text (slave->owner, off)); */
902
903 html_painter_draw_spell_error (p, obj->x + tx + html_painter_pango_to_engine (p, run_width + start_x),
904 obj->y + ty + get_ys (self->owner, p), html_painter_pango_to_engine (p, width));
905 }
906 }
907 if (se->off > self->posStart + self->posLen)
908 break;
909 }
910
911 run_width += cur_width;
912 }
913 }
914
915 static void
draw_focus_rectangle(HTMLTextSlave * slave,HTMLPainter * painter,GdkRectangle * box)916 draw_focus_rectangle (HTMLTextSlave *slave,
917 HTMLPainter *painter,
918 GdkRectangle *box)
919 {
920 HTMLGdkPainter *p;
921 const double dashes[] = { 1, 2 };
922 gint ndash = G_N_ELEMENTS (dashes);
923 HTMLEngine *e;
924
925 if (painter->widget && GTK_IS_HTML (painter->widget))
926 e = html_object_engine (HTML_OBJECT (slave->owner), GTK_HTML (painter->widget)->engine);
927 else
928 return;
929
930 if (HTML_IS_PRINTER (painter))
931 return;
932
933 p = HTML_GDK_PAINTER (painter);
934 /* printf ("draw_text_focus\n"); */
935
936 cairo_save (p->cr);
937 gdk_cairo_set_source_color (p->cr,
938 &html_colorset_get_color_allocated (e->settings->color_set,
939 painter, HTMLTextColor)->color);
940 cairo_set_line_cap (p->cr, CAIRO_LINE_CAP_ROUND);
941 cairo_set_line_width (p->cr, 1);
942 cairo_set_dash (p->cr, dashes, ndash, 2);
943 cairo_rectangle (
944 p->cr,
945 box->x - p->x1 - 0.5,
946 box->y - p->y1 + 0.5,
947 box->width + 1, box->height);
948 cairo_stroke (p->cr);
949 cairo_restore (p->cr);
950 }
951
952 static void
draw_focus(HTMLTextSlave * slave,HTMLPainter * p,gint tx,gint ty)953 draw_focus (HTMLTextSlave *slave,
954 HTMLPainter *p,
955 gint tx,
956 gint ty)
957 {
958 GdkRectangle rect;
959 Link *link = html_text_get_link_at_offset (slave->owner, slave->owner->focused_link_offset);
960
961 if (link && MAX (link->start_offset, slave->posStart) < MIN (link->end_offset, slave->posStart + slave->posLen)) {
962 gint bw = 0;
963 html_object_get_bounds (HTML_OBJECT (slave), &rect);
964 if (slave->posStart < link->start_offset)
965 bw = html_text_calc_part_width (slave->owner, p, html_text_slave_get_text (slave),
966 slave->posStart, link->start_offset - slave->posStart, NULL, NULL);
967 rect.x += tx + bw;
968 rect.width -= bw;
969 if (slave->posStart + slave->posLen > link->end_offset)
970 rect.width -= html_text_calc_part_width (slave->owner, p, slave->owner->text + link->end_index, link->end_offset,
971 slave->posStart + slave->posLen - link->end_offset, NULL, NULL);
972 rect.y += ty;
973 draw_focus_rectangle (slave, p, &rect);
974 }
975 }
976
977 static void
draw(HTMLObject * o,HTMLPainter * p,gint x,gint y,gint width,gint height,gint tx,gint ty)978 draw (HTMLObject *o,
979 HTMLPainter *p,
980 gint x,
981 gint y,
982 gint width,
983 gint height,
984 gint tx,
985 gint ty)
986 {
987 HTMLTextSlave *slave;
988 HTMLText *owner;
989 GtkHTMLFontStyle font_style;
990 GdkRectangle paint;
991
992 /* printf ("slave draw %p\n", o); */
993
994 slave = HTML_TEXT_SLAVE (o);
995 if (!html_object_intersect (o, &paint, x, y, width, height) || slave->posLen == 0)
996 return;
997
998 owner = slave->owner;
999 font_style = html_text_get_font_style (owner);
1000
1001 draw_text (slave, p, font_style, x, y, width, height, tx, ty);
1002
1003 if (HTML_OBJECT (owner)->draw_focused)
1004 draw_focus (slave, p, tx, ty);
1005 }
1006
1007 static gint
calc_min_width(HTMLObject * o,HTMLPainter * painter)1008 calc_min_width (HTMLObject *o,
1009 HTMLPainter *painter)
1010 {
1011 return 0;
1012 }
1013
1014 static gint
calc_preferred_width(HTMLObject * o,HTMLPainter * painter)1015 calc_preferred_width (HTMLObject *o,
1016 HTMLPainter *painter)
1017 {
1018 return 0;
1019 }
1020
1021 static const gchar *
get_url(HTMLObject * o,gint offset)1022 get_url (HTMLObject *o,
1023 gint offset)
1024 {
1025 HTMLTextSlave *slave;
1026
1027 slave = HTML_TEXT_SLAVE (o);
1028 return html_object_get_url (HTML_OBJECT (slave->owner), offset);
1029 }
1030
1031 static gint
calc_offset(HTMLTextSlave * slave,HTMLPainter * painter,gint x)1032 calc_offset (HTMLTextSlave *slave,
1033 HTMLPainter *painter,
1034 gint x)
1035 {
1036 GSList *cur, *glyphs = html_text_slave_get_glyph_items (slave, painter);
1037 gint width = 0, offset = html_object_get_direction (HTML_OBJECT (slave->owner)) == HTML_DIRECTION_RTL ? slave->posLen : 0;
1038 PangoItem *item = NULL;
1039
1040 if (glyphs) {
1041 gint i = 0;
1042
1043 for (cur = glyphs; cur; cur = cur->next) {
1044 HTMLTextSlaveGlyphItem *gi = (HTMLTextSlaveGlyphItem *) cur->data;
1045
1046 item = gi->glyph_item.item;
1047
1048 if (gi->widths == NULL) {
1049
1050 gi->widths = g_new (PangoGlyphUnit, item->num_chars);
1051 html_tmp_fix_pango_glyph_string_get_logical_widths (gi->glyph_item.glyphs, slave->owner->text + item->offset, item->length,
1052 item->analysis.level, gi->widths);
1053 }
1054
1055 if (item->analysis.level % 2 == 0) {
1056 /* LTR */
1057 for (i = 0; i < item->num_chars; i++) {
1058 if (x < html_painter_pango_to_engine (painter, width + gi->widths[i] / 2))
1059 goto done;
1060 width += gi->widths[i];
1061 }
1062 } else {
1063 /* RTL */
1064 for (i = item->num_chars - 1; i >= 0; i--) {
1065 if (x < html_painter_pango_to_engine (painter, width + gi->widths[i] / 2)) {
1066 i++;
1067 goto done;
1068 }
1069 width += gi->widths[i];
1070 }
1071 }
1072 }
1073
1074 done:
1075
1076 if (cur)
1077 offset = g_utf8_pointer_to_offset (html_text_slave_get_text (slave), slave->owner->text + item->offset) + i;
1078 else {
1079 offset = html_object_get_direction (HTML_OBJECT (slave->owner)) == HTML_DIRECTION_RTL ? 0 : slave->posLen;
1080 }
1081 }
1082
1083 /* printf ("offset %d\n", offset); */
1084
1085 return offset;
1086 }
1087
1088 static guint
get_offset_for_pointer(HTMLTextSlave * slave,HTMLPainter * painter,gint x,gint y)1089 get_offset_for_pointer (HTMLTextSlave *slave,
1090 HTMLPainter *painter,
1091 gint x,
1092 gint y)
1093 {
1094 g_return_val_if_fail (slave != NULL, 0);
1095
1096 x -= HTML_OBJECT (slave)->x;
1097
1098 if (x <= 0)
1099 return 0;
1100
1101 if (x >= HTML_OBJECT (slave)->width - 1)
1102 return slave->posLen;
1103
1104 if (slave->posLen > 1)
1105 return calc_offset (slave, painter, x);
1106 else
1107 return x > HTML_OBJECT (slave)->width / 2 ? 1 : 0;
1108 }
1109
1110 static HTMLObject *
check_point(HTMLObject * self,HTMLPainter * painter,gint x,gint y,guint * offset_return,gboolean for_cursor)1111 check_point (HTMLObject *self,
1112 HTMLPainter *painter,
1113 gint x,
1114 gint y,
1115 guint *offset_return,
1116 gboolean for_cursor)
1117 {
1118 if (x >= self->x
1119 && x < self->x + MAX (1, self->width)
1120 && y >= self->y - self->ascent
1121 && y < self->y + self->descent) {
1122 HTMLTextSlave *slave = HTML_TEXT_SLAVE (self);
1123
1124 if (offset_return != NULL)
1125 *offset_return = slave->posStart
1126 + get_offset_for_pointer (slave, painter, x, y);
1127
1128 return HTML_OBJECT (slave->owner);
1129 }
1130
1131 return NULL;
1132 }
1133
1134 static void
clear_glyph_items(HTMLTextSlave * slave)1135 clear_glyph_items (HTMLTextSlave *slave)
1136 {
1137 if (slave->glyph_items) {
1138 glyph_items_destroy (slave->glyph_items);
1139 slave->glyph_items = NULL;
1140 }
1141 }
1142
1143 static void
destroy(HTMLObject * obj)1144 destroy (HTMLObject *obj)
1145 {
1146 HTMLTextSlave *slave = HTML_TEXT_SLAVE (obj);
1147
1148 clear_glyph_items (slave);
1149
1150 HTML_OBJECT_CLASS (parent_class)->destroy (obj);
1151 }
1152
1153 void
html_text_slave_type_init(void)1154 html_text_slave_type_init (void)
1155 {
1156 html_text_slave_class_init (&html_text_slave_class, HTML_TYPE_TEXTSLAVE, sizeof (HTMLTextSlave));
1157 }
1158
1159 void
html_text_slave_class_init(HTMLTextSlaveClass * klass,HTMLType type,guint object_size)1160 html_text_slave_class_init (HTMLTextSlaveClass *klass,
1161 HTMLType type,
1162 guint object_size)
1163 {
1164 HTMLObjectClass *object_class;
1165
1166 object_class = HTML_OBJECT_CLASS (klass);
1167
1168 html_object_class_init (object_class, type, object_size);
1169
1170 object_class->select_range = select_range;
1171 object_class->copy = copy;
1172 object_class->destroy = destroy;
1173 object_class->draw = draw;
1174 object_class->calc_size = html_text_slave_real_calc_size;
1175 object_class->fit_line = hts_fit_line;
1176 object_class->calc_min_width = calc_min_width;
1177 object_class->calc_preferred_width = calc_preferred_width;
1178 object_class->get_url = get_url;
1179 object_class->get_length = get_length;
1180 object_class->check_point = check_point;
1181
1182 parent_class = &html_object_class;
1183 }
1184
1185 void
html_text_slave_init(HTMLTextSlave * slave,HTMLTextSlaveClass * klass,HTMLText * owner,guint posStart,guint posLen)1186 html_text_slave_init (HTMLTextSlave *slave,
1187 HTMLTextSlaveClass *klass,
1188 HTMLText *owner,
1189 guint posStart,
1190 guint posLen)
1191 {
1192 HTMLObject *object;
1193
1194 object = HTML_OBJECT (slave);
1195
1196 html_object_init (object, HTML_OBJECT_CLASS (klass));
1197
1198 object->ascent = HTML_OBJECT (owner)->ascent;
1199 object->descent = HTML_OBJECT (owner)->descent;
1200
1201 slave->posStart = posStart;
1202 slave->posLen = posLen;
1203 slave->owner = owner;
1204 slave->charStart = NULL;
1205 slave->pi = NULL;
1206 slave->glyph_items = NULL;
1207
1208 /* text slaves have always min_width 0 */
1209 object->min_width = 0;
1210 object->change &= ~HTML_CHANGE_MIN_WIDTH;
1211 }
1212
1213 HTMLObject *
html_text_slave_new(HTMLText * owner,guint posStart,guint posLen)1214 html_text_slave_new (HTMLText *owner,
1215 guint posStart,
1216 guint posLen)
1217 {
1218 HTMLTextSlave *slave;
1219
1220 slave = g_new (HTMLTextSlave, 1);
1221 html_text_slave_init (slave, &html_text_slave_class, owner, posStart, posLen);
1222
1223 return HTML_OBJECT (slave);
1224 }
1225
1226 static gboolean
html_text_slave_is_index_in_glyph(HTMLTextSlave * slave,HTMLTextSlave * next_slave,GSList * cur,gint index,PangoItem * item)1227 html_text_slave_is_index_in_glyph (HTMLTextSlave *slave,
1228 HTMLTextSlave *next_slave,
1229 GSList *cur,
1230 gint index,
1231 PangoItem *item)
1232 {
1233 if (item->analysis.level % 2 == 0) {
1234 /* LTR */
1235 return item->offset <= index
1236 && (index < item->offset + item->length
1237 || (index == item->offset + item->length &&
1238 (!cur->next
1239 || (!next_slave && slave->owner->text_bytes == item->offset + item->length)
1240 || (next_slave && html_text_slave_get_text (next_slave) - next_slave->owner->text == item->offset + item->length))));
1241 } else {
1242 /* RTL */
1243 return index <= item->offset + item->length
1244 && (item->offset < index
1245 || (index == item->offset &&
1246 (!cur->next
1247 || (!next_slave && slave->owner->text_bytes == item->offset + item->length)
1248 || (next_slave && html_text_slave_get_text (next_slave) - next_slave->owner->text == item->offset))));
1249 }
1250 }
1251
1252 static HTMLTextSlaveGlyphItem *
html_text_slave_get_glyph_item_at_offset(HTMLTextSlave * slave,HTMLPainter * painter,gint offset,HTMLTextSlaveGlyphItem ** prev,HTMLTextSlaveGlyphItem ** next,gint * start_width,gint * index_out)1253 html_text_slave_get_glyph_item_at_offset (HTMLTextSlave *slave,
1254 HTMLPainter *painter,
1255 gint offset,
1256 HTMLTextSlaveGlyphItem **prev,
1257 HTMLTextSlaveGlyphItem **next,
1258 gint *start_width,
1259 gint *index_out)
1260 {
1261 HTMLTextSlaveGlyphItem *rv = NULL;
1262 HTMLTextSlaveGlyphItem *prev_gi, *next_gi;
1263 HTMLTextSlave *next_slave = HTML_OBJECT (slave)->next && HTML_IS_TEXT_SLAVE (HTML_OBJECT (slave)->next) ? HTML_TEXT_SLAVE (HTML_OBJECT (slave)->next) : NULL;
1264 GSList *cur;
1265 gint index;
1266
1267 next_gi = NULL;
1268
1269 index = g_utf8_offset_to_pointer (html_text_slave_get_text (slave), offset) - slave->owner->text;
1270 if (index_out)
1271 *index_out = index;
1272
1273 if (start_width)
1274 *start_width = 0;
1275
1276 cur = html_text_slave_get_glyph_items (slave, painter);
1277 if (cur) {
1278 for (prev_gi = NULL; cur; cur = cur->next) {
1279 HTMLTextSlaveGlyphItem *gi = (HTMLTextSlaveGlyphItem *) cur->data;
1280
1281 if (html_text_slave_is_index_in_glyph (slave, next_slave, cur, index, gi->glyph_item.item)) {
1282 next_gi = cur->next ? (HTMLTextSlaveGlyphItem *) cur->next->data : NULL;
1283 rv = gi;
1284 break;
1285 }
1286
1287 prev_gi = gi;
1288 if (start_width) {
1289 PangoRectangle log_rect;
1290
1291 pango_glyph_string_extents (gi->glyph_item.glyphs, gi->glyph_item.item->analysis.font, NULL, &log_rect);
1292 (*start_width) += log_rect.width;
1293 }
1294 }
1295 } else {
1296 prev_gi = next_gi = NULL;
1297 }
1298
1299 if (prev)
1300 *prev = prev_gi;
1301
1302 if (next)
1303 *next = next_gi;
1304
1305 return rv;
1306 }
1307
1308 static gboolean
html_text_slave_gi_left_edge(HTMLTextSlave * slave,HTMLCursor * cursor,HTMLTextSlaveGlyphItem * gi)1309 html_text_slave_gi_left_edge (HTMLTextSlave *slave,
1310 HTMLCursor *cursor,
1311 HTMLTextSlaveGlyphItem *gi)
1312 {
1313 gint old_offset = cursor->offset;
1314
1315 if (gi->glyph_item.item->analysis.level % 2 == 0) {
1316 /* LTR */
1317 cursor->offset = slave->posStart + g_utf8_pointer_to_offset (html_text_slave_get_text (slave),
1318 slave->owner->text + gi->glyph_item.item->offset);
1319 cursor->position += cursor->offset - old_offset;
1320 } else {
1321 /* RTL */
1322 cursor->offset = slave->posStart + g_utf8_pointer_to_offset (html_text_slave_get_text (slave),
1323 slave->owner->text + gi->glyph_item.item->offset + gi->glyph_item.item->length);
1324 cursor->position += cursor->offset - old_offset;
1325 }
1326
1327 return TRUE;
1328 }
1329
1330 static gboolean
html_text_slave_gi_right_edge(HTMLTextSlave * slave,HTMLCursor * cursor,HTMLTextSlaveGlyphItem * gi)1331 html_text_slave_gi_right_edge (HTMLTextSlave *slave,
1332 HTMLCursor *cursor,
1333 HTMLTextSlaveGlyphItem *gi)
1334 {
1335 gint old_offset = cursor->offset;
1336
1337 if (gi->glyph_item.item->analysis.level % 2 == 0) {
1338 /* LTR */
1339 cursor->offset = slave->posStart + g_utf8_pointer_to_offset (html_text_slave_get_text (slave),
1340 slave->owner->text + gi->glyph_item.item->offset + gi->glyph_item.item->length);
1341 cursor->position += cursor->offset - old_offset;
1342 } else {
1343 /* RTL */
1344 cursor->offset = slave->posStart + g_utf8_pointer_to_offset (html_text_slave_get_text (slave),
1345 slave->owner->text + gi->glyph_item.item->offset);
1346 cursor->position += cursor->offset - old_offset;
1347 }
1348
1349 return TRUE;
1350 }
1351
1352 static gboolean
html_text_slave_cursor_right_one(HTMLTextSlave * slave,HTMLPainter * painter,HTMLCursor * cursor)1353 html_text_slave_cursor_right_one (HTMLTextSlave *slave,
1354 HTMLPainter *painter,
1355 HTMLCursor *cursor)
1356 {
1357 HTMLTextSlaveGlyphItem *prev, *next;
1358 gint index;
1359 HTMLTextSlaveGlyphItem *gi = html_text_slave_get_glyph_item_at_offset (slave, painter, cursor->offset - slave->posStart, &prev, &next, NULL, &index);
1360
1361 if (!gi)
1362 return FALSE;
1363
1364 if (gi->glyph_item.item->analysis.level % 2 == 0) {
1365 /* LTR */
1366 if (index < gi->glyph_item.item->offset + gi->glyph_item.item->length) {
1367 cursor->offset++;
1368 cursor->position++;
1369
1370 return TRUE;
1371 }
1372 } else {
1373 /* RTL */
1374 if (index > gi->glyph_item.item->offset && index <= gi->glyph_item.item->offset + gi->glyph_item.item->length) {
1375 cursor->offset--;
1376 cursor->position--;
1377
1378 return TRUE;
1379 }
1380 }
1381
1382 if (next) {
1383 if (html_text_slave_gi_left_edge (slave, cursor, next)) {
1384 if (next->glyph_item.item->analysis.level % 2 == 0) {
1385 /* LTR */
1386 cursor->offset++;
1387 cursor->position++;
1388 } else {
1389 /* RTL */
1390 cursor->offset--;
1391 cursor->position--;
1392 }
1393
1394 return TRUE;
1395 }
1396 }
1397
1398 return FALSE;
1399 }
1400
1401 gboolean
html_text_slave_cursor_right(HTMLTextSlave * slave,HTMLPainter * painter,HTMLCursor * cursor)1402 html_text_slave_cursor_right (HTMLTextSlave *slave,
1403 HTMLPainter *painter,
1404 HTMLCursor *cursor)
1405 {
1406 HTMLTextPangoInfo *pi = html_text_get_pango_info (slave->owner, painter);
1407 gboolean step_success;
1408
1409 do
1410 step_success = html_text_slave_cursor_right_one (slave, painter, cursor);
1411 while (step_success && !pi->attrs[cursor->offset].is_cursor_position);
1412
1413 return step_success;
1414 }
1415
1416 static gboolean
html_text_slave_cursor_left_one(HTMLTextSlave * slave,HTMLPainter * painter,HTMLCursor * cursor)1417 html_text_slave_cursor_left_one (HTMLTextSlave *slave,
1418 HTMLPainter *painter,
1419 HTMLCursor *cursor)
1420 {
1421 HTMLTextSlaveGlyphItem *prev, *next;
1422 gint index;
1423 HTMLObject *prev_obj = HTML_OBJECT (slave->owner)->prev;
1424 HTMLTextSlaveGlyphItem *gi = html_text_slave_get_glyph_item_at_offset (slave, painter, cursor->offset - slave->posStart, &prev, &next, NULL, &index);
1425
1426 /* printf ("gi: %p item num chars: %d\n", gi, gi ? gi->glyph_item.item->num_chars : -1); */
1427
1428 if (!gi)
1429 return FALSE;
1430
1431 if (gi->glyph_item.item->analysis.level % 2 == 0) {
1432 /* LTR */
1433 if (index - gi->glyph_item.item->offset > 1 || (!prev && !prev_obj && index - gi->glyph_item.item->offset > 0)) {
1434 cursor->offset--;
1435 cursor->position--;
1436
1437 return TRUE;
1438 }
1439 } else {
1440 /* RTL */
1441 if (index < gi->glyph_item.item->offset + gi->glyph_item.item->length) {
1442 cursor->offset++;
1443 cursor->position++;
1444
1445 return TRUE;
1446 }
1447 }
1448
1449 if (prev) {
1450 if (html_text_slave_gi_right_edge (slave, cursor, prev)) {
1451 if (prev->glyph_item.item->analysis.level % 2 == 0) {
1452 /* LTR */
1453 if (index - gi->glyph_item.item->offset == 0) {
1454 cursor->offset--;
1455 cursor->position--;
1456 }
1457 } else {
1458 /* RTL */
1459 cursor->offset++;
1460 cursor->position++;
1461 }
1462
1463 return TRUE;
1464 }
1465 }
1466
1467 return FALSE;
1468 }
1469
1470 gboolean
html_text_slave_cursor_left(HTMLTextSlave * slave,HTMLPainter * painter,HTMLCursor * cursor)1471 html_text_slave_cursor_left (HTMLTextSlave *slave,
1472 HTMLPainter *painter,
1473 HTMLCursor *cursor)
1474 {
1475 HTMLTextPangoInfo *pi = html_text_get_pango_info (slave->owner, painter);
1476 gboolean step_success;
1477
1478 do
1479 step_success = html_text_slave_cursor_left_one (slave, painter, cursor);
1480 while (step_success && !pi->attrs[cursor->offset].is_cursor_position);
1481
1482 return step_success;
1483 }
1484
1485 static gboolean
html_text_slave_get_left_edge(HTMLTextSlave * slave,HTMLPainter * painter,HTMLCursor * cursor)1486 html_text_slave_get_left_edge (HTMLTextSlave *slave,
1487 HTMLPainter *painter,
1488 HTMLCursor *cursor)
1489 {
1490 HTMLTextPangoInfo *pi = html_text_get_pango_info (slave->owner, painter);
1491 gint old_offset = cursor->offset;
1492 gint old_position = cursor->position;
1493
1494 cursor->offset = html_text_slave_get_left_edge_offset (slave, painter);
1495
1496 if (pi->attrs[cursor->offset].is_cursor_position && old_offset != cursor->offset)
1497 return TRUE;
1498 else {
1499 if (html_text_slave_cursor_right (slave, painter, cursor)) {
1500 /* we should preserve position here as caller function correct position themselves */
1501 cursor->position = old_position;
1502 return TRUE;
1503 } else
1504 return FALSE;
1505 }
1506 }
1507
1508 static gboolean
html_text_slave_get_right_edge(HTMLTextSlave * slave,HTMLPainter * painter,HTMLCursor * cursor)1509 html_text_slave_get_right_edge (HTMLTextSlave *slave,
1510 HTMLPainter *painter,
1511 HTMLCursor *cursor)
1512 {
1513 HTMLTextPangoInfo *pi = html_text_get_pango_info (slave->owner, painter);
1514 gint old_offset = cursor->offset;
1515 gint old_position = cursor->position;
1516
1517 cursor->offset = html_text_slave_get_right_edge_offset (slave, painter);
1518
1519 if (pi->attrs[cursor->offset].is_cursor_position && old_offset != cursor->offset)
1520 return TRUE;
1521 else {
1522 if (html_text_slave_cursor_left (slave, painter, cursor)) {
1523 /* we should preserve position here as caller function correct position themselves */
1524 cursor->position = old_position;
1525 return TRUE;
1526 } else
1527 return FALSE;
1528 }
1529 }
1530
1531 gboolean
html_text_slave_cursor_head(HTMLTextSlave * slave,HTMLCursor * cursor,HTMLPainter * painter)1532 html_text_slave_cursor_head (HTMLTextSlave *slave,
1533 HTMLCursor *cursor,
1534 HTMLPainter *painter)
1535 {
1536 if (html_text_slave_get_glyph_items (slave, painter)) {
1537 cursor->object = HTML_OBJECT (slave->owner);
1538
1539 if (html_text_get_pango_direction (slave->owner) != PANGO_DIRECTION_RTL) {
1540 /* LTR */
1541 return html_text_slave_get_left_edge (slave, painter, cursor);
1542 } else {
1543 /* RTL */
1544 return html_text_slave_get_right_edge (slave, painter, cursor);
1545 }
1546 }
1547
1548 return FALSE;
1549 }
1550
1551 gboolean
html_text_slave_cursor_tail(HTMLTextSlave * slave,HTMLCursor * cursor,HTMLPainter * painter)1552 html_text_slave_cursor_tail (HTMLTextSlave *slave,
1553 HTMLCursor *cursor,
1554 HTMLPainter *painter)
1555 {
1556 if (html_text_slave_get_glyph_items (slave, painter)) {
1557 cursor->object = HTML_OBJECT (slave->owner);
1558
1559 if (html_text_get_pango_direction (slave->owner) != PANGO_DIRECTION_RTL) {
1560 /* LTR */
1561 return html_text_slave_get_right_edge (slave, painter, cursor);
1562 } else {
1563 /* RTL */
1564 return html_text_slave_get_left_edge (slave, painter, cursor);
1565 }
1566 }
1567
1568 return FALSE;
1569 }
1570
1571 void
html_text_slave_get_cursor_base(HTMLTextSlave * slave,HTMLPainter * painter,guint offset,gint * x,gint * y)1572 html_text_slave_get_cursor_base (HTMLTextSlave *slave,
1573 HTMLPainter *painter,
1574 guint offset,
1575 gint *x,
1576 gint *y)
1577 {
1578 HTMLTextSlaveGlyphItem *gi;
1579 gint index, start_width;
1580
1581 html_object_calc_abs_position (HTML_OBJECT (slave), x, y);
1582
1583 gi = html_text_slave_get_glyph_item_at_offset (slave, painter, (gint) offset, NULL, NULL, &start_width, &index);
1584
1585 /* printf ("gi: %p index: %d start_width: %d item indexes %d %d\n", */
1586 /* gi, index, start_width, gi ? gi->glyph_item.item->offset : -1, */
1587 /* gi ? gi->glyph_item.item->offset + gi->glyph_item.item->length : -1); */
1588
1589 if (gi) {
1590 gint start_x;
1591
1592 if (calc_glyph_range_size (slave->owner, &gi->glyph_item, index, index, &start_x, NULL, NULL, NULL) && x) {
1593 /* printf ("start_width: %d start_x: %d\n", start_width, start_x); */
1594 *x += html_painter_pango_to_engine (painter, start_width + start_x);
1595 }
1596 }
1597 }
1598
1599 gint
html_text_slave_get_left_edge_offset(HTMLTextSlave * slave,HTMLPainter * painter)1600 html_text_slave_get_left_edge_offset (HTMLTextSlave *slave,
1601 HTMLPainter *painter)
1602 {
1603 GSList *gis = html_text_slave_get_glyph_items (slave, painter);
1604
1605 if (gis) {
1606 HTMLTextSlaveGlyphItem *gi = (HTMLTextSlaveGlyphItem *) gis->data;
1607
1608 if (gi->glyph_item.item->analysis.level % 2 == 0) {
1609 /* LTR */
1610 return slave->posStart + g_utf8_pointer_to_offset (html_text_slave_get_text (slave), slave->owner->text + gi->glyph_item.item->offset);
1611 } else {
1612 /* RTL */
1613 return slave->posStart + MIN (slave->posLen, g_utf8_pointer_to_offset (html_text_slave_get_text (slave),
1614 slave->owner->text
1615 + gi->glyph_item.item->offset + gi->glyph_item.item->length));
1616 }
1617 } else {
1618 if (slave->owner->text_len > 0)
1619 g_warning ("html_text_slave_get_left_edge_offset failed");
1620
1621 return 0;
1622 }
1623 }
1624
1625 gint
html_text_slave_get_right_edge_offset(HTMLTextSlave * slave,HTMLPainter * painter)1626 html_text_slave_get_right_edge_offset (HTMLTextSlave *slave,
1627 HTMLPainter *painter)
1628 {
1629 GSList *gis = html_text_slave_get_glyph_items (slave, painter);
1630
1631 if (gis) {
1632 HTMLTextSlaveGlyphItem *gi = (HTMLTextSlaveGlyphItem *) g_slist_last (gis)->data;
1633
1634 if (gi->glyph_item.item->analysis.level % 2 == 0) {
1635 /* LTR */
1636 return slave->posStart + MIN (slave->posLen, g_utf8_pointer_to_offset (html_text_slave_get_text (slave),
1637 slave->owner->text
1638 + gi->glyph_item.item->offset + gi->glyph_item.item->length));
1639 } else {
1640 /* RTL */
1641 return slave->posStart + g_utf8_pointer_to_offset (html_text_slave_get_text (slave), slave->owner->text + gi->glyph_item.item->offset);
1642 }
1643 } else {
1644 if (slave->owner->text_len > 0)
1645 g_warning ("html_text_slave_get_left_edge_offset failed");
1646
1647 return 0;
1648 }
1649 }
1650