/gtkhtml-4.4.0/gtkhtml/htmltextslave.c

# · C · 1649 lines · 1247 code · 289 blank · 113 comment · 262 complexity · d6983973f832dd0bfaaf4f6aa115ef54 MD5 · raw file

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