3 * @section Evas_Object_Textblock_Internal Internal Textblock Object Tutorial
5 * This explains the internal design of the Evas Textblock Object, it's assumed
6 * that the reader of this section has already read @ref Evas_Object_Textblock_Tutorial "Textblock's usage docs.".
8 * @subsection textblock_internal_intro Introduction
9 * There are two main parts to the textblock object, the first being the node
10 * system, and the second being the layout system. The former is just an
11 * internal representation of the markup text, while the latter is the internal
12 * visual representation of the text (i.e positioning, sizing, fonts and etc).
14 * @subsection textblock_nodes The Nodes system
15 * The nodes mechanism consists of two main data types:
16 * ::Evas_Object_Textblock_Node_Text and ::Evas_Object_Textblock_Node_Format
17 * the former is for Text nodes and the latter is for format nodes.
18 * There's always at least one text node, even if there are only formats.
20 * @subsection textblock_nodes_text Text nodes
21 * Each text node is essentially a paragraph, it includes an @ref Eina_UStrbuf
22 * that stores the actual paragraph text, a utf8 string to store the paragraph
23 * text in utf8 (which is not used internally at all), A pointer to it's
24 * main @ref textblock_nodes_format_internal "Format Node" and the paragraph's
25 * @ref evas_bidi_props "BiDi properties". The pointer to the format node may be
26 * NULL if there's no format node anywhere before the end of the text node,
27 * not even in previous text nodes. If not NULL, it points to the first format
28 * node pointing to text inside of the text node, or if there is none, it points
29 * to the previous's text nodes format node. Each paragraph has a format node
30 * representing a paragraph separator pointing to it's last position except
31 * for the last paragraph, which has no such constraint. This constraint
32 * happens because text nodes are paragraphs and paragraphs are delimited by
33 * paragraph separators.
35 * @subsection textblock_nodes_format_internal Format Nodes - Internal
36 * Each format node stores a group of format information, for example the
37 * markup: \<font=Vera,Kochi font_size=10 align=left\> will all be inserted
38 * inside the same format node, altohugh it consists of different formatting
40 * Each node has a pointer to it's text node, this pointer is NEVER NULL, even
41 * if there's only one format, and no text, a text node is created. Each format
42 * node includes an offset from the last format node of the same text node. For
43 * example, the markup "0<b>12</b>" will create two format nodes, the first
44 * having an offset of 1 and the second an offset of 2. Each format node also
45 * includes a @ref Eina_Strbuf that includes the textual representation of the
46 * format, and a boolean stating if the format is a visible format or not, see
47 * @ref textblock_nodes_format_visible
49 * @subsection textblock_nodes_format_visible Visible Format Nodes
50 * There are two types of format nodes, visible and invisible. They are the same
51 * in every way, except for the representation in the text node. While invisible
52 * format nodes have no representation in the text node, the visible ones do.
53 * The Uniceode object replacement character (0xFFFC) is inserted to every place
54 * a visible format node points to. This makes it very easy to treat visible
55 * formats as items in the text, both for BiDi purposes and cursor handling
57 * Here are a few example visible an invisible formats:
58 * Visible: newline char, tab, paragraph separator and an embedded item.
59 * Invisible: setting the color, font or alignment of the text.
61 * @subsection textblock_layout The layout system
62 * @todo write @ref textblock_layout
64 #include "evas_common.h"
65 #include "evas_private.h"
69 #include "linebreak.h"
70 #include "wordbreak.h"
74 #define ENFN obj->layer->evas->engine.func
75 #define ENDT obj->layer->evas->engine.data.output
77 /* private magic number for textblock objects */
78 static const char o_type[] = "textblock";
80 /* The char to be inserted instead of visible formats */
81 #define _REPLACEMENT_CHAR 0xFFFC
82 #define _PARAGRAPH_SEPARATOR 0x2029
86 #define _REPLACEMENT_CHAR_UTF8 "\xEF\xBF\xBC"
87 #define _PARAGRAPH_SEPARATOR_UTF8 "\xE2\x80\xA9"
88 #define _NEWLINE_UTF8 "\n"
89 #define _TAB_UTF8 "\t"
90 #define EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(ch) \
91 (((ch) == _REPLACEMENT_CHAR) || \
92 ((ch) == _NEWLINE) || \
94 ((ch) == _PARAGRAPH_SEPARATOR))
96 /* private struct for textblock object internal data */
99 * @typedef Evas_Object_Textblock
100 * The actual textblock object.
102 typedef struct _Evas_Object_Textblock Evas_Object_Textblock;
105 * @typedef Evas_Object_Style_Tag
106 * The structure used for finding style tags.
108 typedef struct _Evas_Object_Style_Tag Evas_Object_Style_Tag;
111 * @typedef Evas_Object_Style_Tag
112 * The structure used for finding style tags.
114 typedef struct _Evas_Object_Style_Tag_Base Evas_Object_Style_Tag_Base;
117 * @typedef Evas_Object_Textblock_Node_Text
120 typedef struct _Evas_Object_Textblock_Node_Text Evas_Object_Textblock_Node_Text;
123 typedef struct _Evas_Object_Textblock_Node_Format Evas_Object_Textblock_Node_Format;
128 * @typedef Evas_Object_Textblock_Paragraph
129 * A layouting paragraph.
131 typedef struct _Evas_Object_Textblock_Paragraph Evas_Object_Textblock_Paragraph;
134 * @typedef Evas_Object_Textblock_Line
137 typedef struct _Evas_Object_Textblock_Line Evas_Object_Textblock_Line;
140 * @typedef Evas_Object_Textblock_Item
143 typedef struct _Evas_Object_Textblock_Item Evas_Object_Textblock_Item;
146 * @typedef Evas_Object_Textblock_Item
147 * A layouting text item.
149 typedef struct _Evas_Object_Textblock_Text_Item Evas_Object_Textblock_Text_Item;
152 * @typedef Evas_Object_Textblock_Format_Item
153 * A layouting format item.
155 typedef struct _Evas_Object_Textblock_Format_Item Evas_Object_Textblock_Format_Item;
158 * @typedef Evas_Object_Textblock_Format
159 * A textblock format.
161 typedef struct _Evas_Object_Textblock_Format Evas_Object_Textblock_Format;
165 * @def IS_AT_END(ti, ind)
166 * Return true if ind is at the end of the text item, false otherwise.
168 #define IS_AT_END(ti, ind) (ind == ti->text_props.text_len)
172 * @def MOVE_PREV_UNTIL(limit, ind)
173 * This decrements ind as long as ind > limit.
175 #define MOVE_PREV_UNTIL(limit, ind) \
178 if ((limit) < (ind)) \
185 * @def MOVE_NEXT_UNTIL(limit, ind)
186 * This increments ind as long as ind < limit
188 #define MOVE_NEXT_UNTIL(limit, ind) \
191 if ((ind) < (limit)) \
198 * @def GET_ITEM_TEXT(ti)
199 * Returns a const reference to the text of the ti (not null terminated).
201 #define GET_ITEM_TEXT(ti) \
202 (((ti)->parent.text_node) ? \
203 (eina_ustrbuf_string_get((ti)->parent.text_node->unicode) + \
204 (ti)->parent.text_pos) : EINA_UNICODE_EMPTY_STRING)
207 * @def _FORMAT_IS_CLOSER_OF(base, closer, closer_len)
208 * Returns true if closer is the closer of base.
210 #define _FORMAT_IS_CLOSER_OF(base, closer, closer_len) \
211 (!strncmp(base, closer, closer_len) && \
212 (!base[closer_len] || \
213 (base[closer_len] == '=') || \
214 _is_white(base[closer_len])))
216 /*FIXME: document the structs and struct items. */
217 struct _Evas_Object_Style_Tag_Base
225 struct _Evas_Object_Style_Tag
228 Evas_Object_Style_Tag_Base tag;
231 struct _Evas_Object_Textblock_Node_Text
234 Eina_UStrbuf *unicode;
236 Evas_Object_Textblock_Node_Format *format_node;
237 Evas_Object_Textblock_Paragraph *par;
239 Eina_Bool is_new : 1;
242 struct _Evas_Object_Textblock_Node_Format
246 const char *orig_format;
247 Evas_Object_Textblock_Node_Text *text_node;
249 unsigned char anchor : 2;
250 Eina_Bool opener : 1;
251 Eina_Bool own_closer : 1;
252 Eina_Bool visible : 1;
253 Eina_Bool format_change : 1;
254 Eina_Bool is_new : 1;
257 /* The default tags to use */
258 static const Evas_Object_Style_Tag_Base default_tags[] = {
259 { "b", "+ font_weight=Bold", 1, 18 },
260 { "i", "+ font_style=Italic", 1, 19 }};
262 #define ANCHOR_NONE 0
264 #define ANCHOR_ITEM 2
269 * A convinience macro for casting to a text node.
271 #define _NODE_TEXT(x) ((Evas_Object_Textblock_Node_Text *) (x))
274 * @def _NODE_FORMAT(x)
275 * A convinience macro for casting to a format node.
277 #define _NODE_FORMAT(x) ((Evas_Object_Textblock_Node_Format *) (x))
281 * A convinience macro for casting to a generic item.
283 #define _ITEM(x) ((Evas_Object_Textblock_Item *) (x))
287 * A convinience macro for casting to a text item.
289 #define _ITEM_TEXT(x) ((Evas_Object_Textblock_Text_Item *) (x))
292 * @def _ITEM_FORMAT(x)
293 * A convinience macro for casting to a format item.
295 #define _ITEM_FORMAT(x) ((Evas_Object_Textblock_Format_Item *) (x))
297 struct _Evas_Object_Textblock_Paragraph
300 Evas_Object_Textblock_Line *lines;
301 Evas_Object_Textblock_Node_Text *text_node;
302 Eina_List *logical_items;
303 Evas_BiDi_Paragraph_Props *bidi_props; /* Only valid during layout */
304 Evas_BiDi_Direction direction;
307 Eina_Bool is_bidi : 1;
308 Eina_Bool visible : 1;
309 Eina_Bool rendered : 1;
312 struct _Evas_Object_Textblock_Line
315 Evas_Object_Textblock_Item *items;
316 Evas_Object_Textblock_Paragraph *par;
317 Evas_Coord x, y, w, h;
322 typedef enum _Evas_Textblock_Item_Type
324 EVAS_TEXTBLOCK_ITEM_TEXT,
325 EVAS_TEXTBLOCK_ITEM_FORMAT,
326 } Evas_Textblock_Item_Type;
328 struct _Evas_Object_Textblock_Item
331 Evas_Textblock_Item_Type type;
332 Evas_Object_Textblock_Node_Text *text_node;
333 Evas_Object_Textblock_Format *format;
338 Evas_Coord adv, x, w, h;
339 Eina_Bool merge : 1; /* Indicates whether this
340 item should merge to the
341 previous item or not */
342 Eina_Bool visually_deleted : 1;
343 /* Indicates whether this
344 item is used in the visual
348 struct _Evas_Object_Textblock_Text_Item
350 Evas_Object_Textblock_Item parent;
351 Evas_Text_Props text_props;
353 Evas_Coord x_adjustment; /* Used to indicate by how
354 much we adjusted sizes */
357 struct _Evas_Object_Textblock_Format_Item
359 Evas_Object_Textblock_Item parent;
360 Evas_BiDi_Direction bidi_dir;
363 unsigned char vsize : 2;
364 unsigned char size : 2;
365 Eina_Bool formatme : 1;
368 struct _Evas_Object_Textblock_Format
370 Evas_Object_Textblock_Node_Format *fnode;
374 Evas_Font_Description *fdesc;
381 unsigned char r, g, b, a;
382 } normal, underline, underline2, underline_dash, outline, shadow, glow, glow2, backing,
392 int underline_dash_width;
393 int underline_dash_gap;
399 Eina_Bool wrap_word : 1;
400 Eina_Bool wrap_char : 1;
401 Eina_Bool wrap_mixed : 1;
402 Eina_Bool underline : 1;
403 Eina_Bool underline2 : 1;
404 Eina_Bool underline_dash : 1;
405 Eina_Bool strikethrough : 1;
406 Eina_Bool backing : 1;
407 Eina_Bool password : 1;
408 Eina_Bool halign_auto : 1;
411 struct _Evas_Textblock_Style
413 const char *style_text;
415 Evas_Object_Style_Tag *tags;
417 Eina_Bool delete_me : 1;
420 struct _Evas_Textblock_Cursor
424 Evas_Object_Textblock_Node_Text *node;
427 /* Size of the index array */
428 #define TEXTBLOCK_PAR_INDEX_SIZE 10
429 struct _Evas_Object_Textblock
432 Evas_Textblock_Style *style;
433 Evas_Textblock_Style *style_user;
434 Evas_Textblock_Cursor *cursor;
436 Evas_Object_Textblock_Node_Text *text_nodes;
437 Evas_Object_Textblock_Node_Format *format_nodes;
440 Evas_Object_Textblock_Paragraph *paragraphs;
441 Evas_Object_Textblock_Paragraph *par_index[TEXTBLOCK_PAR_INDEX_SIZE];
443 Evas_Object_Textblock_Text_Item *ellip_ti;
444 Eina_List *anchors_a;
445 Eina_List *anchors_item;
454 const char *bidi_delimiters;
459 Eina_Bool redraw : 1;
460 Eina_Bool changed : 1;
461 Eina_Bool content_changed : 1;
462 Eina_Bool format_changed : 1;
463 Eina_Bool have_ellipsis : 1;
464 Eina_Bool legacy_newline : 1;
467 /* private methods for textblock objects */
468 static void evas_object_textblock_init(Evas_Object *obj);
469 static void *evas_object_textblock_new(void);
470 static void evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y);
471 static void evas_object_textblock_free(Evas_Object *obj);
472 static void evas_object_textblock_render_pre(Evas_Object *obj);
473 static void evas_object_textblock_render_post(Evas_Object *obj);
475 static unsigned int evas_object_textblock_id_get(Evas_Object *obj);
476 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj);
477 static void *evas_object_textblock_engine_data_get(Evas_Object *obj);
479 static int evas_object_textblock_is_opaque(Evas_Object *obj);
480 static int evas_object_textblock_was_opaque(Evas_Object *obj);
482 static void evas_object_textblock_coords_recalc(Evas_Object *obj);
484 static void evas_object_textblock_scale_update(Evas_Object *obj);
486 static const Evas_Object_Func object_func =
488 /* methods (compulsory) */
489 evas_object_textblock_free,
490 evas_object_textblock_render,
491 evas_object_textblock_render_pre,
492 evas_object_textblock_render_post,
493 evas_object_textblock_id_get,
494 evas_object_textblock_visual_id_get,
495 evas_object_textblock_engine_data_get,
496 /* these are optional. NULL = nothing */
501 evas_object_textblock_is_opaque,
502 evas_object_textblock_was_opaque,
505 evas_object_textblock_coords_recalc,
506 evas_object_textblock_scale_update,
512 /* the actual api call to add a textblock */
515 Evas_Object_Textblock *o; \
516 MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); \
519 o = (Evas_Object_Textblock *)(obj->object_data); \
520 MAGIC_CHECK(o, Evas_Object_Textblock, MAGIC_OBJ_TEXTBLOCK); \
524 #define TB_HEAD_RETURN(x) \
525 Evas_Object_Textblock *o; \
526 MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); \
529 o = (Evas_Object_Textblock *)(obj->object_data); \
530 MAGIC_CHECK(o, Evas_Object_Textblock, MAGIC_OBJ_TEXTBLOCK); \
536 static Eina_Bool _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur);
537 static void _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n);
538 static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur);
539 static size_t _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt);
540 static void _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visual_adjustment);
541 static void _evas_textblock_node_format_free(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n);
542 static void _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n);
543 static void _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj);
544 static void _evas_textblock_invalidate_all(Evas_Object_Textblock *o);
545 static void _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset);
546 static void _evas_textblock_cursors_set_node(Evas_Object_Textblock *o, const Evas_Object_Textblock_Node_Text *n, Evas_Object_Textblock_Node_Text *new_node);
551 * Clears the textblock style passed except for the style_text which is replaced.
552 * @param ts The ts to be cleared. Must not be NULL.
553 * @param style_text the style's text.
556 _style_replace(Evas_Textblock_Style *ts, const char *style_text)
558 eina_stringshare_replace(&ts->style_text, style_text);
559 if (ts->default_tag) free(ts->default_tag);
562 Evas_Object_Style_Tag *tag;
564 tag = (Evas_Object_Style_Tag *)ts->tags;
565 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
567 free(tag->tag.replace);
570 ts->default_tag = NULL;
576 * Clears the textblock style passed.
577 * @param ts The ts to be cleared. Must not be NULL.
580 _style_clear(Evas_Textblock_Style *ts)
582 _style_replace(ts, NULL);
587 * Searches inside the tags stored in the style for the tag matching s.
588 * @param ts The ts to be cleared. Must not be NULL.
589 * @param s The tag to be matched.
590 * @param tag_len the length of the tag string.
591 * @param[out] replace_len The length of the replcaement found. - Must not be NULL.
592 * @return The replacement string found.
594 static inline const char *
595 _style_match_tag(const Evas_Textblock_Style *ts, const char *s, size_t tag_len, size_t *replace_len)
597 Evas_Object_Style_Tag *tag;
599 /* Try the style tags */
600 EINA_INLIST_FOREACH(ts->tags, tag)
602 if (tag->tag.tag_len != tag_len) continue;
603 if (!strncmp(tag->tag.tag, s, tag_len))
605 *replace_len = tag->tag.replace_len;
606 return tag->tag.replace;
610 /* Try the default tags */
613 const Evas_Object_Style_Tag_Base *btag;
614 for (btag = default_tags, i = 0 ;
615 i < (sizeof(default_tags) / sizeof(default_tags[0])) ;
618 if (btag->tag_len != tag_len) continue;
619 if (!strncmp(btag->tag, s, tag_len))
621 *replace_len = btag->replace_len;
622 return btag->replace;
633 * Clears all the nodes (text and format) of the textblock object.
634 * @param obj The evas object, must not be NULL.
637 _nodes_clear(const Evas_Object *obj)
639 Evas_Object_Textblock *o;
641 o = (Evas_Object_Textblock *)(obj->object_data);
642 while (o->text_nodes)
644 Evas_Object_Textblock_Node_Text *n;
647 o->text_nodes = _NODE_TEXT(eina_inlist_remove(
648 EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
649 _evas_textblock_node_text_free(n);
651 while (o->format_nodes)
653 Evas_Object_Textblock_Node_Format *n;
656 o->format_nodes = _NODE_FORMAT(eina_inlist_remove(EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
657 _evas_textblock_node_format_free(o, n);
663 * Unrefs and frees (if needed) a textblock format.
664 * @param obj The Evas_Object, Must not be NULL.
665 * @param fmt the format to be cleaned, must not be NULL.
668 _format_unref_free(const Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
671 if (fmt->ref > 0) return;
672 if (fmt->font.fdesc) evas_font_desc_unref(fmt->font.fdesc);
673 if (fmt->font.source) eina_stringshare_del(fmt->font.source);
674 evas_font_free(obj->layer->evas, fmt->font.font);
681 * @param obj The evas object, must not be NULL.
682 * @param ln the layout line on which the item is in, must not be NULL.
683 * @param it the layout item to be freed
686 _item_free(const Evas_Object *obj, Evas_Object_Textblock_Line *ln, Evas_Object_Textblock_Item *it)
688 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
690 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
692 evas_common_text_props_content_unref(&ti->text_props);
696 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
698 if (fi->item) eina_stringshare_del(fi->item);
700 _format_unref_free(obj, it->format);
703 ln->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(
704 EINA_INLIST_GET(ln->items), EINA_INLIST_GET(ln->items));
711 * Free a layout line.
712 * @param obj The evas object, must not be NULL.
713 * @param ln the layout line to be freed, must not be NULL.
716 _line_free(Evas_Object_Textblock_Line *ln)
718 /* Items are freed from the logical list, except for the ellip item */
722 /* table of html escapes (that i can find) this should be ordered with the
723 * most common first as it's a linear search to match - no hash for this.
725 * these are stored as one large string and one additional array that
726 * contains the offsets to the tokens for space efficiency.
730 * @var escape_strings[]
731 * This string consists of NULL terminated pairs of strings, the first of
732 * every pair is an escape and the second is the value of the escape.
734 static const char escape_strings[] =
735 /* most common escaped stuff */
741 " \0" "\xc2\xa0\0"
742 "¡\0" "\xc2\xa1\0"
743 "¢\0" "\xc2\xa2\0"
744 "£\0" "\xc2\xa3\0"
745 "¤\0" "\xc2\xa4\0"
746 "¥\0" "\xc2\xa5\0"
747 "¦\0" "\xc2\xa6\0"
748 "§\0" "\xc2\xa7\0"
749 "¨\0" "\xc2\xa8\0"
750 "©\0" "\xc2\xa9\0"
751 "ª\0" "\xc2\xaa\0"
752 "«\0" "\xc2\xab\0"
753 "¬\0" "\xc2\xac\0"
754 "®\0" "\xc2\xae\0"
755 "¯\0" "\xc2\xaf\0"
756 "°\0" "\xc2\xb0\0"
757 "±\0" "\xc2\xb1\0"
758 "²\0" "\xc2\xb2\0"
759 "³\0" "\xc2\xb3\0"
760 "´\0" "\xc2\xb4\0"
761 "µ\0" "\xc2\xb5\0"
762 "¶\0" "\xc2\xb6\0"
763 "·\0" "\xc2\xb7\0"
764 "¸\0" "\xc2\xb8\0"
765 "¹\0" "\xc2\xb9\0"
766 "º\0" "\xc2\xba\0"
767 "»\0" "\xc2\xbb\0"
768 "¼\0" "\xc2\xbc\0"
769 "½\0" "\xc2\xbd\0"
770 "¾\0" "\xc2\xbe\0"
771 "¿\0" "\xc2\xbf\0"
772 "À\0" "\xc3\x80\0"
773 "Á\0" "\xc3\x81\0"
774 "Â\0" "\xc3\x82\0"
775 "Ã\0" "\xc3\x83\0"
776 "Ä\0" "\xc3\x84\0"
777 "Å\0" "\xc3\x85\0"
778 "&Aelig;\0" "\xc3\x86\0"
779 "Ç\0" "\xc3\x87\0"
780 "È\0" "\xc3\x88\0"
781 "É\0" "\xc3\x89\0"
782 "Ê\0" "\xc3\x8a\0"
783 "Ë\0" "\xc3\x8b\0"
784 "Ì\0" "\xc3\x8c\0"
785 "Í\0" "\xc3\x8d\0"
786 "Î\0" "\xc3\x8e\0"
787 "Ï\0" "\xc3\x8f\0"
788 "&Eth;\0" "\xc3\x90\0"
789 "Ñ\0" "\xc3\x91\0"
790 "Ò\0" "\xc3\x92\0"
791 "Ó\0" "\xc3\x93\0"
792 "Ô\0" "\xc3\x94\0"
793 "Õ\0" "\xc3\x95\0"
794 "Ö\0" "\xc3\x96\0"
795 "×\0" "\xc3\x97\0"
796 "Ø\0" "\xc3\x98\0"
797 "Ù\0" "\xc3\x99\0"
798 "Ú\0" "\xc3\x9a\0"
799 "Û\0" "\xc3\x9b\0"
800 "Ý\0" "\xc3\x9d\0"
801 "&Thorn;\0" "\xc3\x9e\0"
802 "ß\0" "\xc3\x9f\0"
803 "à\0" "\xc3\xa0\0"
804 "á\0" "\xc3\xa1\0"
805 "â\0" "\xc3\xa2\0"
806 "ã\0" "\xc3\xa3\0"
807 "ä\0" "\xc3\xa4\0"
808 "å\0" "\xc3\xa5\0"
809 "æ\0" "\xc3\xa6\0"
810 "ç\0" "\xc3\xa7\0"
811 "è\0" "\xc3\xa8\0"
812 "é\0" "\xc3\xa9\0"
813 "ê\0" "\xc3\xaa\0"
814 "ë\0" "\xc3\xab\0"
815 "ì\0" "\xc3\xac\0"
816 "í\0" "\xc3\xad\0"
817 "î\0" "\xc3\xae\0"
818 "ï\0" "\xc3\xaf\0"
819 "ð\0" "\xc3\xb0\0"
820 "ñ\0" "\xc3\xb1\0"
821 "ò\0" "\xc3\xb2\0"
822 "ó\0" "\xc3\xb3\0"
823 "ô\0" "\xc3\xb4\0"
824 "õ\0" "\xc3\xb5\0"
825 "ö\0" "\xc3\xb6\0"
826 "÷\0" "\xc3\xb7\0"
827 "ø\0" "\xc3\xb8\0"
828 "ù\0" "\xc3\xb9\0"
829 "ú\0" "\xc3\xba\0"
830 "û\0" "\xc3\xbb\0"
831 "ü\0" "\xc3\xbc\0"
832 "ý\0" "\xc3\xbd\0"
833 "þ\0" "\xc3\xbe\0"
834 "ÿ\0" "\xc3\xbf\0"
835 "α\0" "\xce\x91\0"
836 "β\0" "\xce\x92\0"
837 "γ\0" "\xce\x93\0"
838 "δ\0" "\xce\x94\0"
839 "ε\0" "\xce\x95\0"
840 "ζ\0" "\xce\x96\0"
841 "η\0" "\xce\x97\0"
842 "θ\0" "\xce\x98\0"
843 "ι\0" "\xce\x99\0"
844 "κ\0" "\xce\x9a\0"
845 "λ\0" "\xce\x9b\0"
846 "μ\0" "\xce\x9c\0"
847 "ν\0" "\xce\x9d\0"
848 "ξ\0" "\xce\x9e\0"
849 "ο\0" "\xce\x9f\0"
850 "π\0" "\xce\xa0\0"
851 "ρ\0" "\xce\xa1\0"
852 "σ\0" "\xce\xa3\0"
853 "τ\0" "\xce\xa4\0"
854 "υ\0" "\xce\xa5\0"
855 "φ\0" "\xce\xa6\0"
856 "χ\0" "\xce\xa7\0"
857 "ψ\0" "\xce\xa8\0"
858 "ω\0" "\xce\xa9\0"
859 "…\0" "\xe2\x80\xa6\0"
860 "€\0" "\xe2\x82\xac\0"
861 "←\0" "\xe2\x86\x90\0"
862 "↑\0" "\xe2\x86\x91\0"
863 "→\0" "\xe2\x86\x92\0"
864 "↓\0" "\xe2\x86\x93\0"
865 "↔\0" "\xe2\x86\x94\0"
866 "←\0" "\xe2\x87\x90\0"
867 "→\0" "\xe2\x87\x92\0"
868 "∀\0" "\xe2\x88\x80\0"
869 "∃\0" "\xe2\x88\x83\0"
870 "∇\0" "\xe2\x88\x87\0"
871 "∏\0" "\xe2\x88\x8f\0"
872 "∑\0" "\xe2\x88\x91\0"
873 "∧\0" "\xe2\x88\xa7\0"
874 "∨\0" "\xe2\x88\xa8\0"
875 "∫\0" "\xe2\x88\xab\0"
876 "≠\0" "\xe2\x89\xa0\0"
877 "≡\0" "\xe2\x89\xa1\0"
878 "⊕\0" "\xe2\x8a\x95\0"
879 "⊥\0" "\xe2\x8a\xa5\0"
880 "†\0" "\xe2\x80\xa0\0"
881 "‡\0" "\xe2\x80\xa1\0"
882 "•\0" "\xe2\x80\xa2\0"
885 EVAS_MEMPOOL(_mp_obj);
889 * Checks if a char is a whitespace.
890 * @param c the unicode codepoint.
891 * @return @c EINA_TRUE if the unicode codepoint is a whitespace, @c EINA_FALSE
895 _is_white(Eina_Unicode c)
898 * unicode list of whitespace chars
900 * 0009..000D <control-0009>..<control-000D>
902 * 0085 <control-0085>
903 * 00A0 NO-BREAK SPACE
904 * 1680 OGHAM SPACE MARK
905 * 180E MONGOLIAN VOWEL SEPARATOR
906 * 2000..200A EN QUAD..HAIR SPACE
907 * 2028 LINE SEPARATOR
908 * 2029 PARAGRAPH SEPARATOR
909 * 202F NARROW NO-BREAK SPACE
910 * 205F MEDIUM MATHEMATICAL SPACE
911 * 3000 IDEOGRAPHIC SPACE
915 ((c >= 0x9) && (c <= 0xd)) ||
920 ((c >= 0x2000) && (c <= 0x200a)) ||
933 * Prepends the text between s and p to the main cursor of the object.
935 * @param cur the cursor to prepend to.
936 * @param[in] s start of the string
937 * @param[in] p end of the string
940 _prepend_text_run(Evas_Textblock_Cursor *cur, const char *s, const char *p)
946 ts = alloca(p - s + 1);
947 strncpy(ts, s, p - s);
949 evas_textblock_cursor_text_prepend(cur, ts);
956 * Returns the numeric value of HEX chars for example for ch = 'A'
957 * the function will return 10.
959 * @param ch The HEX char.
960 * @return numeric value of HEX.
963 _hex_string_get(char ch)
965 if ((ch >= '0') && (ch <= '9')) return (ch - '0');
966 else if ((ch >= 'A') && (ch <= 'F')) return (ch - 'A' + 10);
967 else if ((ch >= 'a') && (ch <= 'f')) return (ch - 'a' + 10);
973 * Parses a string of one of the formas:
978 * To the rgba values.
980 * @param[in] str The string to parse - NOT NULL.
981 * @param[out] r The Red value - NOT NULL.
982 * @param[out] g The Green value - NOT NULL.
983 * @param[out] b The Blue value - NOT NULL.
984 * @param[out] a The Alpha value - NOT NULL.
987 _format_color_parse(const char *str, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a)
992 *r = *g = *b = *a = 0;
994 if (slen == 7) /* #RRGGBB */
996 *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2]));
997 *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4]));
998 *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6]));
1001 else if (slen == 9) /* #RRGGBBAA */
1003 *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2]));
1004 *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4]));
1005 *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6]));
1006 *a = (_hex_string_get(str[7]) << 4) | (_hex_string_get(str[8]));
1008 else if (slen == 4) /* #RGB */
1010 *r = _hex_string_get(str[1]);
1011 *r = (*r << 4) | *r;
1012 *g = _hex_string_get(str[2]);
1013 *g = (*g << 4) | *g;
1014 *b = _hex_string_get(str[3]);
1015 *b = (*b << 4) | *b;
1018 else if (slen == 5) /* #RGBA */
1020 *r = _hex_string_get(str[1]);
1021 *r = (*r << 4) | *r;
1022 *g = _hex_string_get(str[2]);
1023 *g = (*g << 4) | *g;
1024 *b = _hex_string_get(str[3]);
1025 *b = (*b << 4) | *b;
1026 *a = _hex_string_get(str[4]);
1027 *a = (*a << 4) | *a;
1029 *r = (*r * *a) / 255;
1030 *g = (*g * *a) / 255;
1031 *b = (*b * *a) / 255;
1034 /* The refcount for the formats. */
1035 static int format_refcount = 0;
1036 /* Holders for the stringshares */
1037 static const char *fontstr = NULL;
1038 static const char *font_fallbacksstr = NULL;
1039 static const char *font_sizestr = NULL;
1040 static const char *font_sourcestr = NULL;
1041 static const char *font_weightstr = NULL;
1042 static const char *font_stylestr = NULL;
1043 static const char *font_widthstr = NULL;
1044 static const char *langstr = NULL;
1045 static const char *colorstr = NULL;
1046 static const char *underline_colorstr = NULL;
1047 static const char *underline2_colorstr = NULL;
1048 static const char *underline_dash_colorstr = NULL;
1049 static const char *outline_colorstr = NULL;
1050 static const char *shadow_colorstr = NULL;
1051 static const char *glow_colorstr = NULL;
1052 static const char *glow2_colorstr = NULL;
1053 static const char *backing_colorstr = NULL;
1054 static const char *strikethrough_colorstr = NULL;
1055 static const char *alignstr = NULL;
1056 static const char *valignstr = NULL;
1057 static const char *wrapstr = NULL;
1058 static const char *left_marginstr = NULL;
1059 static const char *right_marginstr = NULL;
1060 static const char *underlinestr = NULL;
1061 static const char *strikethroughstr = NULL;
1062 static const char *backingstr = NULL;
1063 static const char *stylestr = NULL;
1064 static const char *tabstopsstr = NULL;
1065 static const char *linesizestr = NULL;
1066 static const char *linerelsizestr = NULL;
1067 static const char *linegapstr = NULL;
1068 static const char *linerelgapstr = NULL;
1069 static const char *itemstr = NULL;
1070 static const char *linefillstr = NULL;
1071 static const char *ellipsisstr = NULL;
1072 static const char *passwordstr = NULL;
1073 static const char *underline_dash_widthstr = NULL;
1074 static const char *underline_dash_gapstr = NULL;
1078 * Init the format strings.
1081 _format_command_init(void)
1083 if (format_refcount == 0)
1085 fontstr = eina_stringshare_add("font");
1086 font_fallbacksstr = eina_stringshare_add("font_fallbacks");
1087 font_sizestr = eina_stringshare_add("font_size");
1088 font_sourcestr = eina_stringshare_add("font_source");
1089 font_weightstr = eina_stringshare_add("font_weight");
1090 font_stylestr = eina_stringshare_add("font_style");
1091 font_widthstr = eina_stringshare_add("font_width");
1092 langstr = eina_stringshare_add("lang");
1093 colorstr = eina_stringshare_add("color");
1094 underline_colorstr = eina_stringshare_add("underline_color");
1095 underline2_colorstr = eina_stringshare_add("underline2_color");
1096 underline_dash_colorstr = eina_stringshare_add("underline_dash_color");
1097 outline_colorstr = eina_stringshare_add("outline_color");
1098 shadow_colorstr = eina_stringshare_add("shadow_color");
1099 glow_colorstr = eina_stringshare_add("glow_color");
1100 glow2_colorstr = eina_stringshare_add("glow2_color");
1101 backing_colorstr = eina_stringshare_add("backing_color");
1102 strikethrough_colorstr = eina_stringshare_add("strikethrough_color");
1103 alignstr = eina_stringshare_add("align");
1104 valignstr = eina_stringshare_add("valign");
1105 wrapstr = eina_stringshare_add("wrap");
1106 left_marginstr = eina_stringshare_add("left_margin");
1107 right_marginstr = eina_stringshare_add("right_margin");
1108 underlinestr = eina_stringshare_add("underline");
1109 strikethroughstr = eina_stringshare_add("strikethrough");
1110 backingstr = eina_stringshare_add("backing");
1111 stylestr = eina_stringshare_add("style");
1112 tabstopsstr = eina_stringshare_add("tabstops");
1113 linesizestr = eina_stringshare_add("linesize");
1114 linerelsizestr = eina_stringshare_add("linerelsize");
1115 linegapstr = eina_stringshare_add("linegap");
1116 linerelgapstr = eina_stringshare_add("linerelgap");
1117 itemstr = eina_stringshare_add("item");
1118 linefillstr = eina_stringshare_add("linefill");
1119 ellipsisstr = eina_stringshare_add("ellipsis");
1120 passwordstr = eina_stringshare_add("password");
1121 underline_dash_widthstr = eina_stringshare_add("underline_dash_width");
1122 underline_dash_gapstr = eina_stringshare_add("underline_dash_gap");
1129 * Shutdown the format strings.
1132 _format_command_shutdown(void)
1134 if (--format_refcount > 0) return;
1136 eina_stringshare_del(fontstr);
1137 eina_stringshare_del(font_fallbacksstr);
1138 eina_stringshare_del(font_sizestr);
1139 eina_stringshare_del(font_sourcestr);
1140 eina_stringshare_del(font_weightstr);
1141 eina_stringshare_del(font_stylestr);
1142 eina_stringshare_del(font_widthstr);
1143 eina_stringshare_del(langstr);
1144 eina_stringshare_del(colorstr);
1145 eina_stringshare_del(underline_colorstr);
1146 eina_stringshare_del(underline2_colorstr);
1147 eina_stringshare_del(underline_dash_colorstr);
1148 eina_stringshare_del(outline_colorstr);
1149 eina_stringshare_del(shadow_colorstr);
1150 eina_stringshare_del(glow_colorstr);
1151 eina_stringshare_del(glow2_colorstr);
1152 eina_stringshare_del(backing_colorstr);
1153 eina_stringshare_del(strikethrough_colorstr);
1154 eina_stringshare_del(alignstr);
1155 eina_stringshare_del(valignstr);
1156 eina_stringshare_del(wrapstr);
1157 eina_stringshare_del(left_marginstr);
1158 eina_stringshare_del(right_marginstr);
1159 eina_stringshare_del(underlinestr);
1160 eina_stringshare_del(strikethroughstr);
1161 eina_stringshare_del(backingstr);
1162 eina_stringshare_del(stylestr);
1163 eina_stringshare_del(tabstopsstr);
1164 eina_stringshare_del(linesizestr);
1165 eina_stringshare_del(linerelsizestr);
1166 eina_stringshare_del(linegapstr);
1167 eina_stringshare_del(linerelgapstr);
1168 eina_stringshare_del(itemstr);
1169 eina_stringshare_del(linefillstr);
1170 eina_stringshare_del(ellipsisstr);
1171 eina_stringshare_del(passwordstr);
1172 eina_stringshare_del(underline_dash_widthstr);
1173 eina_stringshare_del(underline_dash_gapstr);
1178 * Copies str to dst while removing the \\ char, i.e unescape the escape sequences.
1180 * @param[out] dst the destination string - Should not be NULL.
1181 * @param[in] src the source string - Should not be NULL.
1184 _format_clean_param(char *dst, const char *src)
1190 for (ss = src; *ss; ss++, ds++)
1192 if ((*ss == '\\') && *(ss + 1)) ss++;
1200 * Parses the cmd and parameter and adds the parsed format to fmt.
1202 * @param obj the evas object - should not be NULL.
1203 * @param fmt The format to populate - should not be NULL.
1204 * @param[in] cmd the command to process, should be stringshared.
1205 * @param[in] param the parameter of the command.
1208 _format_command(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *cmd, const char *param)
1213 len = strlen(param);
1214 tmp_param = alloca(len + 1);
1216 _format_clean_param(tmp_param, param);
1218 /* If we are changing the font, create the fdesc. */
1219 if ((cmd == font_weightstr) || (cmd == font_widthstr) ||
1220 (cmd == font_stylestr) || (cmd == langstr) ||
1221 (cmd == fontstr) || (cmd == font_fallbacksstr))
1223 if (!fmt->font.fdesc)
1225 fmt->font.fdesc = evas_font_desc_new();
1227 else if (!fmt->font.fdesc->is_new)
1229 Evas_Font_Description *old = fmt->font.fdesc;
1230 fmt->font.fdesc = evas_font_desc_dup(fmt->font.fdesc);
1231 if (old) evas_font_desc_unref(old);
1238 evas_font_name_parse(fmt->font.fdesc, tmp_param);
1240 else if (cmd == font_fallbacksstr)
1242 eina_stringshare_replace(&(fmt->font.fdesc->fallbacks), tmp_param);
1244 else if (cmd == font_sizestr)
1248 v = atoi(tmp_param);
1249 if (v != fmt->font.size)
1254 else if (cmd == font_sourcestr)
1256 if ((!fmt->font.source) ||
1257 ((fmt->font.source) && (strcmp(fmt->font.source, tmp_param))))
1259 if (fmt->font.source) eina_stringshare_del(fmt->font.source);
1260 fmt->font.source = eina_stringshare_add(tmp_param);
1263 else if (cmd == font_weightstr)
1265 fmt->font.fdesc->weight = evas_font_style_find(tmp_param,
1266 tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_WEIGHT);
1268 else if (cmd == font_stylestr)
1270 fmt->font.fdesc->slant = evas_font_style_find(tmp_param,
1271 tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_SLANT);
1273 else if (cmd == font_widthstr)
1275 fmt->font.fdesc->width = evas_font_style_find(tmp_param,
1276 tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_WIDTH);
1278 else if (cmd == langstr)
1280 eina_stringshare_replace(&(fmt->font.fdesc->lang), tmp_param);
1282 else if (cmd == colorstr)
1283 _format_color_parse(tmp_param,
1284 &(fmt->color.normal.r), &(fmt->color.normal.g),
1285 &(fmt->color.normal.b), &(fmt->color.normal.a));
1286 else if (cmd == underline_colorstr)
1287 _format_color_parse(tmp_param,
1288 &(fmt->color.underline.r), &(fmt->color.underline.g),
1289 &(fmt->color.underline.b), &(fmt->color.underline.a));
1290 else if (cmd == underline2_colorstr)
1291 _format_color_parse(tmp_param,
1292 &(fmt->color.underline2.r), &(fmt->color.underline2.g),
1293 &(fmt->color.underline2.b), &(fmt->color.underline2.a));
1294 else if (cmd == underline_dash_colorstr)
1295 _format_color_parse(tmp_param,
1296 &(fmt->color.underline_dash.r), &(fmt->color.underline_dash.g),
1297 &(fmt->color.underline_dash.b), &(fmt->color.underline_dash.a));
1298 else if (cmd == outline_colorstr)
1299 _format_color_parse(tmp_param,
1300 &(fmt->color.outline.r), &(fmt->color.outline.g),
1301 &(fmt->color.outline.b), &(fmt->color.outline.a));
1302 else if (cmd == shadow_colorstr)
1303 _format_color_parse(tmp_param,
1304 &(fmt->color.shadow.r), &(fmt->color.shadow.g),
1305 &(fmt->color.shadow.b), &(fmt->color.shadow.a));
1306 else if (cmd == glow_colorstr)
1307 _format_color_parse(tmp_param,
1308 &(fmt->color.glow.r), &(fmt->color.glow.g),
1309 &(fmt->color.glow.b), &(fmt->color.glow.a));
1310 else if (cmd == glow2_colorstr)
1311 _format_color_parse(tmp_param,
1312 &(fmt->color.glow2.r), &(fmt->color.glow2.g),
1313 &(fmt->color.glow2.b), &(fmt->color.glow2.a));
1314 else if (cmd == backing_colorstr)
1315 _format_color_parse(tmp_param,
1316 &(fmt->color.backing.r), &(fmt->color.backing.g),
1317 &(fmt->color.backing.b), &(fmt->color.backing.a));
1318 else if (cmd == strikethrough_colorstr)
1319 _format_color_parse(tmp_param,
1320 &(fmt->color.strikethrough.r), &(fmt->color.strikethrough.g),
1321 &(fmt->color.strikethrough.b), &(fmt->color.strikethrough.a));
1322 else if (cmd == alignstr)
1324 if (!strcmp(tmp_param, "auto"))
1326 fmt->halign_auto = EINA_TRUE;
1330 if (!strcmp(tmp_param, "middle")) fmt->halign = 0.5;
1331 else if (!strcmp(tmp_param, "center")) fmt->halign = 0.5;
1332 else if (!strcmp(tmp_param, "left")) fmt->halign = 0.0;
1333 else if (!strcmp(tmp_param, "right")) fmt->halign = 1.0;
1336 char *endptr = NULL;
1337 double val = strtod(tmp_param, &endptr);
1340 while (*endptr && _is_white(*endptr))
1346 if (fmt->halign < 0.0) fmt->halign = 0.0;
1347 else if (fmt->halign > 1.0) fmt->halign = 1.0;
1349 fmt->halign_auto = EINA_FALSE;
1352 else if (cmd == valignstr)
1354 if (!strcmp(tmp_param, "top")) fmt->valign = 0.0;
1355 else if (!strcmp(tmp_param, "middle")) fmt->valign = 0.5;
1356 else if (!strcmp(tmp_param, "center")) fmt->valign = 0.5;
1357 else if (!strcmp(tmp_param, "bottom")) fmt->valign = 1.0;
1358 else if (!strcmp(tmp_param, "baseline")) fmt->valign = -1.0;
1359 else if (!strcmp(tmp_param, "base")) fmt->valign = -1.0;
1362 char *endptr = NULL;
1363 double val = strtod(tmp_param, &endptr);
1366 while (*endptr && _is_white(*endptr))
1372 if (fmt->valign < 0.0) fmt->valign = 0.0;
1373 else if (fmt->valign > 1.0) fmt->valign = 1.0;
1376 else if (cmd == wrapstr)
1378 if (!strcmp(tmp_param, "word"))
1381 fmt->wrap_char = fmt->wrap_mixed = 0;
1383 else if (!strcmp(tmp_param, "char"))
1385 fmt->wrap_word = fmt->wrap_mixed = 0;
1388 else if (!strcmp(tmp_param, "mixed"))
1390 fmt->wrap_word = fmt->wrap_char = 0;
1391 fmt->wrap_mixed = 1;
1395 fmt->wrap_word = fmt->wrap_mixed = fmt->wrap_char = 0;
1398 else if (cmd == left_marginstr)
1400 if (!strcmp(tmp_param, "reset"))
1404 if (tmp_param[0] == '+')
1405 fmt->margin.l += atoi(&(tmp_param[1]));
1406 else if (tmp_param[0] == '-')
1407 fmt->margin.l -= atoi(&(tmp_param[1]));
1409 fmt->margin.l = atoi(tmp_param);
1410 if (fmt->margin.l < 0) fmt->margin.l = 0;
1413 else if (cmd == right_marginstr)
1415 if (!strcmp(tmp_param, "reset"))
1419 if (tmp_param[0] == '+')
1420 fmt->margin.r += atoi(&(tmp_param[1]));
1421 else if (tmp_param[0] == '-')
1422 fmt->margin.r -= atoi(&(tmp_param[1]));
1424 fmt->margin.r = atoi(tmp_param);
1425 if (fmt->margin.r < 0) fmt->margin.r = 0;
1428 else if (cmd == underlinestr)
1430 if (!strcmp(tmp_param, "off"))
1433 fmt->underline2 = 0;
1435 else if ((!strcmp(tmp_param, "on")) ||
1436 (!strcmp(tmp_param, "single")))
1439 fmt->underline2 = 0;
1441 else if (!strcmp(tmp_param, "double"))
1444 fmt->underline2 = 1;
1446 else if (!strcmp(tmp_param, "dashed"))
1447 fmt->underline_dash = 1;
1449 else if (cmd == strikethroughstr)
1451 if (!strcmp(tmp_param, "off"))
1452 fmt->strikethrough = 0;
1453 else if (!strcmp(tmp_param, "on"))
1454 fmt->strikethrough = 1;
1456 else if (cmd == backingstr)
1458 if (!strcmp(tmp_param, "off"))
1460 else if (!strcmp(tmp_param, "on"))
1463 else if (cmd == stylestr)
1465 char *p1, *p2, *p, *pp;
1467 p1 = alloca(len + 1);
1469 p2 = alloca(len + 1);
1472 if (!strstr(tmp_param, ",")) p1 = tmp_param;
1475 /* split string "str1,str2" into p1 and p2 (if we have more than
1476 * 1 str2 eg "str1,str2,str3,str4" then we don't care. p2 just
1477 * ends up being the last one as right now it's only valid to have
1478 * 1 comma and 2 strings */
1480 for (p = tmp_param; *p; p++)
1493 if (!strcmp(p1, "off")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1494 else if (!strcmp(p1, "none")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1495 else if (!strcmp(p1, "plain")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1496 else if (!strcmp(p1, "shadow")) fmt->style = EVAS_TEXT_STYLE_SHADOW;
1497 else if (!strcmp(p1, "outline")) fmt->style = EVAS_TEXT_STYLE_OUTLINE;
1498 else if (!strcmp(p1, "soft_outline")) fmt->style = EVAS_TEXT_STYLE_SOFT_OUTLINE;
1499 else if (!strcmp(p1, "outline_shadow")) fmt->style = EVAS_TEXT_STYLE_OUTLINE_SHADOW;
1500 else if (!strcmp(p1, "outline_soft_shadow")) fmt->style = EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW;
1501 else if (!strcmp(p1, "glow")) fmt->style = EVAS_TEXT_STYLE_GLOW;
1502 else if (!strcmp(p1, "far_shadow")) fmt->style = EVAS_TEXT_STYLE_FAR_SHADOW;
1503 else if (!strcmp(p1, "soft_shadow")) fmt->style = EVAS_TEXT_STYLE_SOFT_SHADOW;
1504 else if (!strcmp(p1, "far_soft_shadow")) fmt->style = EVAS_TEXT_STYLE_FAR_SOFT_SHADOW;
1505 else fmt->style = EVAS_TEXT_STYLE_PLAIN;
1509 if (!strcmp(p2, "bottom_right")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT);
1510 else if (!strcmp(p2, "bottom")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM);
1511 else if (!strcmp(p2, "bottom_left")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT);
1512 else if (!strcmp(p2, "left")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT);
1513 else if (!strcmp(p2, "top_left")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT);
1514 else if (!strcmp(p2, "top")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP);
1515 else if (!strcmp(p2, "top_right")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT);
1516 else if (!strcmp(p2, "right")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT);
1517 else EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT);
1520 else if (cmd == tabstopsstr)
1522 fmt->tabstops = atoi(tmp_param);
1523 if (fmt->tabstops < 1) fmt->tabstops = 1;
1525 else if (cmd == linesizestr)
1527 fmt->linesize = atoi(tmp_param);
1528 fmt->linerelsize = 0.0;
1530 else if (cmd == linerelsizestr)
1532 char *endptr = NULL;
1533 double val = strtod(tmp_param, &endptr);
1536 while (*endptr && _is_white(*endptr))
1540 fmt->linerelsize = val / 100.0;
1542 if (fmt->linerelsize < 0.0) fmt->linerelsize = 0.0;
1546 else if (cmd == linegapstr)
1548 fmt->linegap = atoi(tmp_param);
1549 fmt->linerelgap = 0.0;
1551 else if (cmd == linerelgapstr)
1553 char *endptr = NULL;
1554 double val = strtod(tmp_param, &endptr);
1557 while (*endptr && _is_white(*endptr))
1561 fmt->linerelgap = val / 100.0;
1563 if (fmt->linerelgap < 0.0) fmt->linerelgap = 0.0;
1567 else if (cmd == itemstr)
1569 // itemstr == replacement object items in textblock - inline imges
1572 else if (cmd == linefillstr)
1574 char *endptr = NULL;
1575 double val = strtod(tmp_param, &endptr);
1578 while (*endptr && _is_white(*endptr))
1582 fmt->linefill = val / 100.0;
1583 if (fmt->linefill < 0.0) fmt->linefill = 0.0;
1587 else if (cmd == ellipsisstr)
1589 char *endptr = NULL;
1590 fmt->ellipsis = strtod(tmp_param, &endptr);
1591 if ((fmt->ellipsis < 0.0) || (fmt->ellipsis > 1.0))
1592 fmt->ellipsis = -1.0;
1595 Evas_Object_Textblock *o;
1597 o = (Evas_Object_Textblock *)(obj->object_data);
1598 o->have_ellipsis = 1;
1601 else if (cmd == passwordstr)
1603 if (!strcmp(tmp_param, "off"))
1605 else if (!strcmp(tmp_param, "on"))
1608 else if (cmd == underline_dash_widthstr)
1610 fmt->underline_dash_width = atoi(tmp_param);
1611 if (fmt->underline_dash_width <= 0) fmt->underline_dash_width = 1;
1613 else if (cmd == underline_dash_gapstr)
1615 fmt->underline_dash_gap = atoi(tmp_param);
1616 if (fmt->underline_dash_gap <= 0) fmt->underline_dash_gap = 1;
1622 * Returns @c EINA_TRUE if the item is a format parameter, @c EINA_FALSE
1625 * @param[in] item the item to check - Not NULL.
1628 _format_is_param(const char *item)
1630 if (strchr(item, '=')) return EINA_TRUE;
1636 * Parse the format item and populate key and val with the stringshares that
1637 * corrospond to the formats parsed.
1638 * It expects item to be of the structure:
1641 * @param[in] item the item to parse - Not NULL.
1642 * @param[out] key where to store the key at - Not NULL.
1643 * @param[out] val where to store the value at - Not NULL.
1646 _format_param_parse(const char *item, const char **key, const char **val)
1648 const char *start, *end, *quote;
1650 start = strchr(item, '=');
1651 *key = eina_stringshare_add_length(item, start - item);
1652 start++; /* Advance after the '=' */
1653 /* If we can find a quote, our new delimiter is a quote, not a space. */
1654 if ((quote = strchr(start, '\'')))
1657 end = strchr(start, '\'');
1661 end = strchr(start, ' ');
1664 /* Null terminate before the spaces */
1667 *val = eina_stringshare_add_length(start, end - start);
1671 *val = eina_stringshare_add(start);
1677 * This function parses the format passed in *s and advances s to point to the
1678 * next format item, while returning the current one as the return value.
1679 * @param s The current and returned position in the format string.
1680 * @return the current item parsed from the string.
1683 _format_parse(const char **s)
1686 const char *s1 = NULL, *s2 = NULL;
1687 Eina_Bool quote = EINA_FALSE;;
1690 if (*p == 0) return NULL;
1695 if (*p != ' ') s1 = p;
1705 if ((p > *s) && (p[-1] != '\\') && (!quote))
1707 if (*p == ' ') s2 = p;
1709 if (*p == 0) s2 = p;
1724 * Parse the format str and populate fmt with the formats found.
1726 * @param obj The evas object - Not NULL.
1727 * @param[out] fmt The format to populate - Not NULL.
1728 * @param[in] str the string to parse.- Not NULL.
1731 _format_fill(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *str)
1738 /* get rid of any spaces at the start of the string */
1739 while (*s == ' ') s++;
1741 while ((item = _format_parse(&s)))
1743 if (_format_is_param(item))
1745 const char *key = NULL, *val = NULL;
1747 _format_param_parse(item, &key, &val);
1748 _format_command(obj, fmt, key, val);
1749 eina_stringshare_del(key);
1750 eina_stringshare_del(val);
1754 /* immediate - not handled here */
1761 * Duplicate a format and return the duplicate.
1763 * @param obj The evas object - Not NULL.
1764 * @param[in] fmt The format to duplicate - Not NULL.
1765 * @return the copy of the format.
1767 static Evas_Object_Textblock_Format *
1768 _format_dup(Evas_Object *obj, const Evas_Object_Textblock_Format *fmt)
1770 Evas_Object_Textblock_Format *fmt2;
1772 fmt2 = calloc(1, sizeof(Evas_Object_Textblock_Format));
1773 memcpy(fmt2, fmt, sizeof(Evas_Object_Textblock_Format));
1775 fmt2->font.fdesc = evas_font_desc_ref(fmt->font.fdesc);
1777 if (fmt->font.source) fmt2->font.source = eina_stringshare_add(fmt->font.source);
1779 /* FIXME: just ref the font here... */
1780 fmt2->font.font = evas_font_load(obj->layer->evas, fmt2->font.fdesc,
1781 fmt2->font.source, (int)(((double) fmt2->font.size) * obj->cur.scale));
1792 * A pack of information that needed to be passed around in the layout engine,
1793 * packed for easier access.
1795 typedef struct _Ctxt Ctxt;
1800 Evas_Object_Textblock *o;
1802 Evas_Object_Textblock_Paragraph *paragraphs;
1803 Evas_Object_Textblock_Paragraph *par;
1804 Evas_Object_Textblock_Line *ln;
1807 Eina_List *format_stack;
1808 Evas_Object_Textblock_Format *fmt;
1813 int maxascent, maxdescent;
1814 int marginl, marginr;
1816 int underline_extend;
1817 int have_underline, have_underline2;
1818 double align, valign;
1819 Eina_Bool align_auto : 1;
1820 Eina_Bool width_changed : 1;
1823 static void _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti, Eina_List *rel);
1824 static void _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti);
1825 static void _layout_do_format(const Evas_Object *obj, Ctxt *c, Evas_Object_Textblock_Format **_fmt, Evas_Object_Textblock_Node_Format *n, int *style_pad_l, int *style_pad_r, int *style_pad_t, int *style_pad_b, Eina_Bool create_item);
1828 * Adjust the ascent/descent of the format and context.
1830 * @param maxascent The ascent to update - Not NUL.
1831 * @param maxdescent The descent to update - Not NUL.
1832 * @param fmt The format to adjust - NOT NULL.
1835 _layout_format_ascent_descent_adjust(const Evas_Object *obj,
1836 Evas_Coord *maxascent, Evas_Coord *maxdescent,
1837 Evas_Object_Textblock_Format *fmt)
1839 int ascent, descent;
1843 // ascent = c->ENFN->font_max_ascent_get(c->ENDT, fmt->font.font);
1844 // descent = c->ENFN->font_max_descent_get(c->ENDT, fmt->font.font);
1845 ascent = ENFN->font_ascent_get(ENDT, fmt->font.font);
1846 descent = ENFN->font_descent_get(ENDT, fmt->font.font);
1847 if (fmt->linesize > 0)
1849 if ((ascent + descent) < fmt->linesize)
1851 ascent = ((fmt->linesize * ascent) / (ascent + descent));
1852 descent = fmt->linesize - ascent;
1855 else if (fmt->linerelsize > 0.0)
1857 descent = descent * fmt->linerelsize;
1858 ascent = ascent * fmt->linerelsize;
1860 descent += fmt->linegap;
1861 descent += ((ascent + descent) * fmt->linerelgap);
1862 if (*maxascent < ascent) *maxascent = ascent;
1863 if (*maxdescent < descent) *maxdescent = descent;
1864 if (fmt->linefill > 0.0)
1868 dh = obj->cur.geometry.h - (*maxascent + *maxdescent);
1870 dh = fmt->linefill * dh;
1871 *maxdescent += dh / 2;
1872 *maxascent += dh - (dh / 2);
1873 // FIXME: set flag that says "if heigh changes - reformat"
1880 * Create a new line using the info from the format and update the format
1883 * @param c The context to work on - Not NULL.
1884 * @param fmt The format to use info from - NOT NULL.
1887 _layout_line_new(Ctxt *c, Evas_Object_Textblock_Format *fmt)
1889 c->ln = calloc(1, sizeof(Evas_Object_Textblock_Line));
1890 c->align = fmt->halign;
1891 c->align_auto = fmt->halign_auto;
1892 c->marginl = fmt->margin.l;
1893 c->marginr = fmt->margin.r;
1894 c->par->lines = (Evas_Object_Textblock_Line *)eina_inlist_append(EINA_INLIST_GET(c->par->lines), EINA_INLIST_GET(c->ln));
1896 c->maxascent = c->maxdescent = 0;
1897 c->ln->line_no = -1;
1898 c->ln->par = c->par;
1901 static inline Evas_Object_Textblock_Paragraph *
1902 _layout_find_paragraph_by_y(Evas_Object_Textblock *o, Evas_Coord y)
1904 Evas_Object_Textblock_Paragraph *start, *par;
1907 start = o->paragraphs;
1909 for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
1911 if (!o->par_index[i] || (o->par_index[i]->y > y))
1915 start = o->par_index[i];
1918 EINA_INLIST_FOREACH(start, par)
1920 if ((par->y <= y) && (y < par->y + par->h))
1927 static inline Evas_Object_Textblock_Paragraph *
1928 _layout_find_paragraph_by_line_no(Evas_Object_Textblock *o, int line_no)
1930 Evas_Object_Textblock_Paragraph *start, *par;
1933 start = o->paragraphs;
1935 for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
1937 if (!o->par_index[i] || (o->par_index[i]->line_no > line_no))
1941 start = o->par_index[i];
1944 EINA_INLIST_FOREACH(start, par)
1946 Evas_Object_Textblock_Paragraph *npar =
1947 (Evas_Object_Textblock_Paragraph *) EINA_INLIST_GET(par)->next;
1948 if ((par->line_no <= line_no) &&
1949 (!npar || (line_no < npar->line_no)))
1955 /* End of rbtree index functios */
1959 * Create a new layout paragraph.
1960 * If c->par is not NULL, the paragraph is appended/prepended according
1961 * to the append parameter. If it is NULL, the paragraph is appended at
1962 * the end of the list.
1964 * @param c The context to work on - Not NULL.
1965 * @param n the associated text node
1966 * @param append true to append, false to prpend.
1969 _layout_paragraph_new(Ctxt *c, Evas_Object_Textblock_Node_Text *n,
1972 Evas_Object_Textblock_Paragraph *rel_par = c->par;
1973 c->par = calloc(1, sizeof(Evas_Object_Textblock_Paragraph));
1974 if (append || !rel_par)
1975 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
1976 eina_inlist_append_relative(EINA_INLIST_GET(c->paragraphs),
1977 EINA_INLIST_GET(c->par),
1978 EINA_INLIST_GET(rel_par));
1980 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
1981 eina_inlist_prepend_relative(EINA_INLIST_GET(c->paragraphs),
1982 EINA_INLIST_GET(c->par),
1983 EINA_INLIST_GET(rel_par));
1986 c->par->text_node = n;
1989 c->par->line_no = -1;
1990 c->par->visible = 1;
1991 c->o->num_paragraphs++;
1997 * Update bidi paragraph props.
1999 * @param par The paragraph to update
2002 _layout_update_bidi_props(const Evas_Object_Textblock *o,
2003 Evas_Object_Textblock_Paragraph *par)
2007 const Eina_Unicode *text;
2008 int *segment_idxs = NULL;
2009 text = eina_ustrbuf_string_get(par->text_node->unicode);
2011 if (o->bidi_delimiters)
2012 segment_idxs = evas_bidi_segment_idxs_get(text, o->bidi_delimiters);
2014 evas_bidi_paragraph_props_unref(par->bidi_props);
2015 par->bidi_props = evas_bidi_paragraph_props_get(text,
2016 eina_ustrbuf_length_get(par->text_node->unicode),
2018 par->direction = EVAS_BIDI_PARAGRAPH_DIRECTION_IS_RTL(par->bidi_props) ?
2019 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
2020 par->is_bidi = !!par->bidi_props;
2021 if (segment_idxs) free(segment_idxs);
2029 * Free the visual lines in the paragraph (logical items are kept)
2032 _paragraph_clear(const Evas_Object *obj __UNUSED__,
2033 Evas_Object_Textblock_Paragraph *par)
2037 Evas_Object_Textblock_Line *ln;
2039 ln = (Evas_Object_Textblock_Line *) par->lines;
2040 par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(par->lines), EINA_INLIST_GET(par->lines));
2047 * Free the layout paragraph and all of it's lines and logical items.
2050 _paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
2052 Evas_Object_Textblock *o;
2053 o = (Evas_Object_Textblock *)(obj->object_data);
2054 _paragraph_clear(obj, par);
2057 Eina_List *i, *i_prev;
2058 Evas_Object_Textblock_Item *it;
2059 EINA_LIST_FOREACH_SAFE(par->logical_items, i, i_prev, it)
2061 _item_free(obj, NULL, it);
2063 eina_list_free(par->logical_items);
2066 if (par->bidi_props)
2067 evas_bidi_paragraph_props_unref(par->bidi_props);
2069 /* If we are the active par of the text node, set to NULL */
2070 if (par->text_node && (par->text_node->par == par))
2071 par->text_node->par = NULL;
2073 o->num_paragraphs--;
2080 * Clear all the paragraphs from the inlist pars.
2082 * @param obj the evas object - Not NULL.
2083 * @param pars the paragraphs to clean - Not NULL.
2086 _paragraphs_clear(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
2088 Evas_Object_Textblock_Paragraph *par;
2090 EINA_INLIST_FOREACH(EINA_INLIST_GET(pars), par)
2092 _paragraph_clear(obj, par);
2098 * Free the paragraphs from the inlist pars, the difference between this and
2099 * _paragraphs_clear is that the latter keeps the logical items and the par
2100 * items, while the former frees them as well.
2102 * @param obj the evas object - Not NULL.
2103 * @param pars the paragraphs to clean - Not NULL.
2106 _paragraphs_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
2108 Evas_Object_Textblock *o;
2109 o = (Evas_Object_Textblock *)(obj->object_data);
2111 o->num_paragraphs = 0;
2115 Evas_Object_Textblock_Paragraph *par;
2117 par = (Evas_Object_Textblock_Paragraph *) pars;
2118 pars = (Evas_Object_Textblock_Paragraph *)eina_inlist_remove(EINA_INLIST_GET(pars), EINA_INLIST_GET(par));
2119 _paragraph_free(obj, par);
2125 * Push fmt to the format stack, if fmt is NULL, will fush a default item.
2127 * @param c the context to work on - Not NULL.
2128 * @param fmt the format to push.
2129 * @see _layout_format_pop()
2131 static Evas_Object_Textblock_Format *
2132 _layout_format_push(Ctxt *c, Evas_Object_Textblock_Format *fmt,
2133 Evas_Object_Textblock_Node_Format *fnode)
2137 fmt = _format_dup(c->obj, fmt);
2138 c->format_stack = eina_list_prepend(c->format_stack, fmt);
2143 fmt = calloc(1, sizeof(Evas_Object_Textblock_Format));
2144 c->format_stack = eina_list_prepend(c->format_stack, fmt);
2147 fmt->halign_auto = EINA_TRUE;
2149 fmt->style = EVAS_TEXT_STYLE_PLAIN;
2152 fmt->linerelsize = 0.0;
2154 fmt->underline_dash_width = 6;
2155 fmt->underline_dash_gap = 2;
2156 fmt->linerelgap = 0.0;
2164 * Pop fmt to the format stack, if there's something in the stack free fmt
2165 * and set it to point to the next item instead, else return fmt.
2167 * @param c the context to work on - Not NULL.
2168 * @param format - the text of the format to free (assured to start with '-').
2169 * @return the next format in the stack, or format if there's none.
2170 * @see _layout_format_push()
2172 static Evas_Object_Textblock_Format *
2173 _layout_format_pop(Ctxt *c, const char *format)
2175 Evas_Object_Textblock_Format *fmt = eina_list_data_get(c->format_stack);
2177 if ((c->format_stack) && (c->format_stack->next))
2179 Eina_List *redo_nodes = NULL;
2181 /* Generic pop, should just pop. */
2182 if (((format[0] == ' ') && !format[1]) ||
2185 _format_unref_free(c->obj, fmt);
2187 eina_list_remove_list(c->format_stack, c->format_stack);
2191 size_t len = strlen(format);
2192 Eina_List *i, *i_next;
2193 /* Remove only the matching format. */
2194 EINA_LIST_FOREACH_SAFE(c->format_stack, i, i_next, fmt)
2196 /* Stop when we reach the base item */
2201 eina_list_remove_list(c->format_stack, c->format_stack);
2203 /* Make sure the ending tag matches the starting tag.
2204 * I.e whole of the ending tag matches the start of the
2205 * starting tag, and the starting tag's next char is either
2206 * NULL or white. Skip the starting '+'. */
2207 if (_FORMAT_IS_CLOSER_OF(
2208 fmt->fnode->orig_format, format, len))
2210 _format_unref_free(c->obj, fmt);
2215 redo_nodes = eina_list_prepend(redo_nodes, fmt->fnode);
2216 _format_unref_free(c->obj, fmt);
2221 /* Redo all the nodes needed to be redone */
2223 Evas_Object_Textblock_Node_Format *fnode;
2224 Eina_List *i, *i_next;
2226 EINA_LIST_FOREACH_SAFE(redo_nodes, i, i_next, fnode)
2228 /* FIXME: Actually do something with the new acquired padding,
2229 * the can be different and affect our padding! */
2230 Evas_Coord style_pad_l, style_pad_r, style_pad_t, style_pad_b;
2231 style_pad_l = style_pad_r = style_pad_t = style_pad_b = 0;
2232 redo_nodes = eina_list_remove_list(redo_nodes, i);
2233 fmt = eina_list_data_get(c->format_stack);
2234 _layout_do_format(c->obj, c, &fmt, fnode,
2235 &style_pad_l, &style_pad_r,
2236 &style_pad_t, &style_pad_b, EINA_FALSE);
2240 fmt = eina_list_data_get(c->format_stack);
2247 * Parse item and fill fmt with the item.
2249 * @param c the context to work on - Not NULL.
2250 * @param fmt the format to fill - not null.
2253 _layout_format_value_handle(Ctxt *c, Evas_Object_Textblock_Format *fmt, const char *item)
2255 const char *key = NULL, *val = NULL;
2257 _format_param_parse(item, &key, &val);
2258 if ((key) && (val)) _format_command(c->obj, fmt, key, val);
2259 if (key) eina_stringshare_del(key);
2260 if (val) eina_stringshare_del(val);
2261 c->align = fmt->halign;
2262 c->align_auto = fmt->halign_auto;
2263 c->marginl = fmt->margin.l;
2264 c->marginr = fmt->margin.r;
2267 #define VSIZE_FULL 0
2268 #define VSIZE_ASCENT 1
2276 * Get the current line's alignment from the context.
2278 * @param c the context to work on - Not NULL.
2280 static inline double
2281 _layout_line_align_get(Ctxt *c)
2284 if (c->align_auto && c->ln)
2286 if (c->ln->items && c->ln->items->text_node &&
2287 (c->ln->par->direction == EVAS_BIDI_DIRECTION_RTL))
2305 * Reorder the items in visual order
2307 * @param line the line to reorder
2310 _layout_line_reorder(Evas_Object_Textblock_Line *line)
2312 /*FIXME: do it a bit more efficient - not very efficient ATM. */
2313 Evas_Object_Textblock_Item *it;
2314 EvasBiDiStrIndex *v_to_l = NULL;
2319 if (line->items && line->items->text_node &&
2320 line->par->bidi_props)
2322 Evas_BiDi_Paragraph_Props *props;
2323 props = line->par->bidi_props;
2324 start = end = line->items->text_pos;
2326 /* Find the first and last positions in the line */
2328 EINA_INLIST_FOREACH(line->items, it)
2330 if (it->text_pos < start)
2332 start = it->text_pos;
2337 tlen = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
2338 _ITEM_TEXT(it)->text_props.text_len : 1;
2339 if (it->text_pos + tlen > end)
2341 end = it->text_pos + tlen;
2347 evas_bidi_props_reorder_line(NULL, start, len, props, &v_to_l);
2349 /* Update visual pos */
2351 Evas_Object_Textblock_Item *i;
2355 i->visual_pos = evas_bidi_position_logical_to_visual(
2356 v_to_l, len, i->text_pos - start);
2357 i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(i)->next;
2361 /*FIXME: not very efficient, sort the items arrays. Anyhow, should only
2362 * reorder if it's a bidi paragraph */
2364 Evas_Object_Textblock_Item *i, *j, *min;
2369 EINA_INLIST_FOREACH(i, j)
2371 if (j->visual_pos < min->visual_pos)
2378 line->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min));
2379 line->items = (Evas_Object_Textblock_Item *) eina_inlist_prepend_relative(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min), EINA_INLIST_GET(i));
2382 i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(min)->next;
2387 if (v_to_l) free(v_to_l);
2389 EINA_INLIST_FOREACH(line->items, it)
2399 _layout_calculate_format_item_size(const Evas_Object *obj,
2400 const Evas_Object_Textblock_Format_Item *fi,
2401 Evas_Coord *maxascent, Evas_Coord *maxdescent,
2402 Evas_Coord *_y, Evas_Coord *_w, Evas_Coord *_h)
2404 /* Adjust sizes according to current line height/scale */
2414 p = strstr(s, " size=");
2418 if (sscanf(p, "%ix%i", &w, &h) == 2)
2420 w = w * obj->cur.scale;
2421 h = h * obj->cur.scale;
2426 p = strstr((char *) s, " relsize=");
2428 if (sscanf(p, "%ix%i", &w, &h) == 2)
2431 if (fi->vsize == VSIZE_FULL)
2433 sz = *maxdescent + *maxascent;
2435 else if (fi->vsize == VSIZE_ASCENT)
2456 if (h > (*maxdescent + *maxascent))
2458 *maxascent += h - (*maxdescent + *maxascent);
2462 *_y = -(h - *maxdescent);
2498 * Order the items in the line, update it's properties and update it's
2499 * corresponding paragraph.
2501 * @param c the context to work on - Not NULL.
2502 * @param fmt the format to use.
2503 * @param add_line true if we should create a line, false otherwise.
2506 _layout_line_finalize(Ctxt *c, Evas_Object_Textblock_Format *fmt)
2508 Evas_Object_Textblock_Item *it;
2511 /* If there are no text items yet, calc ascent/descent
2512 * according to the current format. */
2513 if (c->maxascent + c->maxdescent == 0)
2514 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
2515 &c->maxdescent, fmt);
2517 /* Adjust all the item sizes according to the final line size,
2518 * and update the x positions of all the items of the line. */
2519 EINA_INLIST_FOREACH(c->ln->items, it)
2521 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
2523 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
2524 if (!fi->formatme) goto loop_advance;
2525 _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
2526 &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
2527 fi->parent.adv = fi->parent.w;
2534 if ((it->x + it->adv) > c->ln->w) c->ln->w = it->x + it->adv;
2537 c->ln->y = (c->y - c->par->y) + c->o->style_pad.t;
2538 c->ln->h = c->maxascent + c->maxdescent;
2539 c->ln->baseline = c->maxascent;
2540 if (c->have_underline2)
2542 if (c->maxdescent < 4) c->underline_extend = 4 - c->maxdescent;
2544 else if (c->have_underline)
2546 if (c->maxdescent < 2) c->underline_extend = 2 - c->maxdescent;
2548 c->ln->line_no = c->line_no - c->ln->par->line_no;
2550 c->y += c->maxascent + c->maxdescent;
2553 c->ln->x = c->marginl + c->o->style_pad.l +
2555 c->o->style_pad.l - c->o->style_pad.r -
2556 c->marginl - c->marginr) * _layout_line_align_get(c));
2560 c->ln->x = c->marginl + c->o->style_pad.l;
2563 c->par->h = c->ln->y + c->ln->h;
2564 if (c->ln->w > c->par->w)
2565 c->par->w = c->ln->w;
2568 Evas_Coord new_wmax = c->ln->w +
2569 c->marginl + c->marginr - (c->o->style_pad.l + c->o->style_pad.r);
2570 if (new_wmax > c->wmax)
2577 * Create a new line and append it to the lines in the context.
2579 * @param c the context to work on - Not NULL.
2580 * @param fmt the format to use.
2581 * @param add_line true if we should create a line, false otherwise.
2584 _layout_line_advance(Ctxt *c, Evas_Object_Textblock_Format *fmt)
2586 _layout_line_finalize(c, fmt);
2587 _layout_line_new(c, fmt);
2592 * Create a new text layout item from the string and the format.
2594 * @param c the context to work on - Not NULL.
2595 * @param fmt the format to use.
2596 * @param str the string to use.
2597 * @param len the length of the string.
2599 static Evas_Object_Textblock_Text_Item *
2600 _layout_text_item_new(Ctxt *c __UNUSED__, Evas_Object_Textblock_Format *fmt)
2602 Evas_Object_Textblock_Text_Item *ti;
2604 ti = calloc(1, sizeof(Evas_Object_Textblock_Text_Item));
2605 ti->parent.format = fmt;
2606 ti->parent.format->ref++;
2607 ti->parent.type = EVAS_TEXTBLOCK_ITEM_TEXT;
2613 * Return the cutoff of the text in the text item.
2615 * @param c the context to work on - Not NULL.
2616 * @param fmt the format to use. - Not NULL.
2617 * @param it the item to check - Not null.
2618 * @return -1 if there is no cutoff (either because there is really none,
2619 * or because of an error), cutoff index on success.
2622 _layout_text_cutoff_get(Ctxt *c, Evas_Object_Textblock_Format *fmt,
2623 const Evas_Object_Textblock_Text_Item *ti)
2628 x = c->w - c->o->style_pad.l - c->o->style_pad.r - c->marginl -
2629 c->marginr - c->x - ti->x_adjustment;
2632 return c->ENFN->font_last_up_to_pos(c->ENDT, fmt->font.font,
2633 &ti->text_props, x, 0);
2640 * Split before cut, and strip if str[cut - 1] is a whitespace.
2642 * @param c the context to work on - Not NULL.
2643 * @param ti the item to cut - not null.
2644 * @param lti the logical list item of the item.
2645 * @param cut the cut index.
2646 * @return the second (newly created) item.
2648 static Evas_Object_Textblock_Text_Item *
2649 _layout_item_text_split_strip_white(Ctxt *c,
2650 Evas_Object_Textblock_Text_Item *ti, Eina_List *lti, size_t cut)
2652 const Eina_Unicode *ts;
2653 Evas_Object_Textblock_Text_Item *new_ti = NULL, *white_ti = NULL;
2655 ts = GET_ITEM_TEXT(ti);
2657 if (!IS_AT_END(ti, cut) && (ti->text_props.text_len > 0))
2659 new_ti = _layout_text_item_new(c, ti->parent.format);
2660 new_ti->parent.text_node = ti->parent.text_node;
2661 new_ti->parent.text_pos = ti->parent.text_pos + cut;
2662 new_ti->parent.merge = EINA_TRUE;
2664 evas_common_text_props_split(&ti->text_props,
2665 &new_ti->text_props, cut);
2666 _layout_text_add_logical_item(c, new_ti, lti);
2669 /* Strip the previous white if needed */
2670 if ((cut >= 1) && _is_white(ts[cut - 1]) && (ti->text_props.text_len > 0))
2674 size_t white_cut = cut - 1;
2675 white_ti = _layout_text_item_new(c, ti->parent.format);
2676 white_ti->parent.text_node = ti->parent.text_node;
2677 white_ti->parent.text_pos = ti->parent.text_pos + white_cut;
2678 white_ti->parent.merge = EINA_TRUE;
2679 white_ti->parent.visually_deleted = EINA_TRUE;
2681 evas_common_text_props_split(&ti->text_props,
2682 &white_ti->text_props, white_cut);
2683 _layout_text_add_logical_item(c, white_ti, lti);
2687 /* Mark this one as the visually deleted. */
2688 ti->parent.visually_deleted = EINA_TRUE;
2692 if (new_ti || white_ti)
2694 _text_item_update_sizes(c, ti);
2701 * Merge item2 into item1 and free item2.
2703 * @param c the context to work on - Not NULL.
2704 * @param item1 the item to copy to
2705 * @param item2 the item to copy from
2708 _layout_item_merge_and_free(Ctxt *c,
2709 Evas_Object_Textblock_Text_Item *item1,
2710 Evas_Object_Textblock_Text_Item *item2)
2712 evas_common_text_props_merge(&item1->text_props,
2713 &item2->text_props);
2715 _text_item_update_sizes(c, item1);
2717 item1->parent.merge = EINA_FALSE;
2718 item1->parent.visually_deleted = EINA_FALSE;
2720 _item_free(c->obj, NULL, _ITEM(item2));
2725 * Calculates an item's size.
2727 * @param c the context
2728 * @param it the item itself.
2731 _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti)
2733 int tw, th, inset, advw;
2734 const Evas_Object_Textblock_Format *fmt = ti->parent.format;
2735 int shad_sz = 0, shad_dst = 0, out_sz = 0;
2736 int dx = 0, minx = 0, maxx = 0, shx1, shx2;
2740 c->ENFN->font_string_size_get(c->ENDT, fmt->font.font,
2741 &ti->text_props, &tw, &th);
2744 inset = c->ENFN->font_inset_get(c->ENDT, fmt->font.font,
2748 advw = c->ENFN->font_h_advance_get(c->ENDT, fmt->font.font,
2752 /* These adjustments are calculated and thus heavily linked to those in
2753 * textblock_render!!! Don't change one without the other. */
2755 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
2757 case EVAS_TEXT_STYLE_SHADOW:
2760 case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
2761 case EVAS_TEXT_STYLE_FAR_SHADOW:
2765 case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
2770 case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
2774 case EVAS_TEXT_STYLE_SOFT_SHADOW:
2778 case EVAS_TEXT_STYLE_GLOW:
2779 case EVAS_TEXT_STYLE_SOFT_OUTLINE:
2782 case EVAS_TEXT_STYLE_OUTLINE:
2788 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
2790 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
2791 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
2792 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
2795 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
2796 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
2797 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
2799 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
2800 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
2807 shx1 = dx * shad_dst;
2809 shx2 = dx * shad_dst;
2811 if (shx1 < minx) minx = shx1;
2812 if (shx2 > maxx) maxx = shx2;
2814 ti->x_adjustment = maxx - minx;
2817 ti->parent.w = tw + ti->x_adjustment;
2819 ti->parent.adv = advw;
2825 * Adds the item to the list, updates the item's properties (e.g, x,w,h)
2827 * @param c the context
2828 * @param it the item itself.
2829 * @param rel item ti will be appened after, NULL = last.
2832 _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti,
2835 _text_item_update_sizes(c, ti);
2837 c->par->logical_items = eina_list_append_relative_list(
2838 c->par->logical_items, ti, rel);
2843 * Appends the text from node n starting at start ending at off to the layout.
2844 * It uses the fmt for the formatting.
2846 * @param c the current context- NOT NULL.
2847 * @param fmt the format to use.
2848 * @param n the text node. - Not null.
2849 * @param start the start position. - in range.
2850 * @param off the offset - start + offset in range. if offset is -1, it'll add everything to the end of the string if offset = 0 it'll return with doing nothing.
2851 * @param repch a replacement char to print instead of the original string, for example, * when working with passwords.
2854 _layout_text_append(Ctxt *c, Evas_Object_Textblock_Format *fmt, Evas_Object_Textblock_Node_Text *n, int start, int off, const char *repch)
2856 const Eina_Unicode *str = EINA_UNICODE_EMPTY_STRING;
2857 const Eina_Unicode *tbase;
2858 Evas_Object_Textblock_Text_Item *ti;
2860 Eina_Unicode urepch = 0;
2862 /* prepare a working copy of the string, either filled by the repch or
2863 * filled with the true values */
2869 /* Figure out if we want to bail, work with an empty string,
2870 * or continue with a slice of the passed string */
2871 len = eina_ustrbuf_length_get(n->unicode);
2872 if (off == 0) return;
2873 else if (off < 0) off = len - start;
2879 else if ((start == 0) && (off == 0) && (orig_off == -1))
2881 /* Special case that means that we need to add an empty
2883 str = EINA_UNICODE_EMPTY_STRING;
2886 else if ((start >= len) || (start + off > len))
2891 /* If we work with a replacement char, create a string which is the same
2892 * but with replacement chars instead of regular chars. */
2893 if ((fmt->password) && (repch) && (eina_ustrbuf_length_get(n->unicode)))
2898 tbase = str = ptr = alloca((off + 1) * sizeof(Eina_Unicode));
2900 urepch = eina_unicode_utf8_get_next(repch, &ind);
2901 for (i = 0 ; i < off; ptr++, i++)
2905 /* Use the string, just cut the relevant parts */
2908 str = eina_ustrbuf_string_get(n->unicode) + start;
2917 /* If there's no parent text node, only create an empty item */
2920 ti = _layout_text_item_new(c, fmt);
2921 ti->parent.text_node = NULL;
2922 ti->parent.text_pos = 0;
2923 _layout_text_add_logical_item(c, ti, NULL);
2930 Evas_Font_Instance *script_fi = NULL;
2931 int script_len, tmp_cut;
2932 Evas_Script_Type script;
2934 script_len = cur_len;
2936 tmp_cut = evas_common_language_script_end_of_run_get(str,
2937 c->par->bidi_props, start + str - tbase, script_len);
2940 script_len = tmp_cut;
2942 cur_len -= script_len;
2944 script = evas_common_language_script_type_get(str, script_len);
2947 while (script_len > 0)
2949 Evas_Font_Instance *cur_fi = NULL;
2950 int run_len = script_len;
2951 ti = _layout_text_item_new(c, fmt);
2952 ti->parent.text_node = n;
2953 ti->parent.text_pos = start + str - tbase;
2955 if (ti->parent.format->font.font)
2957 run_len = c->ENFN->font_run_end_get(c->ENDT,
2958 ti->parent.format->font.font, &script_fi, &cur_fi,
2959 script, str, script_len);
2962 evas_common_text_props_bidi_set(&ti->text_props,
2963 c->par->bidi_props, ti->parent.text_pos);
2964 evas_common_text_props_script_set(&ti->text_props, script);
2968 c->ENFN->font_text_props_info_create(c->ENDT,
2969 cur_fi, str, &ti->text_props, c->par->bidi_props,
2970 ti->parent.text_pos, run_len);
2973 script_len -= run_len;
2975 _layout_text_add_logical_item(c, ti, NULL);
2982 * Add a format item from the format node n and the item item.
2984 * @param c the current context- NOT NULL.
2985 * @param n the source format node - not null.
2986 * @param item the format text.
2988 * @return the new format item.
2990 static Evas_Object_Textblock_Format_Item *
2991 _layout_format_item_add(Ctxt *c, Evas_Object_Textblock_Node_Format *n, const char *item, Evas_Object_Textblock_Format *fmt)
2993 Evas_Object_Textblock_Format_Item *fi;
2995 fi = calloc(1, sizeof(Evas_Object_Textblock_Format_Item));
2996 fi->item = eina_stringshare_add(item);
2997 fi->parent.type = EVAS_TEXTBLOCK_ITEM_FORMAT;
2998 fi->parent.format = fmt;
2999 fi->parent.format->ref++;
3000 c->par->logical_items = eina_list_append(c->par->logical_items, fi);
3003 fi->parent.text_node = n->text_node;
3004 /* FIXME: make it more efficient */
3005 fi->parent.text_pos = _evas_textblock_node_format_pos_get(n);
3007 fi->bidi_dir = (evas_bidi_is_rtl_char(
3010 fi->parent.text_pos)) ?
3011 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
3013 fi->bidi_dir = EVAS_BIDI_DIRECTION_LTR;
3021 * Should be call after we finish filling a format.
3025 _format_finalize(Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
3029 of = fmt->font.font;
3031 fmt->font.font = evas_font_load(obj->layer->evas, fmt->font.fdesc,
3032 fmt->font.source, (int)(((double) fmt->font.size) * obj->cur.scale));
3033 if (of) evas_font_free(obj->layer->evas, of);
3038 * Returns true if the item is a tab
3039 * @def _IS_TAB(item)
3041 #define _IS_TAB(item) \
3042 (!strcmp(item, "tab") || !strcmp(item, "\t") || !strcmp(item, "\\t"))
3045 * Returns true if the item is a line spearator, false otherwise
3046 * @def _IS_LINE_SEPARATOR(item)
3048 #define _IS_LINE_SEPARATOR(item) \
3049 (!strcmp(item, "br") || !strcmp(item, "\n") || !strcmp(item, "\\n"))
3052 * Returns true if the item is a paragraph separator, false otherwise
3053 * @def _IS_PARAGRAPH_SEPARATOR(item)
3055 #define _IS_PARAGRAPH_SEPARATOR_SIMPLE(item) \
3056 (!strcmp(item, "ps"))
3059 * Returns true if the item is a paragraph separator, false otherwise
3060 * takes legacy mode into account.
3061 * @def _IS_PARAGRAPH_SEPARATOR(item)
3063 #define _IS_PARAGRAPH_SEPARATOR(o, item) \
3064 (_IS_PARAGRAPH_SEPARATOR_SIMPLE(item) || \
3065 (o->legacy_newline && _IS_LINE_SEPARATOR(item))) /* Paragraph separator */
3069 * Handles a format by processing a format node. It returns the relevant format
3070 * through _fmt and updates the padding through style_pad_*. If needed,
3071 * it creates a format item.
3073 * @param obj the evas object - NOT NULL.
3074 * @param c the current context- NOT NULL.
3075 * @param _fmt the format that holds the result.
3076 * @param n the source format node - not null.
3077 * @param style_pad_l the pad to update.
3078 * @param style_pad_r the pad to update.
3079 * @param style_pad_t the pad to update.
3080 * @param style_pad_b the pad to update.
3081 * @param create_item Create a new format item if true, only process otherwise.
3084 _layout_do_format(const Evas_Object *obj __UNUSED__, Ctxt *c,
3085 Evas_Object_Textblock_Format **_fmt, Evas_Object_Textblock_Node_Format *n,
3086 int *style_pad_l, int *style_pad_r, int *style_pad_t, int *style_pad_b,
3087 Eina_Bool create_item)
3089 Evas_Object_Textblock_Format *fmt = *_fmt;
3090 /* FIXME: comment the algo */
3097 if (!strncmp(s, "item ", 5))
3100 // item size=20x10 href=name
3101 // item relsize=20x10 href=name
3102 // item abssize=20x10 href=name
3104 // optional arguments:
3108 // size == item size (modifies line size) - can be multiplied by
3110 // relsize == relative size (height is current font height, width
3111 // modified accordingly keeping aspect)
3112 // abssize == absolute size (modifies line size) - never mulitplied by
3114 // href == name of item - to be found and matched later and used for
3116 Evas_Object_Textblock_Format_Item *fi;
3118 int vsize = 0, size = 0;
3122 //href = strstr(s, " href=");
3123 p = strstr(s, " vsize=");
3127 if (!strncmp(p, "full", 4)) vsize = VSIZE_FULL;
3128 else if (!strncmp(p, "ascent", 6)) vsize = VSIZE_ASCENT;
3130 p = strstr(s, " size=");
3134 if (sscanf(p, "%ix%i", &w, &h) == 2)
3136 /* this is handled somewhere else because it depends
3137 * on the current scaling factor of the object which
3138 * may change and break because the results of this
3139 * function are cached */
3145 p = strstr(s, " absize=");
3149 if (sscanf(p, "%ix%i", &w, &h) == 2)
3156 p = strstr(s, " relsize=");
3159 /* this is handled somewhere else because it depends
3160 * on the line it resides in, which is not defined
3161 * at this point and will change anyway, which will
3162 * break because the results of this function are
3171 fi = _layout_format_item_add(c, n, s, fmt);
3175 /* For formats items it's usually
3176 the same, we don't handle the
3177 special cases yet. */
3178 fi->parent.w = fi->parent.adv = w;
3181 /* Not sure if it's the best handling, but will do it for now. */
3182 fmt = _layout_format_push(c, fmt, n);
3188 Eina_Bool push_fmt = EINA_FALSE;
3189 if (n->opener && !n->own_closer)
3191 fmt = _layout_format_push(c, fmt, n);
3192 push_fmt = EINA_TRUE;
3194 else if (!n->opener)
3196 fmt = _layout_format_pop(c, n->orig_format);
3198 while ((item = _format_parse(&s)))
3200 if (_format_is_param(item))
3202 /* Only handle it if it's a push format, otherwise,
3203 * don't let overwrite the format stack.. */
3206 _layout_format_value_handle(c, fmt, item);
3209 else if (create_item)
3211 if ((_IS_PARAGRAPH_SEPARATOR(c->o, item)) ||
3212 (_IS_LINE_SEPARATOR(item)))
3214 Evas_Object_Textblock_Format_Item *fi;
3216 fi = _layout_format_item_add(c, n, item, fmt);
3218 fi->parent.w = fi->parent.adv = 0;
3220 else if (_IS_TAB(item))
3222 Evas_Object_Textblock_Format_Item *fi;
3224 fi = _layout_format_item_add(c, n, item, fmt);
3225 fi->parent.w = fi->parent.adv = fmt->tabstops;
3230 _format_finalize(c->obj, fmt);
3234 Evas_Coord pad_l, pad_r, pad_t, pad_b;
3235 pad_l = pad_r = pad_t = pad_b = 0;
3236 evas_text_style_pad_get(fmt->style, &pad_l, &pad_r, &pad_t, &pad_b);
3237 if (pad_l > *style_pad_l) *style_pad_l = pad_l;
3238 if (pad_r > *style_pad_r) *style_pad_r = pad_r;
3239 if (pad_t > *style_pad_t) *style_pad_t = pad_t;
3240 if (pad_b > *style_pad_b) *style_pad_b = pad_b;
3243 if (fmt->underline2)
3244 c->have_underline2 = 1;
3245 else if (fmt->underline || fmt->underline_dash)
3246 c->have_underline = 1;
3251 _layout_update_par(Ctxt *c)
3253 Evas_Object_Textblock_Paragraph *last_par;
3254 last_par = (Evas_Object_Textblock_Paragraph *)
3255 EINA_INLIST_GET(c->par)->prev;
3258 c->par->y = last_par->y + last_par->h;
3266 /* -1 means no wrap */
3268 _layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3269 const Evas_Object_Textblock_Item *it, size_t line_start,
3274 size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
3275 /* Currently not being used, because it doesn't contain relevant
3280 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3283 wrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
3287 uwrap = (size_t) wrap + it->text_pos;
3291 if ((uwrap == line_start) && (it->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3293 uwrap = it->text_pos +
3294 (size_t) evas_common_text_props_cluster_next(
3295 &_ITEM_TEXT(it)->text_props, wrap);
3297 if ((uwrap <= line_start) || (uwrap > len))
3303 /* -1 means no wrap */
3304 #ifdef HAVE_LINEBREAK
3306 /* Allow break means: if we can break after the current char */
3307 #define ALLOW_BREAK(i) \
3308 (breaks[i] <= LINEBREAK_ALLOWBREAK)
3312 #define ALLOW_BREAK(i) \
3317 _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3318 const Evas_Object_Textblock_Item *it, Eina_Bool mixed_wrap,
3319 size_t line_start, const char *breaks)
3321 Eina_Bool wrap_after = EINA_FALSE;
3324 const Eina_Unicode *str = eina_ustrbuf_string_get(
3325 it->text_node->unicode);
3326 int item_start = it->text_pos;
3327 size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
3328 #ifndef HAVE_LINEBREAK
3329 /* Not used without liblinebreak ATM. */
3335 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3338 swrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
3339 /* Avoiding too small textblocks to even contain one char.
3340 * FIXME: This can cause breaking inside ligatures. */
3345 orig_wrap = wrap = swrap + item_start;
3348 if (wrap > line_start)
3350 /* The wrapping point found is the first char of the next string
3351 the rest works on the last char of the previous string.
3352 If it's a whitespace, then it's ok, and no need to go back
3353 because we'll remove it anyway. */
3354 if (!_is_white(str[wrap]))
3355 MOVE_PREV_UNTIL(line_start, wrap);
3356 /* If there's a breakable point inside the text, scan backwards until
3358 while (wrap > line_start)
3360 if (ALLOW_BREAK(wrap))
3365 if ((wrap > line_start) ||
3366 ((wrap == line_start) && (ALLOW_BREAK(wrap)) && (wrap < len)))
3368 /* We found a suitable wrapping point, break here. */
3369 MOVE_NEXT_UNTIL(len, wrap);
3376 return ((orig_wrap >= line_start) && (orig_wrap < len)) ?
3377 ((int) orig_wrap) : -1;
3381 /* Scan forward to find the next wrapping point */
3383 wrap_after = EINA_TRUE;
3388 /* If we need to find the position after the cutting point */
3389 if ((wrap == line_start) || (wrap_after))
3393 return _layout_get_charwrap(c, fmt, it,
3394 line_start, breaks);
3400 if (ALLOW_BREAK(wrap))
3406 if ((wrap < len) && (wrap > line_start))
3408 MOVE_NEXT_UNTIL(len, wrap);
3421 /* -1 means no wrap */
3423 _layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3424 const Evas_Object_Textblock_Item *it, size_t line_start,
3427 return _layout_get_word_mixwrap_common(c, fmt, it, EINA_FALSE, line_start,
3431 /* -1 means no wrap */
3433 _layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3434 const Evas_Object_Textblock_Item *it, size_t line_start,
3437 return _layout_get_word_mixwrap_common(c, fmt, it, EINA_TRUE, line_start,
3441 /* Should be moved inside _layout_ellipsis_item_new once we fix the hack in
3442 * textblock render */
3443 static const Eina_Unicode _ellip_str[2] = { 0x2026, '\0' };
3445 static Evas_Object_Textblock_Text_Item *
3446 _layout_ellipsis_item_new(Ctxt *c, const Evas_Object_Textblock_Item *cur_it)
3448 Evas_Object_Textblock_Text_Item *ellip_ti;
3449 Evas_Script_Type script;
3450 Evas_Font_Instance *script_fi = NULL, *cur_fi;
3451 size_t len = 1; /* The length of _ellip_str */
3453 /* We can free it here, cause there's only one ellipsis item per tb. */
3454 if (c->o->ellip_ti) _item_free(c->obj, NULL, _ITEM(c->o->ellip_ti));
3455 c->o->ellip_ti = ellip_ti = _layout_text_item_new(c,
3456 eina_list_data_get(eina_list_last(c->format_stack)));
3457 ellip_ti->parent.text_node = cur_it->text_node;
3458 ellip_ti->parent.text_pos = cur_it->text_pos;
3459 script = evas_common_language_script_type_get(_ellip_str, len);
3461 evas_common_text_props_bidi_set(&ellip_ti->text_props,
3462 c->par->bidi_props, ellip_ti->parent.text_pos);
3463 evas_common_text_props_script_set (&ellip_ti->text_props, script);
3465 if (ellip_ti->parent.format->font.font)
3467 /* It's only 1 char anyway, we don't need the run end. */
3468 (void) c->ENFN->font_run_end_get(c->ENDT,
3469 ellip_ti->parent.format->font.font, &script_fi, &cur_fi,
3470 script, _ellip_str, len);
3472 c->ENFN->font_text_props_info_create(c->ENDT,
3473 cur_fi, _ellip_str, &ellip_ti->text_props,
3474 c->par->bidi_props, ellip_ti->parent.text_pos, len);
3477 _text_item_update_sizes(c, ellip_ti);
3479 if (cur_it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3481 ellip_ti->parent.text_pos += _ITEM_TEXT(cur_it)->text_props.text_len
3486 ellip_ti->parent.text_pos++;
3497 _layout_handle_ellipsis(Ctxt *c, Evas_Object_Textblock_Item *it, Eina_List *i)
3499 Evas_Object_Textblock_Text_Item *ellip_ti;
3500 Evas_Object_Textblock_Item *last_it;
3503 ellip_ti = _layout_ellipsis_item_new(c, it);
3507 c->w -= ellip_ti->parent.w;
3509 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3511 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3513 wrap = _layout_text_cutoff_get(c, last_it->format, ti);
3514 if ((wrap > 0) && !IS_AT_END(ti, (size_t) wrap))
3516 _layout_item_text_split_strip_white(c, ti, i, wrap);
3518 else if ((wrap == 0) && (c->ln->items))
3520 last_it = _ITEM(EINA_INLIST_GET(c->ln->items)->last);
3523 else if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3525 /* We don't want to add this format item. */
3530 c->w += ellip_ti->parent.w;
3531 /* If we should add this item, do it */
3534 c->ln->items = (Evas_Object_Textblock_Item *)
3535 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3536 EINA_INLIST_GET(it));
3537 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3539 Evas_Object_Textblock_Format_Item *fi;
3540 fi = _ITEM_FORMAT(it);
3544 c->ln->items = (Evas_Object_Textblock_Item *)
3545 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3546 EINA_INLIST_GET(_ITEM(ellip_ti)));
3547 _layout_line_finalize(c, ellip_ti->parent.format);
3552 _layout_paragraph_reorder_lines(Evas_Object_Textblock_Paragraph *par)
3554 Evas_Object_Textblock_Line *ln;
3556 EINA_INLIST_FOREACH(EINA_INLIST_GET(par->lines), ln)
3558 _layout_line_reorder(ln);
3564 _layout_paragraph_render(Evas_Object_Textblock *o,
3565 Evas_Object_Textblock_Paragraph *par)
3569 par->rendered = EINA_TRUE;
3574 _layout_update_bidi_props(o, par);
3575 _layout_paragraph_reorder_lines(par);
3576 /* Clear the bidi props because we don't need them anymore. */
3577 if (par->bidi_props)
3579 evas_bidi_paragraph_props_unref(par->bidi_props);
3580 par->bidi_props = NULL;
3588 /* 0 means go ahead, 1 means break without an error, 2 means
3589 * break with an error, should probably clean this a bit (enum/macro)
3592 _layout_par(Ctxt *c)
3594 Evas_Object_Textblock_Item *it;
3598 char *line_breaks = NULL;
3600 if (!c->par->logical_items)
3603 /* We want to show it. */
3604 c->par->visible = 1;
3606 /* Check if we need to skip this paragraph because it's already layouted
3607 * correctly, and mark handled nodes as dirty. */
3608 c->par->line_no = c->line_no;
3610 if (c->par->text_node)
3612 /* Skip this paragraph if width is the same, there is no ellipsis
3613 * and we aren't just calculating. */
3614 if (!c->par->text_node->is_new && !c->par->text_node->dirty &&
3615 !c->width_changed && c->par->lines &&
3616 !c->o->have_ellipsis)
3618 Evas_Object_Textblock_Line *ln;
3619 /* Update c->line_no */
3620 ln = (Evas_Object_Textblock_Line *)
3621 EINA_INLIST_GET(c->par->lines)->last;
3623 c->line_no = c->par->line_no + ln->line_no + 1;
3626 c->par->text_node->dirty = EINA_FALSE;
3627 c->par->text_node->is_new = EINA_FALSE;
3628 c->par->rendered = EINA_FALSE;
3630 /* Merge back and clear the paragraph */
3632 Eina_List *itr, *itr_next;
3633 Evas_Object_Textblock_Item *ititr, *prev_it = NULL;
3634 _paragraph_clear(c->obj, c->par);
3635 EINA_LIST_FOREACH_SAFE(c->par->logical_items, itr, itr_next, ititr)
3637 if (ititr->merge && prev_it &&
3638 (prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
3639 (ititr->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3641 _layout_item_merge_and_free(c, _ITEM_TEXT(prev_it),
3643 c->par->logical_items =
3644 eina_list_remove_list(c->par->logical_items, itr);
3656 it = _ITEM(eina_list_data_get(c->par->logical_items));
3657 _layout_line_new(c, it->format);
3658 /* We walk on our own because we want to be able to add items from
3659 * inside the list and then walk them on the next iteration. */
3660 for (i = c->par->logical_items ; i ; )
3664 it = _ITEM(eina_list_data_get(i));
3665 /* Skip visually deleted items */
3666 if (it->visually_deleted)
3668 i = eina_list_next(i);
3672 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3674 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3675 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3676 &c->maxdescent, ti->parent.format);
3680 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
3683 /* If there are no text items yet, calc ascent/descent
3684 * according to the current format. */
3685 if (c->maxascent + c->maxdescent == 0)
3686 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3687 &c->maxdescent, it->format);
3689 _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
3690 &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
3691 fi->parent.adv = fi->parent.w;
3696 /* Check if we need to wrap, i.e the text is bigger than the width,
3697 or we already found a wrap point. */
3699 (((c->x + it->adv) >
3700 (c->w - c->o->style_pad.l - c->o->style_pad.r -
3701 c->marginl - c->marginr)) || (wrap > 0)))
3703 /* Handle ellipsis here. If we don't have more width left
3704 * and no height left, or no more width left and no wrapping. */
3705 if ((it->format->ellipsis == 1.0) && (c->h >= 0) &&
3706 ((2 * it->h + c->y >
3707 c->h - c->o->style_pad.t - c->o->style_pad.b) ||
3708 (!it->format->wrap_word && !it->format->wrap_char &&
3709 !it->format->wrap_mixed)))
3711 _layout_handle_ellipsis(c, it, i);
3715 /* If we want to wrap and it's worth checking for wrapping
3716 * (i.e there's actually text). */
3717 else if ((it->format->wrap_word || it->format->wrap_char ||
3718 it->format->wrap_mixed) && it->text_node)
3723 it_len = (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) ?
3724 1 : _ITEM_TEXT(it)->text_props.text_len;
3727 #ifdef HAVE_LINEBREAK
3728 /* If we haven't calculated the linebreaks yet,
3732 /* Only relevant in those cases */
3733 if (it->format->wrap_word || it->format->wrap_mixed)
3736 lang = (it->format->font.fdesc) ?
3737 it->format->font.fdesc->lang : "";
3739 eina_ustrbuf_length_get(
3740 it->text_node->unicode);
3741 line_breaks = malloc(len);
3742 set_linebreaks_utf32((const utf32_t *)
3743 eina_ustrbuf_string_get(
3744 it->text_node->unicode),
3745 len, lang, line_breaks);
3750 line_start = c->ln->items->text_pos;
3752 line_start = it->text_pos;
3755 /* If we don't already have a wrap point from before */
3758 if (it->format->wrap_word)
3759 wrap = _layout_get_wordwrap(c, it->format, it,
3760 line_start, line_breaks);
3761 else if (it->format->wrap_char)
3762 wrap = _layout_get_charwrap(c, it->format, it,
3763 line_start, line_breaks);
3764 else if (it->format->wrap_mixed)
3765 wrap = _layout_get_mixedwrap(c, it->format, it,
3766 line_start, line_breaks);
3771 /* If it's before the item, rollback and apply.
3772 if it's in the item, cut.
3773 If it's after the item, delay the cut */
3776 size_t uwrap = (size_t) wrap;
3777 if (uwrap < it->text_pos)
3779 /* Rollback latest additions, and cut that
3781 i = eina_list_prev(i);
3782 it = eina_list_data_get(i);
3783 while (uwrap < it->text_pos)
3785 c->ln->items = _ITEM(
3787 EINA_INLIST_GET(c->ln->items),
3788 EINA_INLIST_GET(it)));
3789 i = eina_list_prev(i);
3790 it = eina_list_data_get(i);
3793 c->ln->items = _ITEM(
3795 EINA_INLIST_GET(c->ln->items),
3796 EINA_INLIST_GET(it)));
3799 /* If it points to the end, it means the previous
3800 * char is a whitespace we should remove, so this
3801 * is a wanted cutting point. */
3802 else if (uwrap > it->text_pos + it_len)
3804 /* FIXME: Should redo the ellipsis handling.
3805 * If we can do ellipsis, just cut here. */
3806 if (it->format->ellipsis == 1.0)
3808 _layout_handle_ellipsis(c, it, i);
3814 /* Delay the cut in a smart way i.e use the
3815 item_pos as the line_start, because
3816 there's already no cut before*/
3821 wrap -= it->text_pos; /* Cut here */
3826 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3828 _layout_item_text_split_strip_white(c,
3829 _ITEM_TEXT(it), i, wrap);
3834 /* Should wrap before the item */
3837 _layout_line_advance(c, it->format);
3844 if (!redo_item && !it->visually_deleted)
3846 c->ln->items = (Evas_Object_Textblock_Item *)
3847 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3848 EINA_INLIST_GET(it));
3849 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3851 Evas_Object_Textblock_Format_Item *fi;
3852 fi = _ITEM_FORMAT(it);
3854 /* If it's a newline, and we are not in newline compat
3855 * mode, or we are in newline compat mode, and this is
3856 * not used as a paragraph separator, advance */
3857 if (fi->item && _IS_LINE_SEPARATOR(fi->item) &&
3858 (!c->o->legacy_newline ||
3865 i = eina_list_next(i);
3869 /* Each line is according to the first item in it, and here
3870 * i is already the next item (or the current if we redo it) */
3873 it = _ITEM(eina_list_data_get(i));
3875 _layout_line_advance(c, it->format);
3880 /* Here 'it' is the last format used */
3881 _layout_line_finalize(c, it->format);
3885 #ifdef HAVE_LINEBREAK
3895 * Invalidate text nodes according to format changes
3896 * This goes through all the new format changes and marks the text nodes
3897 * that should be invalidated because of format changes.
3899 * @param c the working context.
3902 _format_changes_invalidate_text_nodes(Ctxt *c)
3904 Evas_Object_Textblock_Node_Format *fnode = c->o->format_nodes;
3905 Evas_Object_Textblock_Node_Text *start_n = NULL;
3906 Eina_List *fstack = NULL;
3912 const char *fstr = fnode->orig_format;
3913 /* balance < 0 means we gave up and everything should be
3915 if (fnode->opener && !fnode->own_closer)
3919 start_n = fnode->text_node;
3920 fstack = eina_list_prepend(fstack, fnode);
3922 else if (!fnode->opener)
3925 fstr_len = strlen(fstr);
3926 /* Generic popper, just pop */
3927 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
3929 fstack = eina_list_remove_list(fstack, fstack);
3932 /* Find the matching format and pop it, if the matching format
3933 * is out format, i.e the last one, pop and break. */
3937 Evas_Object_Textblock_Node_Format *fnode2;
3938 EINA_LIST_FOREACH(fstack, i, fnode2)
3940 if (_FORMAT_IS_CLOSER_OF(
3941 fnode2->orig_format, fstr, fstr_len))
3943 fstack = eina_list_remove_list(fstack, i);
3952 Evas_Object_Textblock_Node_Text *f_tnode =
3956 start_n->dirty = EINA_TRUE;
3957 if (start_n == f_tnode)
3960 _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3965 else if (!fnode->visible)
3970 /* if we don't already have a starting point, use the
3971 * current paragraph. */
3973 start_n = fnode->text_node;
3977 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
3984 start_n->dirty = EINA_TRUE;
3985 start_n = _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3991 /** FIXME: Document */
3993 _layout_pre(Ctxt *c, int *style_pad_l, int *style_pad_r, int *style_pad_t,
3996 Evas_Object *obj = c->obj;
3997 Evas_Object_Textblock *o = c->o;
3998 /* Mark text nodes as dirty if format have changed. */
3999 if (c->o->format_changed)
4001 _format_changes_invalidate_text_nodes(c);
4004 if (o->content_changed)
4006 Evas_Object_Textblock_Node_Text *n;
4007 c->o->have_ellipsis = 0;
4008 c->par = c->paragraphs = o->paragraphs;
4009 /* Go through all the text nodes to create the logical layout */
4010 EINA_INLIST_FOREACH(c->o->text_nodes, n)
4012 Evas_Object_Textblock_Node_Format *fnode;
4016 /* If it's not a new paragraph, either update it or skip it.
4017 * Remove all the paragraphs that were deleted */
4020 /* Remove all the deleted paragraphs at this point */
4021 while (c->par->text_node != n)
4023 Evas_Object_Textblock_Paragraph *tmp_par =
4024 (Evas_Object_Textblock_Paragraph *)
4025 EINA_INLIST_GET(c->par)->next;
4027 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4028 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4029 EINA_INLIST_GET(c->par));
4030 _paragraph_free(obj, c->par);
4035 /* If it's dirty, remove and recreate, if it's clean,
4036 * skip to the next. */
4039 Evas_Object_Textblock_Paragraph *prev_par = c->par;
4041 _layout_paragraph_new(c, n, EINA_TRUE);
4043 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4044 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4045 EINA_INLIST_GET(prev_par));
4046 _paragraph_free(obj, prev_par);
4050 c->par = (Evas_Object_Textblock_Paragraph *)
4051 EINA_INLIST_GET(c->par)->next;
4053 /* Update the format stack according to the node's
4055 fnode = n->format_node;
4056 while (fnode && (fnode->text_node == n))
4058 /* Only do this if this actually changes format */
4059 if (fnode->format_change)
4060 _layout_do_format(obj, c, &c->fmt, fnode,
4061 style_pad_l, style_pad_r,
4062 style_pad_t, style_pad_b, EINA_FALSE);
4063 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4070 /* If it's a new paragraph, just add it. */
4071 _layout_paragraph_new(c, n, EINA_FALSE);
4075 _layout_update_bidi_props(c->o, c->par);
4078 /* For each text node to thorugh all of it's format nodes
4079 * append text from the start to the offset of the next format
4080 * using the last format got. if needed it also creates format
4081 * items this is the core algorithm of the layout mechanism.
4082 * Skip the unicode replacement chars when there are because
4083 * we don't want to print them. */
4084 fnode = n->format_node;
4086 while (fnode && (fnode->text_node == n))
4088 off += fnode->offset;
4089 /* No need to skip on the first run, or a non-visible one */
4090 _layout_text_append(c, c->fmt, n, start, off, o->repch);
4091 _layout_do_format(obj, c, &c->fmt, fnode, style_pad_l,
4092 style_pad_r, style_pad_t, style_pad_b, EINA_TRUE);
4093 if ((c->have_underline2) || (c->have_underline))
4095 if (*style_pad_b < c->underline_extend)
4096 *style_pad_b = c->underline_extend;
4097 c->have_underline = 0;
4098 c->have_underline2 = 0;
4099 c->underline_extend = 0;
4111 fnode->is_new = EINA_FALSE;
4112 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4114 _layout_text_append(c, c->fmt, n, start, -1, o->repch);
4116 /* Clear the bidi props because we don't need them anymore. */
4117 if (c->par->bidi_props)
4119 evas_bidi_paragraph_props_unref(c->par->bidi_props);
4120 c->par->bidi_props = NULL;
4123 c->par = (Evas_Object_Textblock_Paragraph *)
4124 EINA_INLIST_GET(c->par)->next;
4127 /* Delete the rest of the layout paragraphs */
4130 Evas_Object_Textblock_Paragraph *tmp_par =
4131 (Evas_Object_Textblock_Paragraph *)
4132 EINA_INLIST_GET(c->par)->next;
4134 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4135 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4136 EINA_INLIST_GET(c->par));
4137 _paragraph_free(obj, c->par);
4141 o->paragraphs = c->paragraphs;
4149 * Create the layout from the nodes.
4151 * @param obj the evas object - NOT NULL.
4152 * @param calc_only true if should only calc sizes false if should also create the layout.. It assumes native size is being calculated, doesn't support formatted size atm.
4153 * @param w the object's w, -1 means no wrapping (i.e infinite size)
4154 * @param h the object's h, -1 means inifinte size.
4155 * @param w_ret the object's calculated w.
4156 * @param h_ret the object's calculated h.
4159 _layout(const Evas_Object *obj, int w, int h, int *w_ret, int *h_ret)
4161 Evas_Object_Textblock *o;
4163 int style_pad_l = 0, style_pad_r = 0, style_pad_t = 0, style_pad_b = 0;
4166 o = (Evas_Object_Textblock *)(obj->object_data);
4168 c->obj = (Evas_Object *)obj;
4170 c->paragraphs = c->par = NULL;
4171 c->format_stack = NULL;
4176 c->wmax = c->hmax = 0;
4177 c->maxascent = c->maxdescent = 0;
4178 c->marginl = c->marginr = 0;
4179 c->have_underline = 0;
4180 c->have_underline2 = 0;
4181 c->underline_extend = 0;
4184 c->align_auto = EINA_TRUE;
4186 c->width_changed = (obj->cur.geometry.w != o->last_w);
4188 /* Start of logical layout creation */
4189 /* setup default base style */
4191 Eina_Bool finalize = EINA_FALSE;
4192 if ((c->o->style) && (c->o->style->default_tag))
4194 c->fmt = _layout_format_push(c, NULL, NULL);
4195 _format_fill(c->obj, c->fmt, c->o->style->default_tag);
4196 finalize = EINA_TRUE;
4199 if ((c->o->style_user) && (c->o->style_user->default_tag))
4203 c->fmt = _layout_format_push(c, NULL, NULL);
4205 _format_fill(c->obj, c->fmt, c->o->style_user->default_tag);
4206 finalize = EINA_TRUE;
4210 _format_finalize(c->obj, c->fmt);
4214 if (w_ret) *w_ret = 0;
4215 if (h_ret) *h_ret = 0;
4219 _layout_pre(c, &style_pad_l, &style_pad_r, &style_pad_t, &style_pad_b);
4220 c->paragraphs = o->paragraphs;
4222 /* If there are no paragraphs, create the minimum needed,
4223 * if the last paragraph has no lines/text, create that as well */
4226 _layout_paragraph_new(c, NULL, EINA_TRUE);
4227 o->paragraphs = c->paragraphs;
4229 c->par = (Evas_Object_Textblock_Paragraph *)
4230 EINA_INLIST_GET(c->paragraphs)->last;
4231 if (!c->par->logical_items)
4233 Evas_Object_Textblock_Text_Item *ti;
4234 ti = _layout_text_item_new(c, c->fmt);
4235 ti->parent.text_node = c->par->text_node;
4236 ti->parent.text_pos = 0;
4237 _layout_text_add_logical_item(c, ti, NULL);
4240 /* End of logical layout creation */
4242 /* Start of visual layout creation */
4244 Evas_Object_Textblock_Paragraph *last_vis_par = NULL;
4245 int par_index_step = o->num_paragraphs / TEXTBLOCK_PAR_INDEX_SIZE;
4246 int par_count = 1; /* Force it to take the first one */
4247 int par_index_pos = 0;
4249 if (par_index_step == 0) par_index_step = 1;
4251 /* Clear all of the index */
4252 memset(o->par_index, 0, sizeof(o->par_index));
4254 EINA_INLIST_FOREACH(c->paragraphs, c->par)
4256 _layout_update_par(c);
4258 /* Break if we should stop here. */
4261 last_vis_par = c->par;
4265 if ((par_index_pos < TEXTBLOCK_PAR_INDEX_SIZE) && (--par_count == 0))
4267 par_count = par_index_step;
4269 o->par_index[par_index_pos++] = c->par;
4273 /* Mark all the rest of the paragraphs as invisible */
4276 c->par = (Evas_Object_Textblock_Paragraph *)
4277 EINA_INLIST_GET(c->par)->next;
4280 c->par->visible = 0;
4281 c->par = (Evas_Object_Textblock_Paragraph *)
4282 EINA_INLIST_GET(c->par)->next;
4286 /* Get the last visible paragraph in the layout */
4287 if (!last_vis_par && c->paragraphs)
4288 last_vis_par = (Evas_Object_Textblock_Paragraph *)
4289 EINA_INLIST_GET(c->paragraphs)->last;
4292 c->hmax = last_vis_par->y + last_vis_par->h;
4295 /* Clean the rest of the format stack */
4296 while (c->format_stack)
4298 c->fmt = c->format_stack->data;
4299 c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
4300 _format_unref_free(c->obj, c->fmt);
4303 if (w_ret) *w_ret = c->wmax;
4304 if (h_ret) *h_ret = c->hmax;
4306 /* Vertically align the textblock */
4307 if ((o->valign > 0.0) && (c->h > c->hmax))
4309 Evas_Coord adjustment = (c->h - c->hmax) * o->valign;
4310 Evas_Object_Textblock_Paragraph *par;
4311 EINA_INLIST_FOREACH(c->paragraphs, par)
4313 par->y += adjustment;
4317 if ((o->style_pad.l != style_pad_l) || (o->style_pad.r != style_pad_r) ||
4318 (o->style_pad.t != style_pad_t) || (o->style_pad.b != style_pad_b))
4320 o->style_pad.l = style_pad_l;
4321 o->style_pad.r = style_pad_r;
4322 o->style_pad.t = style_pad_t;
4323 o->style_pad.b = style_pad_b;
4324 _paragraphs_clear(obj, c->paragraphs);
4325 _layout(obj, w, h, w_ret, h_ret);
4331 * Relayout the object according to current object size.
4333 * @param obj the evas object - NOT NULL.
4336 _relayout(const Evas_Object *obj)
4338 Evas_Object_Textblock *o;
4340 o = (Evas_Object_Textblock *)(obj->object_data);
4341 _layout(obj, obj->cur.geometry.w, obj->cur.geometry.h,
4342 &o->formatted.w, &o->formatted.h);
4343 o->formatted.valid = 1;
4344 o->last_w = obj->cur.geometry.w;
4345 o->last_h = obj->cur.geometry.h;
4347 o->content_changed = 0;
4348 o->format_changed = EINA_FALSE;
4354 * Find the layout item and line that match the text node and position passed.
4356 * @param obj the evas object - NOT NULL.
4357 * @param n the text node - Not null.
4358 * @param pos the position to look for - valid.
4359 * @param[out] lnr the line found - not null.
4360 * @param[out] tir the item found - not null.
4361 * @see _find_layout_format_item_line_match()
4364 _find_layout_item_line_match(Evas_Object *obj, Evas_Object_Textblock_Node_Text *n, int pos, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
4366 Evas_Object_Textblock_Paragraph *found_par;
4367 Evas_Object_Textblock_Line *ln;
4368 Evas_Object_Textblock *o;
4370 o = (Evas_Object_Textblock *)(obj->object_data);
4371 if (!o->formatted.valid) _relayout(obj);
4376 _layout_paragraph_render(o, found_par);
4377 EINA_INLIST_FOREACH(found_par->lines, ln)
4379 Evas_Object_Textblock_Item *it;
4381 EINA_INLIST_FOREACH(ln->items, it)
4383 /* FIXME: p should be size_t, same goes for pos */
4384 int p = (int) it->text_pos;
4386 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
4388 Evas_Object_Textblock_Text_Item *ti =
4391 p += (int) ti->text_props.text_len;
4398 if (((pos >= (int) it->text_pos) && (pos < p)))
4416 * Return the line number 'line'.
4418 * @param obj the evas object - NOT NULL.
4419 * @param line the line to find
4420 * @return the line of line number or NULL if no line found.
4422 static Evas_Object_Textblock_Line *
4423 _find_layout_line_num(const Evas_Object *obj, int line)
4425 Evas_Object_Textblock_Paragraph *par;
4426 Evas_Object_Textblock_Line *ln;
4427 Evas_Object_Textblock *o;
4429 o = (Evas_Object_Textblock *)(obj->object_data);
4431 par = _layout_find_paragraph_by_line_no(o, line);
4434 _layout_paragraph_render(o, par);
4435 EINA_INLIST_FOREACH(par->lines, ln)
4437 if (par->line_no + ln->line_no == line) return ln;
4444 evas_object_textblock_add(Evas *e)
4448 MAGIC_CHECK(e, Evas, MAGIC_EVAS);
4451 obj = evas_object_new(e);
4452 evas_object_textblock_init(obj);
4453 evas_object_inject(obj, e);
4457 EAPI Evas_Textblock_Style *
4458 evas_textblock_style_new(void)
4460 Evas_Textblock_Style *ts;
4462 ts = calloc(1, sizeof(Evas_Textblock_Style));
4467 evas_textblock_style_free(Evas_Textblock_Style *ts)
4480 evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
4486 /* If the style wasn't really changed, abort. */
4487 if ((!ts->style_text && !text) ||
4488 (ts->style_text && text && !strcmp(text, ts->style_text)))
4491 EINA_LIST_FOREACH(ts->objects, l, obj)
4493 Evas_Object_Textblock *o;
4495 o = (Evas_Object_Textblock *)(obj->object_data);
4496 _evas_textblock_invalidate_all(o);
4497 _evas_textblock_changed(o, obj);
4500 _style_replace(ts, text);
4504 // format MUST be KEY='VALUE'[KEY='VALUE']...
4506 const char *key_start, *key_stop, *val_start;
4508 key_start = key_stop = val_start = NULL;
4514 if (!isspace((unsigned char)(*p)))
4519 if ((*p == '=') || (isspace((unsigned char)(*p))))
4522 else if (!val_start)
4524 if (((*p) == '\'') && (*(p + 1)))
4529 if ((key_start) && (key_stop) && (val_start))
4531 char *tags, *replaces = NULL;
4532 Evas_Object_Style_Tag *tag;
4533 const char *val_stop = NULL;
4538 Eina_Strbuf *buf = eina_strbuf_new();
4539 val_stop = val_start;
4544 /* Break if we found the tag end */
4547 eina_strbuf_append_length(buf, val_stop,
4553 eina_strbuf_append_length(buf, val_stop,
4555 eina_strbuf_append_char(buf, '\'');
4561 replaces = eina_strbuf_string_steal(buf);
4562 eina_strbuf_free(buf);
4564 /* If we didn't find an end, just aboart. */
4567 if (replaces) free(replaces);
4571 tag_len = key_stop - key_start;
4572 replace_len = val_stop - val_start;
4574 tags = malloc(tag_len + 1);
4577 memcpy(tags, key_start, tag_len);
4581 if ((tags) && (replaces))
4583 if (!strcmp(tags, "DEFAULT"))
4585 ts->default_tag = replaces;
4590 tag = calloc(1, sizeof(Evas_Object_Style_Tag));
4593 tag->tag.tag = tags;
4594 tag->tag.replace = replaces;
4595 tag->tag.tag_len = tag_len;
4596 tag->tag.replace_len = replace_len;
4597 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
4608 if (tags) free(tags);
4609 if (replaces) free(replaces);
4611 key_start = key_stop = val_start = NULL;
4619 evas_textblock_style_get(const Evas_Textblock_Style *ts)
4621 if (!ts) return NULL;
4622 return ts->style_text;
4625 /* textblock styles */
4628 _textblock_style_generic_set(Evas_Object *obj, Evas_Textblock_Style *ts,
4629 Evas_Textblock_Style **obj_ts)
4632 if (ts == *obj_ts) return;
4633 if ((ts) && (ts->delete_me)) return;
4636 Evas_Textblock_Style *old_ts;
4639 free(o->markup_text);
4640 o->markup_text = NULL;
4644 old_ts->objects = eina_list_remove(old_ts->objects, obj);
4645 if ((old_ts->delete_me) && (!old_ts->objects))
4646 evas_textblock_style_free(old_ts);
4650 ts->objects = eina_list_append(ts->objects, obj);
4654 _evas_textblock_invalidate_all(o);
4655 _evas_textblock_changed(o, obj);
4659 evas_object_textblock_style_set(Evas_Object *obj, Evas_Textblock_Style *ts)
4662 _textblock_style_generic_set(obj, ts, &(o->style));
4665 EAPI const Evas_Textblock_Style *
4666 evas_object_textblock_style_get(const Evas_Object *obj)
4668 TB_HEAD_RETURN(NULL);
4673 evas_object_textblock_style_user_push(Evas_Object *obj, Evas_Textblock_Style *ts)
4676 _textblock_style_generic_set(obj, ts, &(o->style_user));
4679 EAPI const Evas_Textblock_Style *
4680 evas_object_textblock_style_user_peek(const Evas_Object *obj)
4682 TB_HEAD_RETURN(NULL);
4683 return o->style_user;
4687 evas_object_textblock_style_user_pop(Evas_Object *obj)
4690 _textblock_style_generic_set(obj, NULL, &(o->style_user));
4694 evas_object_textblock_replace_char_set(Evas_Object *obj, const char *ch)
4697 if (o->repch) eina_stringshare_del(o->repch);
4698 if (ch) o->repch = eina_stringshare_add(ch);
4699 else o->repch = NULL;
4700 _evas_textblock_invalidate_all(o);
4701 _evas_textblock_changed(o, obj);
4705 evas_object_textblock_legacy_newline_set(Evas_Object *obj, Eina_Bool mode)
4708 if (o->legacy_newline == mode)
4711 o->legacy_newline = mode;
4712 /* FIXME: Should recreate all the textnodes... For now, it's just
4713 * for new text inserted. */
4717 evas_object_textblock_legacy_newline_get(const Evas_Object *obj)
4719 TB_HEAD_RETURN(EINA_FALSE);
4720 return o->legacy_newline;
4724 evas_object_textblock_valign_set(Evas_Object *obj, double align)
4727 if (align < 0.0) align = 0.0;
4728 else if (align > 1.0) align = 1.0;
4729 if (o->valign == align) return;
4731 _evas_textblock_changed(o, obj);
4735 evas_object_textblock_valign_get(const Evas_Object *obj)
4737 TB_HEAD_RETURN(0.0);
4742 evas_object_textblock_bidi_delimiters_set(Evas_Object *obj, const char *delim)
4745 eina_stringshare_replace(&o->bidi_delimiters, delim);
4749 evas_object_textblock_bidi_delimiters_get(const Evas_Object *obj)
4751 TB_HEAD_RETURN(NULL);
4752 return o->bidi_delimiters;
4756 evas_object_textblock_replace_char_get(Evas_Object *obj)
4758 TB_HEAD_RETURN(NULL);
4764 * Advance p_buff to point after the end of the string. It's used with the
4765 * @ref escaped_strings[] variable.
4767 * @param p_buff the pointer to the current string.
4770 _escaped_advance_after_end_of_string(const char **p_buf)
4772 while (**p_buf != 0) (*p_buf)++;
4778 * Advance p_buff to point after the end of the string. It's used with the
4779 * @ref escaped_strings[] variable. Also chec if matches.
4782 * @param p_buff the pointer to the current string.
4785 _escaped_is_eq_and_advance(const char *s, const char *s_end,
4786 const char **p_m, const char *m_end)
4788 Eina_Bool reached_end;
4789 for (;((s < s_end) && (*p_m < m_end)); s++, (*p_m)++)
4793 _escaped_advance_after_end_of_string(p_m);
4798 reached_end = !**p_m;
4800 _escaped_advance_after_end_of_string(p_m);
4802 return ((s == s_end) && reached_end);
4808 * @param s the string to match
4810 static inline const char *
4811 _escaped_char_match(const char *s, int *adv)
4813 const char *map_itr, *map_end, *mc, *sc;
4815 map_itr = escape_strings;
4816 map_end = map_itr + sizeof(escape_strings);
4818 while (map_itr < map_end)
4824 _escaped_advance_after_end_of_string(&map_itr);
4825 if (map_itr >= map_end) break;
4830 while ((*mc) && (*sc))
4832 if ((unsigned char)*sc < (unsigned char)*mc) return NULL;
4843 *adv = mc - map_itr;
4846 _escaped_advance_after_end_of_string(&map_itr);
4855 * @param s the string to match
4857 static inline const char *
4858 _escaped_char_get(const char *s, const char *s_end)
4860 /* Handle numeric escape codes. */
4863 static char utf8_escape[7]; /* Support up to 6 bytes utf8 */
4865 Eina_Unicode uchar[2] = { 0, 0 };
4869 s += 2; /* Skip "&#" */
4871 if (tolower((unsigned char)(*s)) == 'x')
4878 if (len >= sizeof(ustr) + 1)
4881 memcpy(ustr, s, len);
4883 uchar[0] = strtol(ustr, NULL, base);
4888 utf8_char = eina_unicode_unicode_to_utf8(uchar, NULL);
4889 strcpy(utf8_escape, utf8_char);
4896 const char *map_itr, *map_end;
4898 map_itr = escape_strings;
4899 map_end = map_itr + sizeof(escape_strings);
4901 while (map_itr < map_end)
4903 if (_escaped_is_eq_and_advance(s, s_end, &map_itr, map_end))
4905 if (map_itr < map_end)
4906 _escaped_advance_after_end_of_string(&map_itr);
4914 evas_textblock_escape_string_get(const char *escape)
4917 return _escaped_char_get(escape, escape + strlen(escape));
4921 evas_textblock_escape_string_range_get(const char *escape_start, const char *escape_end)
4923 return _escaped_char_get(escape_start, escape_end);
4927 evas_textblock_string_escape_get(const char *string, int *len_ret)
4930 return _escaped_char_match(string, len_ret);
4935 * Appends the escaped char beteewn s and s_end to the curosr
4938 * @param s the start of the string
4939 * @param s_end the end of the string.
4942 _append_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4947 escape = _escaped_char_get(s, s_end);
4949 evas_textblock_cursor_text_append(cur, escape);
4954 * prepends the escaped char beteewn s and s_end to the curosr
4957 * @param s the start of the string
4958 * @param s_end the end of the string.
4961 _prepend_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4966 escape = _escaped_char_get(s, s_end);
4968 evas_textblock_cursor_text_prepend(cur, escape);
4973 evas_object_textblock_text_markup_set(Evas_Object *obj, const char *text)
4976 if ((text != o->markup_text) && (o->markup_text))
4978 free(o->markup_text);
4979 o->markup_text = NULL;
4982 if (!o->style && !o->style_user)
4984 if (text != o->markup_text)
4986 if (text) o->markup_text = strdup(text);
4990 evas_textblock_cursor_paragraph_first(o->cursor);
4992 evas_object_textblock_text_markup_prepend(o->cursor, text);
4993 /* Point all the cursors to the starrt */
4996 Evas_Textblock_Cursor *data;
4998 evas_textblock_cursor_paragraph_first(o->cursor);
4999 EINA_LIST_FOREACH(o->cursors, l, data)
5000 evas_textblock_cursor_paragraph_first(data);
5005 evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char *text)
5007 Evas_Object *obj = cur->obj;
5012 char *tag_start, *tag_end, *esc_start, *esc_end;
5014 tag_start = tag_end = esc_start = esc_end = NULL;
5017 /* This loop goes through all of the mark up text until it finds format
5018 * tags, escape sequences or the terminating NULL. When it finds either
5019 * of those, it appends the text found up until that point to the textblock
5020 * proccesses whatever found. It repeats itself until the termainating
5021 * NULL is reached. */
5025 /* If we got to the end of string or just finished/started tag
5026 * or escape sequence handling. */
5028 (tag_end) || (esc_end) ||
5029 (tag_start) || (esc_start))
5033 /* If we reached to a tag ending, analyze the tag */
5035 size_t ttag_len = tag_end - tag_start;
5038 ttag = malloc(ttag_len + 1);
5041 memcpy(ttag, tag_start, ttag_len);
5043 evas_textblock_cursor_format_prepend(cur, ttag);
5046 tag_start = tag_end = NULL;
5050 _prepend_escaped_char(cur, esc_start, esc_end + 1);
5051 esc_start = esc_end = NULL;
5055 _prepend_text_run(cur, s, p);
5065 /* Append the text prior to this to the textblock and mark
5066 * the start of the tag */
5069 _prepend_text_run(cur, s, p);
5085 /* Append the text prior to this to the textblock and mark
5086 * the start of the escape sequence */
5089 _prepend_text_run(cur, s, p);
5101 /* Unicode object replcament char */
5102 else if (!strncmp(_REPLACEMENT_CHAR_UTF8, p,
5103 text_len = strlen(_REPLACEMENT_CHAR_UTF8)) ||
5104 !strncmp(_NEWLINE_UTF8, p,
5105 text_len = strlen(_NEWLINE_UTF8)) ||
5106 !strncmp(_TAB_UTF8, p,
5107 text_len = strlen(_TAB_UTF8)) ||
5108 !strncmp(_PARAGRAPH_SEPARATOR_UTF8, p,
5109 text_len = strlen(_PARAGRAPH_SEPARATOR_UTF8)))
5111 /*FIXME: currently just remove them, maybe do something
5112 * fancier in the future, atm it breaks if this char
5114 _prepend_text_run(cur, s, p);
5115 /* it's also advanced later in this loop need +text_len
5118 s = p + 1; /* One after the end of the replacement char */
5123 _evas_textblock_changed(o, obj);
5129 * An helper function to markup get. Appends the format from fnode to the strbugf txt.
5131 * @param o the textblock object.
5132 * @param txt the strbuf to append to.
5133 * @param fnode the format node to process.
5136 _markup_get_format_append(Eina_Strbuf *txt, Evas_Object_Textblock_Node_Format *fnode)
5138 eina_strbuf_append_char(txt, '<');
5142 // FIXME: need to escape
5143 s = fnode->orig_format;
5144 if (!fnode->opener && !fnode->own_closer)
5145 eina_strbuf_append_char(txt, '/');
5146 eina_strbuf_append(txt, s);
5147 if (fnode->own_closer)
5148 eina_strbuf_append_char(txt, '/');
5150 eina_strbuf_append_char(txt, '>');
5155 * An helper function to markup get. Appends the text in text.
5157 * @param txt the strbuf to append to.
5158 * @param text the text to process.
5161 _markup_get_text_append(Eina_Strbuf *txt, const Eina_Unicode *text)
5163 char *p = eina_unicode_unicode_to_utf8(text, NULL);
5170 escape = _escaped_char_match(p, &adv);
5174 eina_strbuf_append(txt, escape);
5178 eina_strbuf_append_char(txt, *p);
5185 evas_object_textblock_text_markup_get(const Evas_Object *obj)
5187 Evas_Object_Textblock_Node_Text *n;
5188 Eina_Strbuf *txt = NULL;
5190 TB_HEAD_RETURN(NULL);
5191 if (o->markup_text) return(o->markup_text);
5192 txt = eina_strbuf_new();
5193 EINA_INLIST_FOREACH(o->text_nodes, n)
5195 Evas_Object_Textblock_Node_Format *fnode;
5196 Eina_Unicode *text_base, *text;
5200 /* For each text node to thorugh all of it's format nodes
5201 * append text from the start to the offset of the next format
5202 * using the last format got. if needed it also creates format items
5203 * this is the core algorithm of the layout mechanism.
5204 * Skip the unicode replacement chars when there are because
5205 * we don't want to print them. */
5206 len = eina_ustrbuf_length_get(n->unicode);
5208 eina_unicode_strndup(eina_ustrbuf_string_get(n->unicode), len);
5209 fnode = n->format_node;
5211 while (fnode && (fnode->text_node == n))
5213 Eina_Unicode tmp_ch;
5214 off += fnode->offset;
5216 if (off > len) break;
5217 /* No need to skip on the first run */
5219 text[off] = 0; /* Null terminate the part of the string */
5220 _markup_get_text_append(txt, text);
5221 _markup_get_format_append(txt, fnode);
5222 text[off] = tmp_ch; /* Restore the char */
5233 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
5235 /* Add the rest, skip replacement */
5236 _markup_get_text_append(txt, text);
5241 o->markup_text = eina_strbuf_string_steal(txt);
5242 eina_strbuf_free(txt);
5243 return o->markup_text;
5247 evas_textblock_text_markup_to_utf8(const Evas_Object *obj, const char *text)
5249 /* FIXME: Redundant and awful, should be merged with markup_prepend */
5252 char *tag_start, *tag_end, *esc_start, *esc_end;
5254 if (!text) return NULL;
5257 tag_start = tag_end = esc_start = esc_end = NULL;
5258 sbuf = eina_strbuf_new();
5261 /* This loop goes through all of the mark up text until it finds format
5262 * tags, escape sequences or the terminating NULL. When it finds either
5263 * of those, it appends the text found up until that point to the textblock
5264 * proccesses whatever found. It repeats itself until the termainating
5265 * NULL is reached. */
5268 /* If we got to the end of string or just finished/started tag
5269 * or escape sequence handling. */
5271 (tag_end) || (esc_end) ||
5272 (tag_start) || (esc_start))
5276 /* If we reached to a tag ending, analyze the tag */
5280 tag_start++; /* Skip the < */
5281 tag_end--; /* Skip the > */
5282 if ((tag_end > tag_start) && (*(tag_end - 1) == '/'))
5284 tag_end --; /* Skip the terminating '/' */
5285 while (*(tag_end - 1) == ' ')
5286 tag_end--; /* skip trailing ' ' */
5289 ttag_len = tag_end - tag_start;
5291 ttag = malloc(ttag_len + 1);
5294 const char *match = NULL;
5296 memcpy(ttag, tag_start, ttag_len);
5302 match = _style_match_tag(
5303 evas_object_textblock_style_get(obj),
5304 ttag, ttag_len, &replace_len);
5307 if (!match) match = ttag;
5309 if (_IS_PARAGRAPH_SEPARATOR_SIMPLE(match))
5310 eina_strbuf_append(sbuf, _PARAGRAPH_SEPARATOR_UTF8);
5311 else if (_IS_LINE_SEPARATOR(match))
5312 eina_strbuf_append(sbuf, _NEWLINE_UTF8);
5313 else if (_IS_TAB(match))
5314 eina_strbuf_append(sbuf, _TAB_UTF8);
5315 else if (!strncmp(match, "item", 4))
5316 eina_strbuf_append(sbuf, _REPLACEMENT_CHAR_UTF8);
5320 tag_start = tag_end = NULL;
5326 escape = _escaped_char_get(esc_start, esc_end + 1);
5327 if (escape) eina_strbuf_append(sbuf, escape);
5328 esc_start = esc_end = NULL;
5332 eina_strbuf_append_length(sbuf, s, p - s);
5342 /* Append the text prior to this to the textblock and
5343 * mark the start of the tag */
5346 eina_strbuf_append_length(sbuf, s, p - s);
5362 /* Append the text prior to this to the textblock and mark
5363 * the start of the escape sequence */
5366 eina_strbuf_append_length(sbuf, s, p - s);
5381 ret = eina_strbuf_string_steal(sbuf);
5382 eina_strbuf_free(sbuf);
5387 evas_textblock_text_utf8_to_markup(const Evas_Object *obj, const char *text)
5391 int ch, pos = 0, pos2 = 0;
5395 if (!text) return NULL;
5397 sbuf = eina_strbuf_new();
5402 pos2 = evas_string_char_next_get(text, pos2, &ch);
5403 if ((ch <= 0) || (pos2 <= 0)) break;
5406 eina_strbuf_append(sbuf, "<br/>");
5407 else if (ch == _TAB)
5408 eina_strbuf_append(sbuf, "<tab/>");
5410 eina_strbuf_append(sbuf, "<");
5412 eina_strbuf_append(sbuf, ">");
5414 eina_strbuf_append(sbuf, "&");
5415 else if (ch == _PARAGRAPH_SEPARATOR)
5416 eina_strbuf_append(sbuf, "<ps/>");
5417 else if (ch == _REPLACEMENT_CHAR)
5418 eina_strbuf_append(sbuf, "");
5421 eina_strbuf_append_length(sbuf, text + pos, pos2 - pos);
5424 str = eina_strbuf_string_steal(sbuf);
5425 eina_strbuf_free(sbuf);
5434 * Merge the current node with the next, no need to remove PS, already
5437 * @param o the text block object.
5438 * @param to merge into to.
5441 _evas_textblock_nodes_merge(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *to)
5443 Evas_Object_Textblock_Node_Format *itr;
5444 Evas_Object_Textblock_Node_Format *pnode;
5445 Evas_Object_Textblock_Node_Text *from;
5446 const Eina_Unicode *text;
5450 from = _NODE_TEXT(EINA_INLIST_GET(to)->next);
5452 to_len = eina_ustrbuf_length_get(to->unicode);
5453 text = eina_ustrbuf_string_get(from->unicode);
5454 len = eina_ustrbuf_length_get(from->unicode);
5455 eina_ustrbuf_append_length(to->unicode, text, len);
5457 itr = from->format_node;
5458 if (itr && (itr->text_node == from))
5460 pnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->prev);
5461 if (pnode && (pnode->text_node == to))
5463 itr->offset += to_len - _evas_textblock_node_format_pos_get(pnode);
5467 itr->offset += to_len;
5471 while (itr && (itr->text_node == from))
5473 itr->text_node = to;
5474 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
5476 if (!to->format_node || (to->format_node->text_node != to))
5478 to->format_node = from->format_node;
5481 /* When it comes to how we handle it, merging is like removing both nodes
5482 * and creating a new one, se we need to do the needed cleanups. */
5484 to->par->text_node = NULL;
5487 to->is_new = EINA_TRUE;
5489 _evas_textblock_cursors_set_node(o, from, to);
5490 _evas_textblock_node_text_remove(o, from);
5495 * Merge the current node with the next, no need to remove PS, already
5498 * @param cur the cursor that points to the current node
5501 _evas_textblock_cursor_nodes_merge(Evas_Textblock_Cursor *cur)
5503 Evas_Object_Textblock_Node_Text *nnode;
5504 Evas_Object_Textblock *o;
5508 len = eina_ustrbuf_length_get(cur->node->unicode);
5510 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5511 nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5512 _evas_textblock_nodes_merge(o, cur->node);
5513 _evas_textblock_cursors_update_offset(cur, nnode, 0, len);
5514 _evas_textblock_cursors_set_node(o, nnode, cur->node);
5515 if (nnode == o->cursor->node)
5517 o->cursor->node = cur->node;
5518 o->cursor->pos += len;
5524 * Return the format at a specific position.
5526 * @param cur the cursor to the position.
5527 * @return the format node at the specific position or NULL if not found.
5529 static Evas_Object_Textblock_Node_Format *
5530 _evas_textblock_cursor_node_format_at_pos_get(const Evas_Textblock_Cursor *cur)
5532 Evas_Object_Textblock_Node_Format *node;
5533 Evas_Object_Textblock_Node_Format *itr;
5536 if (!cur->node) return NULL;
5538 node = cur->node->format_node;
5539 if (!node) return NULL;
5540 /* If there is no exclusive format node to this paragraph return the
5541 * previous's node */
5542 /* Find the main format node */
5543 EINA_INLIST_FOREACH(node, itr)
5545 if (itr->text_node != cur->node)
5549 if ((position + itr->offset) == cur->pos)
5553 position += itr->offset;
5560 * Return the last format node at the position of the format node n.
5562 * @param n a format node at the position.
5563 * @return the last format node at the position of n.
5565 static Evas_Object_Textblock_Node_Format *
5566 _evas_textblock_node_format_last_at_off(const Evas_Object_Textblock_Node_Format *n)
5568 const Evas_Object_Textblock_Node_Format *nnode;
5569 const Evas_Object_Textblock_Node_Text *tnode;
5570 if (!n) return NULL;
5572 tnode = n->text_node;
5576 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5578 while (nnode && (nnode->text_node == tnode) && (nnode->offset == 0));
5580 return (Evas_Object_Textblock_Node_Format *) n;
5585 * Returns the visible format at a specific location.
5587 * @param n a format at the specific position.
5588 * @return the format node at the specific position or NULL if not found.
5590 static Evas_Object_Textblock_Node_Format *
5591 _evas_textblock_node_visible_at_pos_get(const Evas_Object_Textblock_Node_Format *n)
5593 const Evas_Object_Textblock_Node_Format *nnode;
5594 if (!n) return NULL;
5595 /* The visible format is the last one, because it inserts a replacement
5596 * char that advances the next formats. */
5602 if (n->visible) return (Evas_Object_Textblock_Node_Format *) n;
5603 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5605 while (nnode && (nnode->offset == 0));
5612 * Return the last format that applies to a specific cursor or at the specific
5613 * position the cursor points to. This means either a cursor at or before the
5614 * position of the cursor in the text node is returned or the previous's text
5615 * node's format node.
5617 * @param cur the position to look at.
5618 * @return the format node found.
5620 static Evas_Object_Textblock_Node_Format *
5621 _evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur)
5623 Evas_Object_Textblock_Node_Format *node, *pitr = NULL;
5624 Evas_Object_Textblock_Node_Format *itr;
5625 size_t position = 0;
5627 if (!cur->node) return NULL;
5629 node = cur->node->format_node;
5630 if (!node) return NULL;
5631 /* If there is no exclusive format node to this paragraph return the
5632 * previous's node */
5633 if (node->text_node != cur->node)
5637 else if (node->offset > cur->pos)
5639 return _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5641 /* Find the main format node */
5642 pitr = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5643 EINA_INLIST_FOREACH(node, itr)
5645 if ((itr->text_node != cur->node) ||
5646 ((position + itr->offset) > cur->pos))
5650 else if ((position + itr->offset) == cur->pos)
5655 position += itr->offset;
5662 * Find the layout item and line that match the cursor.
5664 * @param cur the cursor we are currently at. - NOT NULL.
5665 * @param[out] lnr the line found - not null.
5666 * @param[out] itr the item found - not null.
5667 * @return @c EINA_TRUE if we matched the previous format, @c EINA_FALSE
5671 _find_layout_item_match(const Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
5673 Evas_Textblock_Cursor cur2;
5674 Eina_Bool previous_format = EINA_FALSE;
5676 cur2.obj = cur->obj;
5677 evas_textblock_cursor_copy(cur, &cur2);
5683 if (_evas_textblock_cursor_is_at_the_end(cur) &&
5684 evas_textblock_cursor_format_is_visible_get(&cur2))
5686 _find_layout_item_line_match(cur2.obj, cur2.node, cur2.pos, lnr, itr);
5687 previous_format = EINA_TRUE;
5691 _find_layout_item_line_match(cur->obj, cur->node, cur->pos, lnr, itr);
5693 return previous_format;
5696 EAPI Evas_Textblock_Cursor *
5697 evas_object_textblock_cursor_get(const Evas_Object *obj)
5699 TB_HEAD_RETURN(NULL);
5703 EAPI Evas_Textblock_Cursor *
5704 evas_object_textblock_cursor_new(const Evas_Object *obj)
5706 Evas_Textblock_Cursor *cur;
5708 TB_HEAD_RETURN(NULL);
5709 cur = calloc(1, sizeof(Evas_Textblock_Cursor));
5710 cur->obj = (Evas_Object *) obj;
5711 cur->node = o->text_nodes;
5714 o->cursors = eina_list_append(o->cursors, cur);
5719 evas_textblock_cursor_free(Evas_Textblock_Cursor *cur)
5721 Evas_Object_Textblock *o;
5724 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5725 if (cur == o->cursor) return;
5726 o->cursors = eina_list_remove(o->cursors, cur);
5731 evas_textblock_cursor_is_format(const Evas_Textblock_Cursor *cur)
5733 if (!cur || !cur->node) return EINA_FALSE;
5734 return (_evas_textblock_cursor_node_format_at_pos_get(cur)) ?
5735 EINA_TRUE : EINA_FALSE;
5738 EAPI const Eina_List *
5739 evas_textblock_node_format_list_get(const Evas_Object *obj, const char *anchor)
5741 TB_HEAD_RETURN(NULL);
5742 if (!strcmp(anchor, "a"))
5743 return o->anchors_a;
5744 else if (!strcmp(anchor, "item"))
5745 return o->anchors_item;
5750 EAPI const Evas_Object_Textblock_Node_Format *
5751 evas_textblock_node_format_first_get(const Evas_Object *obj)
5753 TB_HEAD_RETURN(NULL);
5754 return o->format_nodes;
5757 EAPI const Evas_Object_Textblock_Node_Format *
5758 evas_textblock_node_format_last_get(const Evas_Object *obj)
5760 TB_HEAD_RETURN(NULL);
5761 if (o->format_nodes)
5763 return _NODE_FORMAT(EINA_INLIST_GET(o->format_nodes)->last);
5768 EAPI const Evas_Object_Textblock_Node_Format *
5769 evas_textblock_node_format_next_get(const Evas_Object_Textblock_Node_Format *n)
5771 return _NODE_FORMAT(EINA_INLIST_GET(n)->next);
5774 EAPI const Evas_Object_Textblock_Node_Format *
5775 evas_textblock_node_format_prev_get(const Evas_Object_Textblock_Node_Format *n)
5777 return _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
5781 evas_textblock_node_format_remove_pair(Evas_Object *obj,
5782 Evas_Object_Textblock_Node_Format *n)
5784 Evas_Object_Textblock_Node_Text *tnode1;
5785 Evas_Object_Textblock_Node_Format *fmt, *found_node = NULL;
5786 Eina_List *fstack = NULL;
5795 const char *fstr = fmt->orig_format;
5797 if (fmt->opener && !fmt->own_closer)
5799 fstack = eina_list_prepend(fstack, fmt);
5801 else if (fstr && !fmt->opener)
5804 fstr_len = strlen(fstr);
5805 /* Generic popper, just pop */
5806 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
5808 fstack = eina_list_remove_list(fstack, fstack);
5815 /* Find the matching format and pop it, if the matching format
5816 * is out format, i.e the last one, pop and break. */
5820 Evas_Object_Textblock_Node_Format *fnode;
5821 EINA_LIST_FOREACH(fstack, i, fnode)
5823 if (_FORMAT_IS_CLOSER_OF(
5824 fnode->orig_format, fstr, fstr_len))
5826 /* Last one, this is our item! */
5827 if (!eina_list_next(i))
5832 fstack = eina_list_remove_list(fstack, i);
5839 fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
5841 while (fmt && fstack);
5845 fstack = eina_list_free(fstack);
5849 size_t ind = _evas_textblock_node_format_pos_get(n);
5850 const char *format = n->format;
5851 Evas_Textblock_Cursor cur;
5854 eina_ustrbuf_remove(n->text_node->unicode, ind, ind + 1);
5855 if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
5857 evas_textblock_cursor_at_format_set(&cur, n);
5858 _evas_textblock_cursor_nodes_merge(&cur);
5860 _evas_textblock_cursors_update_offset(&cur, n->text_node, ind, -1);
5862 tnode1 = n->text_node;
5863 _evas_textblock_node_format_remove(o, n, 0);
5864 if (found_node && (found_node != n))
5866 Evas_Object_Textblock_Node_Text *tnode2;
5867 tnode2 = found_node->text_node;
5868 /* found_node can never be visible! (it's the closing format) */
5869 _evas_textblock_node_format_remove(o, found_node, 0);
5871 /* FIXME: Should be unified in the layout, for example, added to a list
5872 * that checks this kind of removals. But until then, this is very fast
5874 /* Mark all the text nodes in between the removed formats as dirty. */
5877 tnode1->dirty = EINA_TRUE;
5878 if (tnode1 == tnode2)
5881 _NODE_TEXT(EINA_INLIST_GET(tnode1)->next);
5885 _evas_textblock_changed(o, obj);
5889 evas_textblock_cursor_paragraph_first(Evas_Textblock_Cursor *cur)
5891 Evas_Object_Textblock *o;
5893 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5894 cur->node = o->text_nodes;
5900 evas_textblock_cursor_paragraph_last(Evas_Textblock_Cursor *cur)
5902 Evas_Object_Textblock *o;
5903 Evas_Object_Textblock_Node_Text *node;
5906 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5907 node = o->text_nodes;
5910 node = _NODE_TEXT(EINA_INLIST_GET(node)->last);
5914 evas_textblock_cursor_paragraph_char_last(cur);
5925 evas_textblock_cursor_paragraph_next(Evas_Textblock_Cursor *cur)
5927 if (!cur) return EINA_FALSE;
5928 if (!cur->node) return EINA_FALSE;
5929 /* If there is a current text node, return the next text node (if exists)
5930 * otherwise, just return False. */
5933 Evas_Object_Textblock_Node_Text *nnode;
5934 nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5947 evas_textblock_cursor_paragraph_prev(Evas_Textblock_Cursor *cur)
5949 Evas_Object_Textblock_Node_Text *node;
5950 if (!cur) return EINA_FALSE;
5951 if (!cur->node) return EINA_FALSE;
5952 /* If the current node is a text node, just get the prev if any,
5953 * if it's a format, get the current text node out of the format and return
5954 * the prev text node if any. */
5956 /* If there is a current text node, return the prev text node
5957 * (if exists) otherwise, just return False. */
5960 Evas_Object_Textblock_Node_Text *pnode;
5961 pnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->prev);
5965 evas_textblock_cursor_paragraph_char_last(cur);
5973 evas_textblock_cursor_set_at_format(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *n)
5975 evas_textblock_cursor_at_format_set(cur, n);
5979 evas_textblock_cursor_format_next(Evas_Textblock_Cursor *cur)
5981 Evas_Object_Textblock_Node_Format *node;
5983 if (!cur) return EINA_FALSE;
5984 if (!cur->node) return EINA_FALSE;
5985 /* If the current node is a format node, just get the next if any,
5986 * if it's a text, get the current format node out of the text and return
5987 * the next format node if any. */
5988 node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
5989 node = _evas_textblock_node_format_last_at_off(node);
5992 if (cur->node->format_node)
5994 cur->pos = _evas_textblock_node_format_pos_get(node);
5998 /* If there is a current text node, return the next format node (if exists)
5999 * otherwise, just return False. */
6002 Evas_Object_Textblock_Node_Format *nnode;
6003 nnode = _NODE_FORMAT(EINA_INLIST_GET(node)->next);
6006 cur->node = nnode->text_node;
6007 cur->pos = _evas_textblock_node_format_pos_get(nnode);
6016 evas_textblock_cursor_format_prev(Evas_Textblock_Cursor *cur)
6018 const Evas_Object_Textblock_Node_Format *node;
6019 if (!cur) return EINA_FALSE;
6020 if (!cur->node) return EINA_FALSE;
6021 node = evas_textblock_cursor_format_get(cur);
6024 node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6027 cur->node = node->text_node;
6028 cur->pos = _evas_textblock_node_format_pos_get(node);
6033 /* If there is a current text node, return the next text node (if exists)
6034 * otherwise, just return False. */
6037 Evas_Object_Textblock_Node_Format *pnode;
6038 pnode = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
6041 cur->node = pnode->text_node;
6042 cur->pos = _evas_textblock_node_format_pos_get(pnode);
6050 #ifdef HAVE_LINEBREAK
6052 /* BREAK_AFTER: true if we can break after the current char.
6053 * Both macros assume str[i] is not the terminating nul */
6054 #define BREAK_AFTER(i) \
6055 (breaks[i] == WORDBREAK_BREAK)
6059 #define BREAK_AFTER(i) \
6060 ((!text[i + 1]) || \
6061 (_is_white(text[i]) && !_is_white(text[i + 1])) || \
6062 (!_is_white(text[i]) && _is_white(text[i + 1])))
6067 evas_textblock_cursor_word_start(Evas_Textblock_Cursor *cur)
6069 const Eina_Unicode *text;
6071 #ifdef HAVE_LINEBREAK
6075 if (!cur) return EINA_FALSE;
6076 if (!cur->node) return EINA_FALSE;
6078 text = eina_ustrbuf_string_get(cur->node->unicode);
6080 #ifdef HAVE_LINEBREAK
6082 const char *lang = ""; /* FIXME: get lang */
6083 size_t len = eina_ustrbuf_length_get(cur->node->unicode);
6084 breaks = malloc(len);
6085 set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
6091 /* Skip the first one. This ensures we don't point to the nul, and also
6092 * we just don't care about it anyway. */
6095 for ( ; i > 0 ; i--)
6099 /* Advance to the current char */
6107 #ifdef HAVE_LINEBREAK
6114 evas_textblock_cursor_word_end(Evas_Textblock_Cursor *cur)
6116 const Eina_Unicode *text;
6118 #ifdef HAVE_LINEBREAK
6122 if (!cur) return EINA_FALSE;
6123 if (!cur->node) return EINA_FALSE;
6125 text = eina_ustrbuf_string_get(cur->node->unicode);
6127 #ifdef HAVE_LINEBREAK
6129 const char *lang = ""; /* FIXME: get lang */
6130 size_t len = eina_ustrbuf_length_get(cur->node->unicode);
6131 breaks = malloc(len);
6132 set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
6138 for ( ; text[i] ; i++)
6142 /* This is the one to break after. */
6149 #ifdef HAVE_LINEBREAK
6156 evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur)
6159 const Eina_Unicode *text;
6161 if (!cur) return EINA_FALSE;
6162 if (!cur->node) return EINA_FALSE;
6165 text = eina_ustrbuf_string_get(cur->node->unicode);
6166 if (text[ind]) ind++;
6167 /* Only allow pointing a null if it's the last paragraph.
6168 * because we don't have a PS there. */
6176 if (!evas_textblock_cursor_paragraph_next(cur))
6178 /* If we already were at the end, that means we don't have
6179 * where to go next we should return FALSE */
6180 if (cur->pos == (size_t) ind)
6194 evas_textblock_cursor_char_prev(Evas_Textblock_Cursor *cur)
6196 if (!cur) return EINA_FALSE;
6197 if (!cur->node) return EINA_FALSE;
6204 return evas_textblock_cursor_paragraph_prev(cur);
6208 evas_textblock_cursor_paragraph_char_first(Evas_Textblock_Cursor *cur)
6216 evas_textblock_cursor_paragraph_char_last(Evas_Textblock_Cursor *cur)
6221 if (!cur->node) return;
6222 ind = eina_ustrbuf_length_get(cur->node->unicode);
6223 /* If it's not the last paragraph, go back one, because we want to point
6224 * to the PS, not the NULL */
6225 if (EINA_INLIST_GET(cur->node)->next)
6236 evas_textblock_cursor_line_char_first(Evas_Textblock_Cursor *cur)
6238 Evas_Object_Textblock *o;
6239 Evas_Object_Textblock_Line *ln = NULL;
6240 Evas_Object_Textblock_Item *it = NULL;
6243 if (!cur->node) return;
6244 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6245 if (!o->formatted.valid) _relayout(cur->obj);
6247 _find_layout_item_match(cur, &ln, &it);
6252 Evas_Object_Textblock_Item *i;
6254 EINA_INLIST_FOREACH(ln->items, i)
6256 if (it->text_pos > i->text_pos)
6264 cur->pos = it->text_pos;
6265 cur->node = it->text_node;
6270 evas_textblock_cursor_line_char_last(Evas_Textblock_Cursor *cur)
6272 Evas_Object_Textblock *o;
6273 Evas_Object_Textblock_Line *ln = NULL;
6274 Evas_Object_Textblock_Item *it = NULL;
6277 if (!cur->node) return;
6278 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6279 if (!o->formatted.valid) _relayout(cur->obj);
6281 _find_layout_item_match(cur, &ln, &it);
6286 Evas_Object_Textblock_Item *i;
6288 EINA_INLIST_FOREACH(ln->items, i)
6290 if (it->text_pos < i->text_pos)
6300 cur->node = it->text_node;
6301 cur->pos = it->text_pos;
6302 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
6304 ind = _ITEM_TEXT(it)->text_props.text_len - 1;
6305 if (!IS_AT_END(_ITEM_TEXT(it), ind)) ind++;
6308 else if (!EINA_INLIST_GET(ln)->next && !EINA_INLIST_GET(ln->par)->next)
6317 * checks if a format (as a string) is visible/changes format and sets the
6318 * fnode properties accordingly.
6320 * @param fnode the format node
6321 * @param s the string.
6324 _evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format *fnode,
6328 Eina_Bool is_opener = EINA_TRUE;
6330 fnode->visible = fnode->format_change = EINA_FALSE;
6331 fnode->anchor = ANCHOR_NONE;
6334 if (!fnode->own_closer)
6336 is_opener = fnode->opener;
6337 fnode->format_change = EINA_TRUE;
6340 while ((item = _format_parse(&s)))
6342 int itlen = s - item;
6343 /* We care about all of the formats even after a - except for
6344 * item which we don't care after a - because it's just a standard
6346 if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) ||
6347 (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) ||
6348 (!strncmp(item, "br", itlen) && (itlen >= 2)) ||
6349 (!strncmp(item, "tab", itlen) && (itlen >= 3)) ||
6350 (!strncmp(item, "ps", itlen) && (itlen >= 2)) ||
6351 (!strncmp(item, "item", itlen) && (itlen >= 4) && is_opener))
6353 fnode->visible = EINA_TRUE;
6356 if (is_opener && !strncmp(item, "a", itlen))
6358 fnode->anchor = ANCHOR_A;
6360 else if (is_opener && !strncmp(item, "item", itlen) && (itlen >= 4))
6362 fnode->anchor = ANCHOR_ITEM;
6368 * Sets the cursor to the position of where the fmt points to.
6370 * @param cur the cursor to update.
6371 * @param fmt the format to set according to.
6374 static void __UNUSED__
6375 _evas_textblock_cursor_node_text_at_format(Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Node_Format *fmt)
6377 Evas_Object_Textblock_Node_Text *text;
6378 Evas_Object_Textblock_Node_Format *base_format;
6379 Evas_Object_Textblock_Node_Format *itr;
6380 size_t position = 0;
6382 if (!cur || !fmt) return;
6383 /* Find the main format node */
6384 text = fmt->text_node;
6386 base_format = text->format_node;
6387 EINA_INLIST_FOREACH(base_format, itr)
6393 position += itr->offset;
6395 cur->pos = position;
6402 * Remove pairs of + and - formats and also remove formats without + or -
6403 * i.e formats that pair to themselves. Only removes invisible formats
6404 * that pair themselves, if you want to remove invisible formats that pair
6405 * themselves, please first change fmt->visible to EINA_FALSE.
6407 * @param o the textblock object.
6408 * @param fmt the current format.
6411 _evas_textblock_node_format_remove_matching(Evas_Object_Textblock *o,
6412 Evas_Object_Textblock_Node_Format *fmt)
6414 Evas_Object_Textblock_Node_Text *tnode;
6415 Eina_List *formats = NULL;
6420 tnode = fmt->text_node;
6424 Evas_Object_Textblock_Node_Format *nnode;
6425 const char *fstr = fmt->orig_format;
6427 nnode = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6430 offset = nnode->offset;
6434 if (fmt->opener && !fmt->own_closer)
6436 formats = eina_list_prepend(formats, fmt);
6438 else if (fstr && !fmt->opener)
6440 Evas_Object_Textblock_Node_Format *fnode;
6442 fstr_len = strlen(fstr);
6443 /* Generic popper, just pop (if there's anything to pop). */
6444 if (formats && (((fstr[0] == ' ') && !fstr[1]) || !fstr[0]))
6446 fnode = eina_list_data_get(formats);
6447 formats = eina_list_remove_list(formats, formats);
6448 _evas_textblock_node_format_remove(o, fnode, 0);
6449 _evas_textblock_node_format_remove(o, fmt, 0);
6451 /* Find the matching format and pop it, if the matching format
6452 * is our format, i.e the last one, pop and break. */
6455 Eina_List *i, *next;
6456 EINA_LIST_FOREACH_SAFE(formats, i, next, fnode)
6458 if (_FORMAT_IS_CLOSER_OF(
6459 fnode->orig_format, fstr, fstr_len))
6461 fnode = eina_list_data_get(i);
6462 formats = eina_list_remove_list(formats, i);
6463 _evas_textblock_node_format_remove(o, fnode, 0);
6464 _evas_textblock_node_format_remove(o, fmt, 0);
6470 else if (!fmt->visible)
6472 _evas_textblock_node_format_remove(o, fmt, 0);
6476 while (fmt && (offset == 0) && (fmt->text_node == tnode));
6477 eina_list_free(formats);
6481 * Add the offset (may be negative) to the first node after fmt which is
6482 * pointing to the text node tnode or to o->format_nodes if fmt is null
6483 * and it points to tnode.
6485 * @param o the textblock object.
6486 * @param tnode the text node the format should point to.
6487 * @param fmt the current format.
6488 * @param offset the offest to add (may be negative).
6491 _evas_textblock_node_format_adjust_offset(Evas_Object_Textblock *o,
6492 Evas_Object_Textblock_Node_Text *tnode,
6493 Evas_Object_Textblock_Node_Format *fmt, int offset)
6497 fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6501 fmt = o->format_nodes;
6503 if (fmt && (tnode == fmt->text_node))
6505 fmt->offset += offset;
6511 * Removes a format node updating the offset of the next format node and the
6512 * text nodes pointing to this node.
6514 * @param o the textblock object.
6515 * @param n the fromat node to remove
6518 _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visible_adjustment)
6520 /* Update the text nodes about the change */
6522 Evas_Object_Textblock_Node_Format *nnode;
6523 nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->next);
6524 /* If there's a next node that belongs to the same text node
6525 * and the curret node was the main one, advance the format node */
6526 if (nnode && (nnode->text_node == n->text_node))
6528 if (nnode->text_node->format_node == n)
6530 nnode->text_node->format_node = nnode;
6535 Evas_Object_Textblock_Node_Text *tnode;
6536 /* If there's no next one update the text nodes */
6537 nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
6538 tnode = n->text_node;
6539 /* Even if it's not the current text_node's main node
6540 * it can still be the next's. */
6541 if (tnode && (tnode->format_node != n))
6543 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6545 while (tnode && (tnode->format_node == n))
6547 tnode->format_node = nnode;
6548 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6552 _evas_textblock_node_format_adjust_offset(o, n->text_node, n,
6553 n->offset - visible_adjustment);
6555 o->format_nodes = _NODE_FORMAT(eina_inlist_remove(
6556 EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
6557 _evas_textblock_node_format_free(o, n);
6562 * Sets all the offsets of the format nodes between start and end in the text
6563 * node n to 0 and sets visibility to EINA_FALSE.
6564 * If end == -1 end means the end of the string.
6565 * Assumes there is a prev node or the current node will be preserved.
6567 * @param n the text node the positinos refer to.
6568 * @param start the start of where to delete from.
6569 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6570 * @returns @c EINA_TRUE if removed a PS, @c EINA_FALSE otherwise.
6573 _evas_textblock_node_text_adjust_offsets_to_start(Evas_Object_Textblock *o,
6574 Evas_Object_Textblock_Node_Text *n, size_t start, int end)
6576 Evas_Object_Textblock_Node_Format *last_node, *itr;
6577 Evas_Object_Textblock_Node_Text *new_node;
6584 itr = n->format_node;
6585 if (!itr || (itr->text_node != n)) return EINA_FALSE;
6588 if ((end < 0) || ((size_t) end == eina_ustrbuf_length_get(n->unicode)))
6594 /* We don't want the last one */
6598 /* If we are not removing the text node, all should stay in this text
6599 * node, otherwise, everything should move to the previous node */
6600 if ((start == 0) && !use_end)
6602 new_node = _NODE_TEXT(EINA_INLIST_GET(n)->prev);
6613 /* Find the first node after start */
6614 while (itr && (itr->text_node == n))
6621 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6624 if (!itr || (itr->text_node != n))
6629 delta = orig_end - pos;
6630 itr->offset -= pos - start;
6632 while (itr && (itr->text_node == n))
6635 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6639 pos += last_node->offset;
6642 /* start is negative when this gets relevant */
6643 if (use_end && (pos > (size_t) end))
6645 last_node->offset -= delta;
6649 delta = orig_end - pos;
6652 last_node->offset = 0;
6658 last_node->visible = EINA_FALSE;
6660 if (!itr || (itr && (itr->text_node != n)))
6662 /* Remove the PS, and return since it's the end of the node */
6663 if (_IS_PARAGRAPH_SEPARATOR(o, last_node->format))
6665 _evas_textblock_node_format_remove(o, last_node, 0);
6670 last_node->text_node = new_node;
6678 * Returns the first format in the range between start and end in the textblock
6681 * @param o the textblock object.
6682 * @param n the text node the positinos refer to.
6683 * @param start the start of where to delete from.
6684 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6686 static Evas_Object_Textblock_Node_Format *
6687 _evas_textblock_node_text_get_first_format_between(
6688 Evas_Object_Textblock_Node_Text *n, int start, int end)
6690 Evas_Object_Textblock_Node_Format *itr;
6692 itr = n->format_node;
6693 if (end < 0) use_end = 0;
6694 while (itr && (itr->text_node == n))
6696 start -= itr->offset;
6698 if ((end <= 0) && use_end)
6706 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6712 * Removes a text node and the corresponding format nodes.
6714 * @param o the textblock objec.t
6715 * @param n the node to remove.
6718 _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n)
6720 _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
6722 o->text_nodes = _NODE_TEXT(eina_inlist_remove(
6723 EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
6724 _evas_textblock_node_text_free(n);
6729 * Return the position where the formats starts at.
6731 * @param fmt the format to return the position of.
6732 * @return the position of the format in the text node it points to.
6735 _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt)
6737 Evas_Object_Textblock_Node_Text *text;
6738 Evas_Object_Textblock_Node_Format *base_format;
6739 Evas_Object_Textblock_Node_Format *itr;
6740 size_t position = 0;
6743 /* Find the main format node */
6744 text = fmt->text_node;
6745 base_format = text->format_node;
6746 EINA_INLIST_FOREACH(base_format, itr)
6752 position += itr->offset;
6754 return position + fmt->offset;
6758 evas_textblock_cursor_pos_get(const Evas_Textblock_Cursor *cur)
6760 Evas_Object_Textblock *o;
6761 Evas_Object_Textblock_Node_Text *n;
6764 if (!cur) return -1;
6765 if (!cur->node) return 0;
6766 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6768 while (n != cur->node)
6770 npos += eina_ustrbuf_length_get(n->unicode);
6771 n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6773 return npos + cur->pos;
6777 evas_textblock_cursor_pos_set(Evas_Textblock_Cursor *cur, int _pos)
6779 Evas_Object_Textblock *o;
6780 Evas_Object_Textblock_Node_Text *n;
6784 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6792 pos = (size_t) _pos;
6796 while (n && (pos >= eina_ustrbuf_length_get(n->unicode)))
6798 pos -= eina_ustrbuf_length_get(n->unicode);
6799 n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6807 else if (o->text_nodes)
6809 /* In case we went pass the last node, we need to put the cursor
6810 * at the absolute end. */
6811 Evas_Object_Textblock_Node_Text *last_n;
6813 last_n = _NODE_TEXT(EINA_INLIST_GET(o->text_nodes)->last);
6814 pos = eina_ustrbuf_length_get(last_n->unicode);
6823 evas_textblock_cursor_line_set(Evas_Textblock_Cursor *cur, int line)
6825 Evas_Object_Textblock *o;
6826 Evas_Object_Textblock_Line *ln;
6827 Evas_Object_Textblock_Item *it;
6829 if (!cur) return EINA_FALSE;
6830 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6831 if (!o->formatted.valid) _relayout(cur->obj);
6833 ln = _find_layout_line_num(cur->obj, line);
6834 if (!ln) return EINA_FALSE;
6835 it = (Evas_Object_Textblock_Item *)ln->items;
6838 cur->pos = it->text_pos;
6839 cur->node = it->text_node;
6845 cur->node = o->text_nodes;
6851 evas_textblock_cursor_compare(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
6853 Eina_Inlist *l1, *l2;
6855 if (!cur1) return 0;
6856 if (!cur2) return 0;
6857 if (cur1->obj != cur2->obj) return 0;
6858 if ((!cur1->node) || (!cur2->node)) return 0;
6859 if (cur1->node == cur2->node)
6861 if (cur1->pos < cur2->pos) return -1; /* cur1 < cur2 */
6862 else if (cur1->pos > cur2->pos) return 1; /* cur2 < cur1 */
6865 for (l1 = EINA_INLIST_GET(cur1->node),
6866 l2 = EINA_INLIST_GET(cur1->node); (l1) || (l2);)
6868 if (l1 == EINA_INLIST_GET(cur2->node)) return 1; /* cur2 < cur 1 */
6869 else if (l2 == EINA_INLIST_GET(cur2->node)) return -1; /* cur1 < cur 2 */
6870 else if (!l1) return -1; /* cur1 < cur 2 */
6871 else if (!l2) return 1; /* cur2 < cur 1 */
6879 evas_textblock_cursor_copy(const Evas_Textblock_Cursor *cur, Evas_Textblock_Cursor *cur_dest)
6882 if (!cur_dest) return;
6883 if (cur->obj != cur_dest->obj) return;
6884 cur_dest->pos = cur->pos;
6885 cur_dest->node = cur->node;
6893 * Free a text node. Shouldn't be used usually, it's better to use
6894 * @ref _evas_textblock_node_text_remove for most cases .
6896 * @param n the text node to free
6897 * @see _evas_textblock_node_text_remove
6900 _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n)
6903 eina_ustrbuf_free(n->unicode);
6907 n->par->text_node = NULL;
6913 * Create a new text node
6915 * @return the new text node.
6917 static Evas_Object_Textblock_Node_Text *
6918 _evas_textblock_node_text_new(void)
6920 Evas_Object_Textblock_Node_Text *n;
6922 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Text));
6923 n->unicode = eina_ustrbuf_new();
6924 /* We want to layout each paragraph at least once. */
6925 n->dirty = EINA_TRUE;
6926 n->is_new = EINA_TRUE;
6933 * Break a paragraph. This does not add a PS but only splits the paragraph
6934 * where a ps was just added!
6936 * @param cur the cursor to break at.
6937 * @param fnode the format node of the PS just added.
6938 * @return Returns no value.
6941 _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur,
6942 Evas_Object_Textblock_Node_Format *fnode)
6944 Evas_Object_Textblock *o;
6945 Evas_Object_Textblock_Node_Text *n;
6948 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6950 n = _evas_textblock_node_text_new();
6951 o->text_nodes = _NODE_TEXT(eina_inlist_append_relative(
6952 EINA_INLIST_GET(o->text_nodes),
6954 EINA_INLIST_GET(cur->node)));
6955 /* Handle text and format changes. */
6958 Evas_Object_Textblock_Node_Format *nnode;
6960 const Eina_Unicode *text;
6962 /* If there was a format node in the delete range,
6963 * make it our format and update the text_node fields,
6964 * otherwise, use the paragraph separator
6965 * of the previous paragraph. */
6966 nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
6967 if (nnode && (nnode->text_node == cur->node))
6969 n->format_node = nnode;
6970 nnode->offset--; /* We don't have to take the replacement char
6971 into account anymore */
6972 while (nnode && (nnode->text_node == cur->node))
6974 nnode->text_node = n;
6975 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
6980 n->format_node = fnode;
6983 /* cur->pos now points to the PS, move after. */
6984 start = cur->pos + 1;
6985 len = eina_ustrbuf_length_get(cur->node->unicode) - start;
6988 text = eina_ustrbuf_string_get(cur->node->unicode);
6989 eina_ustrbuf_append_length(n->unicode, text + start, len);
6990 eina_ustrbuf_remove(cur->node->unicode, start, start + len);
6991 cur->node->dirty = EINA_TRUE;
6996 fnode = o->format_nodes;
6999 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->last);
7001 n->format_node = fnode;
7007 * Set the node and offset of all the curs after cur.
7009 * @param cur the cursor.
7010 * @param n the current textblock node.
7011 * @param new_node the new node to set.
7014 _evas_textblock_cursors_set_node(Evas_Object_Textblock *o,
7015 const Evas_Object_Textblock_Node_Text *n,
7016 Evas_Object_Textblock_Node_Text *new_node)
7019 Evas_Textblock_Cursor *data;
7021 if (n == o->cursor->node)
7024 o->cursor->node = new_node;
7026 EINA_LIST_FOREACH(o->cursors, l, data)
7028 if (n == data->node)
7031 data->node = new_node;
7038 * Update the offset of all the cursors after cur.
7040 * @param cur the cursor.
7041 * @param n the current textblock node.
7042 * @param start the starting pos.
7043 * @param offset how much to adjust (can be negative).
7046 _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur,
7047 const Evas_Object_Textblock_Node_Text *n,
7048 size_t start, int offset)
7051 Evas_Textblock_Cursor *data;
7052 Evas_Object_Textblock *o;
7053 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7055 if (cur != o->cursor)
7057 if ((n == o->cursor->node) &&
7058 (o->cursor->pos > start))
7060 if ((offset < 0) && (o->cursor->pos <= (size_t) (-1 * offset)))
7066 o->cursor->pos += offset;
7070 EINA_LIST_FOREACH(o->cursors, l, data)
7074 if ((n == data->node) &&
7075 (data->pos > start))
7077 if ((offset < 0) && (data->pos <= (size_t) (-1 * offset)))
7083 data->pos += offset;
7086 else if (!data->node)
7088 data->node = o->text_nodes;
7097 * Mark that the textblock has changed.
7099 * @param o the textblock object.
7100 * @param obj the evas object.
7103 _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj)
7105 o->formatted.valid = 0;
7106 o->native.valid = 0;
7107 o->content_changed = 1;
7110 free(o->markup_text);
7111 o->markup_text = NULL;
7114 evas_object_change(obj);
7118 _evas_textblock_invalidate_all(Evas_Object_Textblock *o)
7120 Evas_Object_Textblock_Node_Text *n;
7122 EINA_INLIST_FOREACH(o->text_nodes, n)
7124 n->dirty = EINA_TRUE;
7129 evas_textblock_cursor_text_append(Evas_Textblock_Cursor *cur, const char *_text)
7131 Evas_Object_Textblock *o;
7132 Evas_Object_Textblock_Node_Text *n;
7133 Evas_Object_Textblock_Node_Format *fnode = NULL;
7138 text = eina_unicode_utf8_to_unicode(_text, &len);
7139 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7144 Evas_Object_Textblock_Node_Format *nnode;
7145 fnode = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7146 fnode = _evas_textblock_node_format_last_at_off(fnode);
7147 /* find the node after the current in the same paragraph
7148 * either we find one and then take the next, or we try to get
7149 * the first for the paragraph which must be after our position */
7152 if (!evas_textblock_cursor_format_is_visible_get(cur))
7154 nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7155 if (nnode && (nnode->text_node == n))
7167 fnode = n->format_node;
7170 else if (o->text_nodes)
7172 n = cur->node = o->text_nodes;
7177 n = _evas_textblock_node_text_new();
7178 o->text_nodes = _NODE_TEXT(eina_inlist_append(
7179 EINA_INLIST_GET(o->text_nodes),
7180 EINA_INLIST_GET(n)));
7184 eina_ustrbuf_insert_length(n->unicode, text, len, cur->pos);
7185 /* Advance the formats */
7186 if (fnode && (fnode->text_node == cur->node))
7187 fnode->offset += len;
7189 /* Update all the cursors after our position. */
7190 _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, len);
7192 _evas_textblock_changed(o, cur->obj);
7193 n->dirty = EINA_TRUE;
7196 if (!o->cursor->node)
7197 o->cursor->node = o->text_nodes;
7202 evas_textblock_cursor_text_prepend(Evas_Textblock_Cursor *cur, const char *_text)
7205 /*append is essentially prepend without advancing */
7206 len = evas_textblock_cursor_text_append(cur, _text);
7207 cur->pos += len; /*Advance */
7213 * Free a format node
7215 * @param o the textblock object
7216 * @param n the format node to free
7219 _evas_textblock_node_format_free(Evas_Object_Textblock *o,
7220 Evas_Object_Textblock_Node_Format *n)
7223 eina_stringshare_del(n->format);
7224 eina_stringshare_del(n->orig_format);
7225 if (n->anchor == ANCHOR_ITEM)
7226 o->anchors_item = eina_list_remove(o->anchors_item, n);
7227 else if (n->anchor == ANCHOR_A)
7228 o->anchors_a = eina_list_remove(o->anchors_a, n);
7234 * Create a new format node.
7236 * @param format the text to create the format node from.
7237 * @param o the textblock object.
7238 * @return Returns the new format node
7240 static Evas_Object_Textblock_Node_Format *
7241 _evas_textblock_node_format_new(Evas_Object_Textblock *o, const char *_format)
7243 Evas_Object_Textblock_Node_Format *n;
7244 const char *format = _format;
7245 const char *pre_stripped_format = NULL;
7247 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Format));
7248 /* Create orig_format and format */
7249 if (format[0] == '<')
7255 format++; /* Advance after '<' */
7256 format_len = strlen(format);
7257 if ((format_len > 0) && format[format_len - 1] == '>')
7259 format_len--; /* We don't care about '>' */
7260 /* Check if it closes itself. Skip the </> case. */
7261 if ((format_len > 1) && format[format_len - 1] == '/')
7263 format_len--; /* We don't care about '/' */
7264 n->own_closer = EINA_TRUE;
7268 if (!o->style_user || !(match = _style_match_tag(o->style_user, format,
7269 format_len, &replace_len)))
7271 match = _style_match_tag(o->style, format, format_len,
7277 if (match[0] != '-')
7279 n->opener = EINA_TRUE;
7280 if (match[0] != '+')
7282 n->own_closer = EINA_TRUE;
7286 pre_stripped_format = match;
7290 if (format[0] == '/')
7297 n->opener = EINA_TRUE;
7301 n->orig_format = eina_stringshare_add_length(format, format_len);
7303 if (!pre_stripped_format)
7304 pre_stripped_format = n->orig_format;
7306 /* Just use as is, it's a special format. */
7309 const char *tmp = format;
7310 if (format[0] != '-')
7312 n->opener = EINA_TRUE;
7313 if (format[0] != '+')
7315 n->own_closer = EINA_TRUE;
7318 if ((*tmp == '+') || (*tmp == '-'))
7321 while (*tmp == ' ') tmp++;
7323 n->orig_format = eina_stringshare_add(tmp);
7324 pre_stripped_format = n->orig_format;
7329 const char *tmp = pre_stripped_format;
7330 if ((*tmp == '+') || (*tmp == '-'))
7333 while (*tmp == ' ') tmp++;
7335 n->format = eina_stringshare_add(tmp);
7339 _evas_textblock_format_is_visible(n, format);
7340 if (n->anchor == ANCHOR_A)
7342 o->anchors_a = eina_list_append(o->anchors_a, n);
7344 else if (n->anchor == ANCHOR_ITEM)
7346 o->anchors_item = eina_list_append(o->anchors_item, n);
7348 n->is_new = EINA_TRUE;
7354 _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur)
7356 const Eina_Unicode *text;
7358 if (!cur) return EINA_FALSE;
7359 if (!cur->node) return EINA_FALSE;
7360 if (cur->pos < 0) return EINA_FALSE;
7361 text = eina_ustrbuf_string_get(cur->node->unicode);
7362 if ((cur->pos - 1) > eina_ustrbuf_length_get(cur->node->unicode)) return EINA_FALSE;
7363 return ((text[cur->pos] == 0) && (!EINA_INLIST_GET(cur->node)->next)) ?
7364 EINA_TRUE : EINA_FALSE;
7368 evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format)
7370 Evas_Object_Textblock *o;
7371 Evas_Object_Textblock_Node_Format *n;
7372 Eina_Bool is_visible;
7374 if (!cur) return EINA_FALSE;
7375 if ((!format) || (format[0] == 0)) return EINA_FALSE;
7376 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7377 /* We should always have at least one text node */
7380 evas_textblock_cursor_text_prepend(cur, "");
7383 n = _evas_textblock_node_format_new(o, format);
7384 is_visible = n->visible;
7388 o->format_nodes = _NODE_FORMAT(eina_inlist_append(
7389 EINA_INLIST_GET(o->format_nodes),
7390 EINA_INLIST_GET(n)));
7392 n->text_node = (EINA_INLIST_GET(n)->prev) ?
7393 _NODE_FORMAT(EINA_INLIST_GET(n)->prev)->text_node :
7395 cur->node = n->text_node;
7399 Evas_Object_Textblock_Node_Format *fmt;
7400 fmt = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7401 n->text_node = cur->node;
7404 o->format_nodes = _NODE_FORMAT(eina_inlist_prepend(
7405 EINA_INLIST_GET(o->format_nodes),
7406 EINA_INLIST_GET(n)));
7407 n->offset = cur->pos;
7411 if (evas_textblock_cursor_format_is_visible_get(cur))
7413 o->format_nodes = _NODE_FORMAT(eina_inlist_prepend_relative(
7414 EINA_INLIST_GET(o->format_nodes),
7416 EINA_INLIST_GET(fmt)
7418 n->offset = fmt->offset;
7419 if (fmt->text_node->format_node == fmt)
7421 fmt->text_node->format_node = n;
7426 fmt = _evas_textblock_node_format_last_at_off(fmt);
7427 o->format_nodes = _NODE_FORMAT(eina_inlist_append_relative(
7428 EINA_INLIST_GET(o->format_nodes),
7430 EINA_INLIST_GET(fmt)
7432 if (fmt->text_node != cur->node)
7434 n->offset = cur->pos;
7438 n->offset = cur->pos -
7439 _evas_textblock_node_format_pos_get(fmt);
7443 /* Adjust differently if we insert a format char */
7446 _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7451 _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7455 if (!fmt || (fmt->text_node != cur->node))
7457 cur->node->format_node = n;
7460 if (is_visible && cur->node)
7462 Eina_Unicode insert_char;
7463 /* Insert a visual representation according to the type of the
7465 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7466 insert_char = _PARAGRAPH_SEPARATOR;
7467 else if (_IS_LINE_SEPARATOR(format))
7468 insert_char = _NEWLINE;
7469 else if (_IS_TAB(format))
7472 insert_char = _REPLACEMENT_CHAR;
7474 eina_ustrbuf_insert_char(cur->node->unicode, insert_char, cur->pos);
7476 /* Advance all the cursors after our cursor */
7477 _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, 1);
7478 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7480 _evas_textblock_cursor_break_paragraph(cur, n);
7484 /* Handle visible format nodes here */
7485 cur->node->dirty = EINA_TRUE;
7486 n->is_new = EINA_FALSE;
7491 o->format_changed = EINA_TRUE;
7494 _evas_textblock_changed(o, cur->obj);
7496 if (!o->cursor->node)
7497 o->cursor->node = o->text_nodes;
7502 evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor *cur, const char *format)
7504 Eina_Bool is_visible;
7505 /* append is essentially prepend without advancing */
7506 is_visible = evas_textblock_cursor_format_append(cur, format);
7509 /* Advance after the replacement char */
7510 evas_textblock_cursor_char_next(cur);
7518 evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur)
7520 Evas_Object_Textblock *o;
7521 Evas_Object_Textblock_Node_Text *n, *n2;
7522 const Eina_Unicode *text;
7525 if (!cur || !cur->node) return;
7526 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7529 text = eina_ustrbuf_string_get(n->unicode);
7536 if (chr == 0) return;
7538 eina_ustrbuf_remove(n->unicode, cur->pos, ind);
7539 /* Remove a format node if needed, and remove the char only if the
7540 * fmt node is not visible */
7542 Eina_Bool should_merge = EINA_FALSE;
7543 Evas_Object_Textblock_Node_Format *fmt, *fmt2;
7544 fmt = _evas_textblock_cursor_node_format_at_pos_get(cur);
7547 const char *format = NULL;
7548 Evas_Object_Textblock_Node_Format *last_fmt;
7549 /* If there's a PS it must be the last become it delimits paragraphs */
7550 last_fmt = _evas_textblock_node_format_last_at_off(fmt);
7551 format = last_fmt->format;
7552 if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
7554 /* If it was a paragraph separator, we should merge the
7555 * current with the next, there must be a next. */
7556 should_merge = EINA_TRUE;
7558 /* If a singnular, mark as invisible, so we'll delete it. */
7559 if (!format || last_fmt->own_closer)
7561 last_fmt->visible = EINA_FALSE;
7565 fmt2 = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7566 fmt2 = _evas_textblock_node_format_last_at_off(fmt2);
7567 _evas_textblock_node_format_adjust_offset(o, cur->node, fmt2,
7572 _evas_textblock_cursor_nodes_merge(cur);
7575 _evas_textblock_node_format_remove_matching(o, fmt);
7578 if (cur->pos == eina_ustrbuf_length_get(n->unicode))
7580 n2 = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7588 _evas_textblock_cursors_update_offset(cur, n, ppos, -(ind - ppos));
7589 _evas_textblock_changed(o, cur->obj);
7590 cur->node->dirty = EINA_TRUE;
7594 evas_textblock_cursor_range_delete(Evas_Textblock_Cursor *cur1, Evas_Textblock_Cursor *cur2)
7596 Evas_Object_Textblock_Node_Format *fnode = NULL;
7597 Evas_Object_Textblock *o;
7598 Evas_Object_Textblock_Node_Text *n1, *n2;
7599 Eina_Bool should_merge = EINA_FALSE, reset_cursor = EINA_FALSE;
7601 if (!cur1 || !cur1->node) return;
7602 if (!cur2 || !cur2->node) return;
7603 if (cur1->obj != cur2->obj) return;
7604 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7605 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7607 Evas_Textblock_Cursor *tc;
7615 if ((evas_textblock_cursor_compare(o->cursor, cur1) >= 0) &&
7616 (evas_textblock_cursor_compare(cur2, o->cursor) >= 0))
7618 reset_cursor = EINA_TRUE;
7624 Evas_Object_Textblock_Node_Format *remove_format = NULL;
7625 Evas_Object_Textblock_Node_Text *merge_node = NULL;
7626 if ((cur1->pos == 0) &&
7627 (cur2->pos == eina_ustrbuf_length_get(n1->unicode)))
7629 /* Remove the whole node. */
7630 Evas_Object_Textblock_Node_Text *n =
7631 _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7634 should_merge = EINA_TRUE;
7638 n = _NODE_TEXT(EINA_INLIST_GET(n1)->prev);
7642 remove_format = merge_node->format_node;
7646 /* Clear the whole textblock - do it nicer. */
7647 evas_object_textblock_text_markup_set(cur1->obj, "");
7654 should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o,
7655 n1, cur1->pos, cur2->pos);
7657 eina_ustrbuf_remove(n1->unicode, cur1->pos, cur2->pos);
7658 _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos, - (cur2->pos - cur1->pos));
7661 _evas_textblock_node_text_adjust_offsets_to_start(o, n1,
7663 _evas_textblock_nodes_merge(o, merge_node);
7664 evas_textblock_cursor_set_at_format(cur1, remove_format);
7669 Evas_Object_Textblock_Node_Text *n;
7671 _evas_textblock_node_text_adjust_offsets_to_start(o, n1, cur1->pos, -1);
7672 n = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7673 /* Remove all the text nodes between */
7674 while (n && (n != n2))
7676 Evas_Object_Textblock_Node_Text *nnode;
7678 nnode = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7679 _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
7680 _evas_textblock_nodes_merge(o, n1);
7683 should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o, n2,
7686 /* Remove the formats and the strings in the first and last nodes */
7687 len = eina_ustrbuf_length_get(n1->unicode);
7688 eina_ustrbuf_remove(n1->unicode, cur1->pos, len);
7689 eina_ustrbuf_remove(n2->unicode, 0, cur2->pos);
7690 /* Merge the nodes because we removed the PS */
7691 _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos,
7693 _evas_textblock_cursors_update_offset(cur2, cur2->node, 0, -cur2->pos);
7695 _evas_textblock_nodes_merge(o, n1);
7697 fnode = _evas_textblock_cursor_node_format_at_pos_get(cur1);
7701 n1->dirty = n2->dirty = EINA_TRUE;
7705 /* We call this function instead of the cursor one because we already
7706 * updated the cursors */
7707 _evas_textblock_nodes_merge(o, n1);
7709 _evas_textblock_node_format_remove_matching(o, fnode);
7711 evas_textblock_cursor_copy(cur1, cur2);
7713 evas_textblock_cursor_copy(cur1, o->cursor);
7715 _evas_textblock_changed(o, cur1->obj);
7720 evas_textblock_cursor_content_get(const Evas_Textblock_Cursor *cur)
7722 if (!cur || !cur->node) return NULL;
7723 if (evas_textblock_cursor_format_is_visible_get(cur))
7726 Evas_Object_Textblock_Node_Format *fnode;
7728 fnode = _evas_textblock_node_visible_at_pos_get(
7729 evas_textblock_cursor_format_get(cur));
7731 buf = eina_strbuf_new();
7732 _markup_get_format_append(buf, fnode);
7733 ret = eina_strbuf_string_steal(buf);
7734 eina_strbuf_free(buf);
7740 const Eina_Unicode *ustr;
7741 Eina_Unicode buf[2];
7744 ustr = eina_ustrbuf_string_get(cur->node->unicode);
7745 buf[0] = ustr[cur->pos];
7747 s = eina_unicode_unicode_to_utf8(buf, NULL);
7754 _evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7756 Evas_Object_Textblock_Node_Text *tnode;
7758 Evas_Textblock_Cursor *cur2;
7759 buf = eina_strbuf_new();
7761 if (!cur1 || !cur1->node) return NULL;
7762 if (!_cur2 || !_cur2->node) return NULL;
7763 if (cur1->obj != _cur2->obj) return NULL;
7764 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7766 const Evas_Textblock_Cursor *tc;
7772 /* Work on a local copy of the cur */
7773 cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7774 cur2->obj = _cur2->obj;
7775 evas_textblock_cursor_copy(_cur2, cur2);
7777 /* Parse the text between the cursors. */
7778 for (tnode = cur1->node ; tnode ;
7779 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next))
7781 Evas_Object_Textblock_Node_Format *fnode;
7782 Eina_Unicode *text_base, *text;
7786 eina_unicode_strndup(eina_ustrbuf_string_get(tnode->unicode),
7787 eina_ustrbuf_length_get(tnode->unicode));
7788 if (tnode == cur2->node)
7790 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7791 cur1->pos, cur2->pos);
7793 else if (tnode == cur1->node)
7795 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7800 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7803 /* Init the offset so the first one will count starting from cur1->pos
7804 * and not the previous format node */
7805 if (tnode == cur1->node)
7809 off = _evas_textblock_node_format_pos_get(fnode) -
7810 cur1->pos - fnode->offset;
7818 while (fnode && (fnode->text_node == tnode))
7820 Eina_Unicode tmp_ch;
7821 off += fnode->offset;
7822 if ((tnode == cur2->node) &&
7823 ((size_t) (text - text_base + off) >= cur2->pos))
7827 /* No need to skip on the first run */
7829 text[off] = 0; /* Null terminate the part of the string */
7830 _markup_get_text_append(buf, text);
7831 _markup_get_format_append(buf, fnode);
7832 text[off] = tmp_ch; /* Restore the char */
7843 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7845 /* If we got to the last node, stop and add the rest outside */
7846 if (cur2->node == tnode)
7848 /* Add the rest, skip replacement */
7849 /* Don't go past the second cursor pos */
7850 text_base[cur2->pos] = '\0';
7851 _markup_get_text_append(buf, text);
7857 /* Add the rest, skip replacement */
7858 _markup_get_text_append(buf, text);
7862 /* return the string */
7865 ret = eina_strbuf_string_steal(buf);
7866 eina_strbuf_free(buf);
7872 _evas_textblock_cursor_range_text_plain_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7875 Evas_Object_Textblock_Node_Text *n1, *n2;
7876 Evas_Textblock_Cursor *cur2;
7878 buf = eina_ustrbuf_new();
7880 if (!cur1 || !cur1->node) return NULL;
7881 if (!_cur2 || !_cur2->node) return NULL;
7882 if (cur1->obj != _cur2->obj) return NULL;
7883 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7885 const Evas_Textblock_Cursor *tc;
7893 /* Work on a local copy of the cur */
7894 cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7895 cur2->obj = _cur2->obj;
7896 evas_textblock_cursor_copy(_cur2, cur2);
7901 const Eina_Unicode *tmp;
7902 tmp = eina_ustrbuf_string_get(n1->unicode);
7903 eina_ustrbuf_append_length(buf, tmp + cur1->pos, cur2->pos - cur1->pos);
7907 const Eina_Unicode *tmp;
7908 tmp = eina_ustrbuf_string_get(n1->unicode);
7909 eina_ustrbuf_append(buf, tmp + cur1->pos);
7910 n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7913 tmp = eina_ustrbuf_string_get(n1->unicode);
7914 eina_ustrbuf_append_length(buf, tmp,
7915 eina_ustrbuf_length_get(n1->unicode));
7916 n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7918 tmp = eina_ustrbuf_string_get(n2->unicode);
7919 eina_ustrbuf_append_length(buf, tmp, cur2->pos);
7922 /* Free and return */
7925 ret = eina_unicode_unicode_to_utf8(eina_ustrbuf_string_get(buf), NULL);
7926 eina_ustrbuf_free(buf);
7932 evas_textblock_cursor_range_formats_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
7934 Evas_Object *obj = cur1->obj;
7935 Eina_List *ret = NULL;
7936 Evas_Object_Textblock_Node_Text *n1, *n2;
7937 Evas_Object_Textblock_Node_Format *first, *last;
7938 TB_HEAD_RETURN(NULL);
7939 if (!cur1 || !cur1->node) return NULL;
7940 if (!cur2 || !cur2->node) return NULL;
7941 if (cur1->obj != cur2->obj) return NULL;
7942 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7944 const Evas_Textblock_Cursor *tc;
7953 /* FIXME: Change first and last getting to format_before_or_at_pos_get */
7955 last = n2->format_node;
7957 /* If n2->format_node is NULL, we don't have formats in the tb/range. */
7960 /* If the found format is on our text node, we should go to the last
7961 * one, otherwise, the one we found is good enough. */
7962 if (last->text_node == n2)
7964 Evas_Object_Textblock_Node_Format *fnode = last;
7965 while (fnode && (fnode->text_node == n2))
7968 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7972 /* If the first format node is within the range (i.e points to n1) or if
7973 * we have other formats in the range, go through them */
7974 first = n1->format_node;
7975 if ((first->text_node == n1) || (first != last))
7977 Evas_Object_Textblock_Node_Format *fnode = first;
7978 /* Go to the first one in the range */
7979 if (first->text_node != n1)
7981 first = _NODE_FORMAT(EINA_INLIST_GET(first)->next);
7986 ret = eina_list_append(ret, fnode);
7989 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7998 evas_textblock_cursor_range_text_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2, Evas_Textblock_Text_Type format)
8000 if (format == EVAS_TEXTBLOCK_TEXT_MARKUP)
8001 return _evas_textblock_cursor_range_text_markup_get(cur1, cur2);
8002 else if (format == EVAS_TEXTBLOCK_TEXT_PLAIN)
8003 return _evas_textblock_cursor_range_text_plain_get(cur1, cur2);
8005 return NULL; /* Not yet supported */
8009 evas_textblock_cursor_paragraph_text_get(const Evas_Textblock_Cursor *cur)
8011 Evas_Textblock_Cursor cur1, cur2;
8012 if (!cur) return NULL;
8013 if (!cur->node) return NULL;
8014 if (cur->node->utf8)
8016 free(cur->node->utf8);
8018 cur1.obj = cur2.obj = cur->obj;
8019 cur1.node = cur2.node = cur->node;
8020 evas_textblock_cursor_paragraph_char_first(&cur1);
8021 evas_textblock_cursor_paragraph_char_last(&cur2);
8023 cur->node->utf8 = evas_textblock_cursor_range_text_get(&cur1, &cur2,
8024 EVAS_TEXTBLOCK_TEXT_MARKUP);
8025 return cur->node->utf8;
8029 evas_textblock_cursor_paragraph_text_length_get(const Evas_Textblock_Cursor *cur)
8032 if (!cur) return -1;
8033 if (!cur->node) return -1;
8034 len = eina_ustrbuf_length_get(cur->node->unicode);
8036 if (EINA_INLIST_GET(cur->node)->next)
8037 return len - 1; /* Remove the paragraph separator */
8042 EAPI const Evas_Object_Textblock_Node_Format *
8043 evas_textblock_cursor_format_get(const Evas_Textblock_Cursor *cur)
8045 if (!cur) return NULL;
8046 if (!cur->node) return NULL;
8047 return _evas_textblock_cursor_node_format_at_pos_get(cur);
8051 evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format *fmt)
8053 static char *ret = NULL;
8056 if (!fmt) return NULL;
8059 ret = malloc(strlen(fmt->orig_format) + 2 + 1);
8062 if (fmt->opener && !fmt->own_closer)
8067 else if (!fmt->opener)
8072 strcpy(tmp, fmt->orig_format);
8077 evas_textblock_cursor_at_format_set(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *fmt)
8079 if (!fmt || !cur) return;
8080 cur->node = fmt->text_node;
8081 cur->pos = _evas_textblock_node_format_pos_get(fmt);
8085 evas_textblock_cursor_format_is_visible_get(const Evas_Textblock_Cursor *cur)
8087 const Eina_Unicode *text;
8089 if (!cur) return EINA_FALSE;
8090 if (!cur->node) return EINA_FALSE;
8091 if (!evas_textblock_cursor_is_format(cur)) return EINA_FALSE;
8092 text = eina_ustrbuf_string_get(cur->node->unicode);
8093 return EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(text[cur->pos]);
8097 evas_textblock_cursor_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch, Evas_BiDi_Direction *dir, Evas_Textblock_Cursor_Type ctype)
8100 const Evas_Textblock_Cursor *dir_cur;
8101 Evas_Textblock_Cursor cur2;
8102 Evas_Object_Textblock *o;
8103 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8104 if (!o->formatted.valid) _relayout(cur->obj);
8107 if (ctype == EVAS_TEXTBLOCK_CURSOR_UNDER)
8109 ret = evas_textblock_cursor_pen_geometry_get(cur, cx, cy, cw, ch);
8111 else if (ctype == EVAS_TEXTBLOCK_CURSOR_BEFORE)
8113 /* In the case of a "before cursor", we should get the coordinates
8114 * of just after the previous char (which in bidi text may not be
8115 * just before the current char). */
8116 Evas_Coord x, y, h, w;
8117 Evas_Object_Textblock_Node_Format *fmt;
8119 /* If it's at the end of the line, we want to get the position, not
8120 * the position of the previous */
8121 if ((cur->pos > 0) && !_evas_textblock_cursor_is_at_the_end(cur))
8124 Eina_Bool before_char = EINA_FALSE;
8126 cur2.obj = cur->obj;
8127 evas_textblock_cursor_copy(cur, &cur2);
8128 evas_textblock_cursor_char_prev(&cur2);
8130 fmt = _evas_textblock_cursor_node_format_at_pos_get(&cur2);
8132 if (!fmt || !_IS_LINE_SEPARATOR(fmt->format))
8136 before_char = EINA_FALSE;
8142 before_char = EINA_TRUE;
8145 ret = evas_textblock_cursor_pen_geometry_get(
8146 dir_cur, &x, &y, &w, &h);
8148 /* Adjust if the char is an rtl char */
8151 Eina_Bool is_rtl = EINA_FALSE;
8152 if (dir_cur->node->par->is_bidi)
8154 Evas_Object_Textblock_Line *ln;
8155 Evas_Object_Textblock_Item *it;
8156 _find_layout_item_match(dir_cur, &ln, &it);
8157 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8158 (_ITEM_TEXT(it)->text_props.bidi.dir ==
8159 EVAS_BIDI_DIRECTION_RTL))
8161 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8162 (_ITEM_FORMAT(it)->bidi_dir ==
8163 EVAS_BIDI_DIRECTION_RTL))
8167 if ((!before_char && is_rtl) ||
8168 (before_char && !is_rtl))
8170 /* Just don't advance the width */
8176 else if (cur->pos == 0)
8178 ret = evas_textblock_cursor_pen_geometry_get(
8179 dir_cur, &x, &y, &w, &h);
8181 Eina_Bool is_rtl = EINA_FALSE;
8182 if (dir_cur->node && dir_cur->node->par->is_bidi)
8184 Evas_Object_Textblock_Line *ln;
8185 Evas_Object_Textblock_Item *it;
8186 _find_layout_item_match(dir_cur, &ln, &it);
8187 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8188 (_ITEM_TEXT(it)->text_props.bidi.dir ==
8189 EVAS_BIDI_DIRECTION_RTL))
8191 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8192 (_ITEM_FORMAT(it)->bidi_dir ==
8193 EVAS_BIDI_DIRECTION_RTL))
8197 /* Adjust if the char is an rtl char */
8198 if ((ret >= 0) && (!is_rtl))
8200 /* Just don't advance the width */
8207 ret = evas_textblock_cursor_pen_geometry_get(
8208 dir_cur, &x, &y, &w, &h);
8212 if (cx) *cx = x + w;
8219 if (dir && dir_cur && dir_cur->node)
8222 Eina_Bool is_rtl = EINA_FALSE;
8223 if (dir_cur->node->par->is_bidi)
8225 Evas_Object_Textblock_Line *ln;
8226 Evas_Object_Textblock_Item *it;
8227 _find_layout_item_match(dir_cur, &ln, &it);
8228 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8229 (_ITEM_TEXT(it)->text_props.bidi.dir ==
8230 EVAS_BIDI_DIRECTION_RTL))
8232 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8233 (_ITEM_FORMAT(it)->bidi_dir ==
8234 EVAS_BIDI_DIRECTION_RTL))
8238 if (_evas_textblock_cursor_is_at_the_end(dir_cur) && (dir_cur->pos > 0))
8241 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
8243 else if (dir_cur->pos > 0)
8246 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
8251 *dir = EVAS_BIDI_DIRECTION_LTR;
8259 * Returns the geometry/pen position (depending on query_func) of the char
8262 * @param cur the position of the char.
8263 * @param query_func the query function to use.
8264 * @param cx the x of the char (or pen_x in the case of pen position).
8265 * @param cy the y of the char.
8266 * @param cw the w of the char (or advance in the case pen position).
8267 * @param ch the h of the char.
8268 * @return line number of the char on success, -1 on error.
8271 _evas_textblock_cursor_char_pen_geometry_common_get(int (*query_func) (void *data, Evas_Font_Set *font, const Evas_Text_Props *intl_props, int pos, int *cx, int *cy, int *cw, int *ch), const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8273 Evas_Object_Textblock *o;
8274 Evas_Object_Textblock_Line *ln = NULL;
8275 Evas_Object_Textblock_Item *it = NULL;
8276 Evas_Object_Textblock_Text_Item *ti = NULL;
8277 Evas_Object_Textblock_Format_Item *fi = NULL;
8278 int x = 0, y = 0, w = 0, h = 0;
8280 Eina_Bool previous_format;
8282 if (!cur) return -1;
8283 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8284 if (!o->formatted.valid) _relayout(cur->obj);
8290 if (!o->paragraphs) return -1;
8291 ln = o->paragraphs->lines;
8293 if (cx) *cx = ln->x;
8294 if (cy) *cy = ln->par->y + ln->y;
8295 if (cw) *cw = ln->w;
8296 if (ch) *ch = ln->h;
8297 return ln->par->line_no + ln->line_no;
8303 previous_format = _find_layout_item_match(cur, &ln, &it);
8308 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8310 ti = _ITEM_TEXT(it);
8314 fi = _ITEM_FORMAT(it);
8319 pos = cur->pos - ti->parent.text_pos;
8321 if (pos < 0) pos = 0;
8322 if (ti->parent.format->font.font)
8324 query_func(cur->ENDT,
8325 ti->parent.format->font.font,
8331 x += ln->x + _ITEM(ti)->x;
8337 y = ln->par->y + ln->y;
8342 if (previous_format)
8344 if (_IS_LINE_SEPARATOR(fi->item))
8347 y = ln->par->y + ln->y + ln->h;
8352 if (ln->par->direction == EVAS_BIDI_DIRECTION_RTL)
8361 y = ln->par->y + ln->y;
8368 x = ln->x + _ITEM(fi)->x;
8369 y = ln->par->y + ln->y;
8382 return ln->par->line_no + ln->line_no;
8386 evas_textblock_cursor_char_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8388 return _evas_textblock_cursor_char_pen_geometry_common_get(
8389 cur->ENFN->font_char_coords_get, cur, cx, cy, cw, ch);
8393 evas_textblock_cursor_pen_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8395 return _evas_textblock_cursor_char_pen_geometry_common_get(
8396 cur->ENFN->font_pen_coords_get, cur, cx, cy, cw, ch);
8400 evas_textblock_cursor_line_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8402 Evas_Object_Textblock *o;
8403 Evas_Object_Textblock_Line *ln = NULL;
8404 Evas_Object_Textblock_Item *it = NULL;
8407 if (!cur) return -1;
8408 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8409 if (!o->formatted.valid) _relayout(cur->obj);
8412 ln = o->paragraphs->lines;
8416 _find_layout_item_match(cur, &ln, &it);
8420 y = ln->par->y + ln->y;
8427 return ln->par->line_no + ln->line_no;
8431 evas_textblock_cursor_visible_range_get(Evas_Textblock_Cursor *start, Evas_Textblock_Cursor *end)
8435 Evas_Object *obj = start->obj;
8436 TB_HEAD_RETURN(EINA_FALSE);
8437 e = evas_object_evas_get(obj);
8438 cy = 0 - obj->cur.geometry.y;
8440 evas_textblock_cursor_line_coord_set(start, cy);
8441 evas_textblock_cursor_line_coord_set(end, cy + ch);
8442 evas_textblock_cursor_line_char_last(end);
8448 evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
8450 Evas_Object_Textblock *o;
8451 Evas_Object_Textblock_Paragraph *found_par;
8452 Evas_Object_Textblock_Line *ln;
8453 Evas_Object_Textblock_Item *it = NULL;
8455 if (!cur) return EINA_FALSE;
8456 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8457 if (!o->formatted.valid) _relayout(cur->obj);
8458 x += o->style_pad.l;
8459 y += o->style_pad.t;
8461 found_par = _layout_find_paragraph_by_y(o, y);
8464 _layout_paragraph_render(o, found_par);
8465 EINA_INLIST_FOREACH(found_par->lines, ln)
8467 if (ln->par->y + ln->y > y) break;
8468 if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8470 /* If before or after the line, go to start/end according
8471 * to paragraph direction. */
8474 cur->pos = ln->items->text_pos;
8475 cur->node = found_par->text_node;
8476 if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8478 evas_textblock_cursor_line_char_last(cur);
8482 evas_textblock_cursor_line_char_first(cur);
8486 else if (x >= ln->x + ln->w)
8488 cur->pos = ln->items->text_pos;
8489 cur->node = found_par->text_node;
8490 if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8492 evas_textblock_cursor_line_char_first(cur);
8496 evas_textblock_cursor_line_char_last(cur);
8501 EINA_INLIST_FOREACH(ln->items, it)
8503 if (((it->x + ln->x) <= x) && (((it->x + ln->x) + it->adv) > x))
8505 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8509 Evas_Object_Textblock_Text_Item *ti;
8510 ti = _ITEM_TEXT(it);
8513 if (ti->parent.format->font.font)
8514 pos = cur->ENFN->font_char_at_coords_get(
8516 ti->parent.format->font.font,
8518 x - it->x - ln->x, 0,
8519 &cx, &cy, &cw, &ch);
8522 cur->pos = pos + it->text_pos;
8523 cur->node = it->text_node;
8528 Evas_Object_Textblock_Format_Item *fi;
8529 fi = _ITEM_FORMAT(it);
8530 cur->pos = fi->parent.text_pos;
8531 cur->node = found_par->text_node;
8539 else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8541 /* If we are after the last paragraph, use the last position in the
8543 evas_textblock_cursor_paragraph_last(cur);
8546 else if (o->paragraphs && (y < o->paragraphs->y))
8548 evas_textblock_cursor_paragraph_first(cur);
8556 evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
8558 Evas_Object_Textblock *o;
8559 Evas_Object_Textblock_Paragraph *found_par;
8560 Evas_Object_Textblock_Line *ln;
8562 if (!cur) return -1;
8563 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8564 if (!o->formatted.valid) _relayout(cur->obj);
8565 y += o->style_pad.t;
8567 found_par = _layout_find_paragraph_by_y(o, y);
8571 _layout_paragraph_render(o, found_par);
8572 EINA_INLIST_FOREACH(found_par->lines, ln)
8574 if (ln->par->y + ln->y > y) break;
8575 if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8577 evas_textblock_cursor_line_set(cur, ln->par->line_no +
8579 return ln->par->line_no + ln->line_no;
8583 else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8586 /* If we are after the last paragraph, use the last position in the
8588 evas_textblock_cursor_paragraph_last(cur);
8589 if (cur->node && cur->node->par)
8591 line_no = cur->node->par->line_no;
8592 if (cur->node->par->lines)
8594 line_no += ((Evas_Object_Textblock_Line *)
8595 EINA_INLIST_GET(cur->node->par->lines)->last)->line_no;
8600 else if (o->paragraphs && (y < o->paragraphs->y))
8603 evas_textblock_cursor_paragraph_first(cur);
8604 if (cur->node && cur->node->par)
8606 line_no = cur->node->par->line_no;
8615 * Updates x and w according to the text direction, position in text and
8616 * if it's a special case switch
8618 * @param ti the text item we are working on
8619 * @param x the current x (we get) and the x we return
8620 * @param w the current w (we get) and the w we return
8621 * @param start if this is the first item or not
8622 * @param switch_items toogles item switching (rtl cases)
8625 _evas_textblock_range_calc_x_w(const Evas_Object_Textblock_Item *it,
8626 Evas_Coord *x, Evas_Coord *w, Eina_Bool start, Eina_Bool switch_items)
8628 if ((start && !switch_items) || (!start && switch_items))
8631 if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8632 _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8634 ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8635 _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8649 if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8650 _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8652 ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8653 _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8670 * Returns the geometry of the range in line ln. Cur1 is the start cursor,
8671 * cur2 is the end cursor, NULL means from the start or to the end accordingly.
8672 * Assumes that ln is valid, and that at least one of cur1 and cur2 is not NULL.
8674 * @param ln the line to work on.
8675 * @param cur1 the start cursor
8676 * @param cur2 the end cursor
8677 * @return Returns the geometry of the range
8680 _evas_textblock_cursor_range_in_line_geometry_get(
8681 const Evas_Object_Textblock_Line *ln, const Evas_Textblock_Cursor *cur1,
8682 const Evas_Textblock_Cursor *cur2)
8684 Evas_Object_Textblock_Item *it;
8685 Evas_Object_Textblock_Item *it1, *it2;
8686 Eina_List *rects = NULL;
8687 Evas_Textblock_Rectangle *tr;
8689 Eina_Bool switch_items;
8690 const Evas_Textblock_Cursor *cur;
8692 cur = (cur1) ? cur1 : cur2;
8694 if (!cur) return NULL;
8696 /* Find the first and last items */
8699 EINA_INLIST_FOREACH(ln->items, it)
8702 item_len = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
8703 _ITEM_TEXT(it)->text_props.text_len
8705 if ((!cur1 || (cur1->pos < it->text_pos + item_len)) &&
8706 (!cur2 || (cur2->pos >= it->text_pos)))
8711 start = item_len; /* start stores the first item_len */
8714 end = item_len; /* end stores the last item_len */
8718 /* If we couldn't find even one item, return */
8719 if (!it1) return NULL;
8721 /* If the first item is logically before or equal the second item
8722 * we have to set start and end differently than in the other case */
8723 if (it1->text_pos <= it2->text_pos)
8725 start = (cur1) ? (cur1->pos - it1->text_pos) : 0;
8726 end = (cur2) ? (cur2->pos - it2->text_pos) : end;
8727 switch_items = EINA_FALSE;
8731 start = (cur2) ? (cur2->pos - it1->text_pos) : start;
8732 end = (cur1) ? (cur1->pos - it2->text_pos) : 0;
8733 switch_items = EINA_TRUE;
8736 /* IMPORTANT: Don't use cur1/cur2 past this point (because they probably
8737 * don't make sense anymore. That's why there are start and end),
8738 * unless you know what you are doing */
8740 /* Special case when they share the same item and it's a text item */
8741 if ((it1 == it2) && (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT))
8743 Evas_Coord x1, w1, x2, w2;
8744 Evas_Coord x, w, y, h;
8745 Evas_Object_Textblock_Text_Item *ti;
8748 ti = _ITEM_TEXT(it1);
8749 if (ti->parent.format->font.font)
8751 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8752 ti->parent.format->font.font,
8761 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8762 ti->parent.format->font.font,
8771 /* Make x2 the one on the right */
8785 if (ti->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8798 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8799 rects = eina_list_append(rects, tr);
8800 tr->x = ln->x + it1->x + x;
8801 tr->y = ln->par->y + ln->y;
8806 else if (it1 != it2)
8808 /* Get the middle items */
8809 Evas_Coord min_x, max_x;
8811 it = _ITEM(EINA_INLIST_GET(it1)->next);
8812 min_x = max_x = it->x;
8814 if (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8817 Evas_Object_Textblock_Text_Item *ti;
8819 ti = _ITEM_TEXT(it1);
8821 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8822 ti->parent.format->font.font,
8828 /* BUG! Skip the first item */
8833 _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8841 _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8846 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8847 rects = eina_list_append(rects, tr);
8848 tr->x = ln->x + it1->x + x;
8849 tr->y = ln->par->y + ln->y;
8854 while (it && (it != it2))
8856 max_x = it->x + it->adv;
8857 it = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(it)->next;
8861 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8862 rects = eina_list_append(rects, tr);
8863 tr->x = ln->x + min_x;
8864 tr->y = ln->par->y + ln->y;
8866 tr->w = max_x - min_x;
8868 if (it2->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8871 Evas_Object_Textblock_Text_Item *ti;
8873 ti = _ITEM_TEXT(it2);
8875 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8876 ti->parent.format->font.font,
8882 /* BUG! skip the last item */
8887 _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8895 _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8900 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8901 rects = eina_list_append(rects, tr);
8902 tr->x = ln->x + it2->x + x;
8903 tr->y = ln->par->y + ln->y;
8912 evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
8914 Evas_Object_Textblock *o;
8915 Evas_Object_Textblock_Line *ln1, *ln2;
8916 Evas_Object_Textblock_Item *it1, *it2;
8917 Eina_List *rects = NULL;
8918 Evas_Textblock_Rectangle *tr;
8920 if (!cur1 || !cur1->node) return NULL;
8921 if (!cur2 || !cur2->node) return NULL;
8922 if (cur1->obj != cur2->obj) return NULL;
8923 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
8924 if (!o->formatted.valid) _relayout(cur1->obj);
8925 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
8927 const Evas_Textblock_Cursor *tc;
8936 _find_layout_item_match(cur1, &ln1, &it1);
8937 if (!ln1 || !it1) return NULL;
8938 _find_layout_item_match(cur2, &ln2, &it2);
8939 if (!ln2 || !it2) return NULL;
8943 rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8948 Evas_Object_Textblock_Line *plni, *lni;
8949 Eina_List *rects2 = NULL;
8950 /* Handle the first line */
8951 rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8954 /* Handle the lines between the first and the last line */
8955 lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(ln1)->next;
8956 if (!lni && (ln1->par != ln2->par))
8958 lni = ((Evas_Object_Textblock_Paragraph *)
8959 EINA_INLIST_GET(ln1->par)->next)->lines;
8961 while (lni && (lni != ln2))
8963 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8964 rects = eina_list_append(rects, tr);
8966 tr->y = lni->par->y + lni->y;
8970 lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(lni)->next;
8971 if (!lni && (plni->par != ln2->par))
8973 lni = ((Evas_Object_Textblock_Paragraph *)
8974 EINA_INLIST_GET(plni->par)->next)->lines;
8977 rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2,
8979 rects = eina_list_merge(rects, rects2);
8985 evas_textblock_cursor_format_item_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8987 Evas_Object_Textblock *o;
8988 Evas_Object_Textblock_Line *ln = NULL;
8989 Evas_Object_Textblock_Format_Item *fi;
8990 Evas_Object_Textblock_Item *it = NULL;
8991 Evas_Coord x, y, w, h;
8993 if (!cur || !evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
8994 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8995 if (!o->formatted.valid) _relayout(cur->obj);
8996 if (!evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
8997 _find_layout_item_line_match(cur->obj, cur->node, cur->pos, &ln, &it);
8998 fi = _ITEM_FORMAT(it);
8999 if ((!ln) || (!fi)) return EINA_FALSE;
9000 x = ln->x + fi->parent.x;
9001 y = ln->par->y + ln->y + ln->baseline + fi->y;
9012 evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur)
9014 Eina_Bool ret = EINA_FALSE;
9015 Evas_Textblock_Cursor cur2;
9016 if (!cur) return EINA_FALSE;
9018 cur2.obj = cur->obj;
9019 evas_textblock_cursor_copy(cur, &cur2);
9020 evas_textblock_cursor_line_char_last(&cur2);
9021 if (cur2.pos == cur->pos)
9028 /* general controls */
9030 evas_object_textblock_line_number_geometry_get(const Evas_Object *obj, int line, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
9032 Evas_Object_Textblock_Line *ln;
9035 ln = _find_layout_line_num(obj, line);
9036 if (!ln) return EINA_FALSE;
9037 if (cx) *cx = ln->x;
9038 if (cy) *cy = ln->par->y + ln->y;
9039 if (cw) *cw = ln->w;
9040 if (ch) *ch = ln->h;
9045 evas_object_textblock_clear(Evas_Object *obj)
9048 Evas_Textblock_Cursor *cur;
9053 _paragraphs_free(obj, o->paragraphs);
9054 o->paragraphs = NULL;
9058 o->cursor->node = NULL;
9060 EINA_LIST_FOREACH(o->cursors, l, cur)
9066 _evas_textblock_changed(o, obj);
9070 evas_object_textblock_size_formatted_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
9073 if (!o->formatted.valid) _relayout(obj);
9074 if (w) *w = o->formatted.w;
9075 if (h) *h = o->formatted.h;
9079 _size_native_calc_line_finalize(const Evas_Object *obj, Eina_List *items,
9080 Evas_Coord *ascent, Evas_Coord *descent, Evas_Coord *w)
9082 Evas_Object_Textblock_Item *it;
9085 it = eina_list_data_get(items);
9090 /* If there are no text items yet, calc ascent/descent
9091 * according to the current format. */
9092 if (*ascent + *descent == 0)
9093 _layout_format_ascent_descent_adjust(obj, ascent, descent,
9098 *w = it->format->margin.l + it->format->margin.r;
9102 /* Adjust all the item sizes according to the final line size,
9103 * and update the x positions of all the items of the line. */
9104 EINA_LIST_FOREACH(items, i, it)
9106 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
9108 Evas_Coord fw, fh, fy;
9110 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
9111 if (!fi->formatme) goto loop_advance;
9112 _layout_calculate_format_item_size(obj, fi, ascent,
9113 descent, &fy, &fw, &fh);
9123 _size_native_calc_paragraph_size(const Evas_Object *obj,
9124 const Evas_Object_Textblock *o,
9125 const Evas_Object_Textblock_Paragraph *par,
9126 Evas_Coord *_w, Evas_Coord *_h)
9129 Evas_Object_Textblock_Item *it;
9130 Eina_List *line_items = NULL;
9131 Evas_Coord w = 0, y = 0, wmax = 0, h = 0, ascent = 0, descent = 0;
9133 EINA_LIST_FOREACH(par->logical_items, i, it)
9135 line_items = eina_list_append(line_items, it);
9136 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
9138 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
9139 if (fi->item && (_IS_LINE_SEPARATOR(fi->item) ||
9140 _IS_PARAGRAPH_SEPARATOR(o, fi->item)))
9142 _size_native_calc_line_finalize(obj, line_items, &ascent,
9145 if (ascent + descent > h)
9146 h = ascent + descent;
9152 ascent = descent = 0;
9153 line_items = eina_list_free(line_items);
9157 Evas_Coord fw, fh, fy;
9158 /* If there are no text items yet, calc ascent/descent
9159 * according to the current format. */
9160 if (it && (ascent + descent == 0))
9161 _layout_format_ascent_descent_adjust(obj, &ascent,
9162 &descent, it->format);
9164 _layout_calculate_format_item_size(obj, fi, &ascent,
9165 &descent, &fy, &fw, &fh);
9170 _layout_format_ascent_descent_adjust(obj, &ascent,
9171 &descent, it->format);
9175 _size_native_calc_line_finalize(obj, line_items, &ascent, &descent, &w);
9177 line_items = eina_list_free(line_items);
9179 /* Do the last addition */
9180 if (ascent + descent > h)
9181 h = ascent + descent;
9191 evas_object_textblock_size_native_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
9194 if (!o->native.valid)
9196 Evas_Coord wmax = 0, hmax = 0;
9197 Evas_Object_Textblock_Paragraph *par;
9198 /* We just want the layout objects to update, should probably
9200 if (!o->formatted.valid) _relayout(obj);
9201 EINA_INLIST_FOREACH(o->paragraphs, par)
9204 _size_native_calc_paragraph_size(obj, o, par, &tw, &th);
9213 o->native.valid = 1;
9214 o->content_changed = 0;
9215 o->format_changed = EINA_FALSE;
9217 if (w) *w = o->native.w;
9218 if (h) *h = o->native.h;
9222 evas_object_textblock_style_insets_get(const Evas_Object *obj, Evas_Coord *l, Evas_Coord *r, Evas_Coord *t, Evas_Coord *b)
9225 if (!o->formatted.valid) _relayout(obj);
9226 if (l) *l = o->style_pad.l;
9227 if (r) *r = o->style_pad.r;
9228 if (t) *t = o->style_pad.t;
9229 if (b) *b = o->style_pad.b;
9233 * FIXME: DELETE ME! DELETE ME!
9234 * This is an ugly workaround to get around the fact that
9235 * evas_object_textblock_coords_recalc isn't really called when it's supposed
9236 * to. When that bug is fixed please remove this. */
9238 _workaround_object_coords_recalc(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
9240 evas_object_textblock_coords_recalc(obj);
9243 /* all nice and private */
9245 evas_object_textblock_init(Evas_Object *obj)
9247 Evas_Object_Textblock *o;
9248 #ifdef HAVE_LINEBREAK
9249 static Eina_Bool linebreak_init = EINA_FALSE;
9250 if (!linebreak_init)
9252 linebreak_init = EINA_TRUE;
9258 /* alloc image ob, setup methods and default values */
9259 obj->object_data = evas_object_textblock_new();
9260 /* set up default settings for this kind of object */
9261 obj->cur.color.r = 255;
9262 obj->cur.color.g = 255;
9263 obj->cur.color.b = 255;
9264 obj->cur.color.a = 255;
9265 obj->cur.geometry.x = 0.0;
9266 obj->cur.geometry.y = 0.0;
9267 obj->cur.geometry.w = 0.0;
9268 obj->cur.geometry.h = 0.0;
9270 /* set up object-specific settings */
9271 obj->prev = obj->cur;
9272 /* set up methods (compulsory) */
9273 obj->func = &object_func;
9276 o = (Evas_Object_Textblock *)(obj->object_data);
9277 o->cursor->obj = obj;
9278 o->legacy_newline = EINA_TRUE;
9279 evas_object_event_callback_priority_add(obj, EVAS_CALLBACK_RESIZE, -1000,
9280 _workaround_object_coords_recalc, NULL);
9284 evas_object_textblock_new(void)
9286 Evas_Object_Textblock *o;
9288 /* alloc obj private data */
9289 EVAS_MEMPOOL_INIT(_mp_obj, "evas_object_textblock", Evas_Object_Textblock, 64, NULL);
9290 o = EVAS_MEMPOOL_ALLOC(_mp_obj, Evas_Object_Textblock);
9291 if (!o) return NULL;
9292 EVAS_MEMPOOL_PREP(_mp_obj, o, Evas_Object_Textblock);
9293 o->magic = MAGIC_OBJ_TEXTBLOCK;
9294 o->cursor = calloc(1, sizeof(Evas_Textblock_Cursor));
9295 _format_command_init();
9300 evas_object_textblock_free(Evas_Object *obj)
9302 Evas_Object_Textblock *o;
9304 evas_object_textblock_clear(obj);
9305 evas_object_textblock_style_set(obj, NULL);
9306 while (evas_object_textblock_style_user_peek(obj))
9308 evas_object_textblock_style_user_pop(obj);
9310 o = (Evas_Object_Textblock *)(obj->object_data);
9314 Evas_Textblock_Cursor *cur;
9316 cur = (Evas_Textblock_Cursor *)o->cursors->data;
9317 o->cursors = eina_list_remove_list(o->cursors, o->cursors);
9320 if (o->repch) eina_stringshare_del(o->repch);
9321 if (o->ellip_ti) _item_free(obj, NULL, _ITEM(o->ellip_ti));
9323 EVAS_MEMPOOL_FREE(_mp_obj, o);
9324 _format_command_shutdown();
9329 evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y)
9331 Evas_Object_Textblock_Paragraph *par, *start = NULL;
9332 Evas_Object_Textblock_Line *ln;
9333 Evas_Object_Textblock *o;
9335 int cx, cy, cw, ch, clip;
9336 const char vals[5][5] =
9345 /* render object to surface with context, and offxet by x,y */
9346 o = (Evas_Object_Textblock *)(obj->object_data);
9347 obj->layer->evas->engine.func->context_multiplier_unset(output,
9349 /* FIXME: This clipping is just until we fix inset handling correctly. */
9350 ENFN->context_clip_clip(output, context,
9351 obj->cur.geometry.x + x,
9352 obj->cur.geometry.y + y,
9353 obj->cur.geometry.w,
9354 obj->cur.geometry.h);
9355 clip = ENFN->context_clip_get(output, context, &cx, &cy, &cw, &ch);
9356 /* If there are no paragraphs and thus there are no lines,
9357 * there's nothing left to do. */
9358 if (!o->paragraphs) return;
9360 #define ITEM_WALK() \
9361 EINA_INLIST_FOREACH(start, par) \
9363 if (!par->visible) continue; \
9366 if ((obj->cur.geometry.y + y + par->y + par->h) < (cy - 20)) \
9368 if ((obj->cur.geometry.y + y + par->y) > (cy + ch + 20)) \
9371 _layout_paragraph_render(o, par); \
9372 EINA_INLIST_FOREACH(par->lines, ln) \
9374 Evas_Object_Textblock_Item *itr; \
9378 if ((obj->cur.geometry.y + y + par->y + ln->y + ln->h) < (cy - 20)) \
9380 if ((obj->cur.geometry.y + y + par->y + ln->y) > (cy + ch + 20)) \
9383 EINA_INLIST_FOREACH(ln->items, itr) \
9386 yoff = ln->baseline; \
9387 if (itr->format->valign != -1.0) \
9389 yoff += itr->format->valign * (ln->h - itr->h); \
9393 if ((obj->cur.geometry.x + x + ln->x + itr->x + itr->w) < (cx - 20)) \
9395 if ((obj->cur.geometry.x + x + ln->x + itr->x) > (cx + cw + 20)) \
9398 if ((ln->x + itr->x + itr->w) <= 0) continue; \
9399 if (ln->x + itr->x > obj->cur.geometry.w) break; \
9402 #define ITEM_WALK_END() \
9408 #define COLOR_SET(col) \
9409 ENFN->context_color_set(output, context, \
9410 (obj->cur.cache.clip.r * ti->parent.format->color.col.r) / 255, \
9411 (obj->cur.cache.clip.g * ti->parent.format->color.col.g) / 255, \
9412 (obj->cur.cache.clip.b * ti->parent.format->color.col.b) / 255, \
9413 (obj->cur.cache.clip.a * ti->parent.format->color.col.a) / 255);
9414 #define COLOR_SET_AMUL(col, amul) \
9415 ENFN->context_color_set(output, context, \
9416 (obj->cur.cache.clip.r * ti->parent.format->color.col.r * (amul)) / 65025, \
9417 (obj->cur.cache.clip.g * ti->parent.format->color.col.g * (amul)) / 65025, \
9418 (obj->cur.cache.clip.b * ti->parent.format->color.col.b * (amul)) / 65025, \
9419 (obj->cur.cache.clip.a * ti->parent.format->color.col.a * (amul)) / 65025);
9420 #define DRAW_TEXT(ox, oy) \
9421 if (ti->parent.format->font.font) ENFN->font_draw(output, context, surface, ti->parent.format->font.font, \
9422 obj->cur.geometry.x + ln->x + ti->parent.x + x + (ox), \
9423 obj->cur.geometry.y + ln->par->y + ln->y + yoff + y + (oy), \
9424 ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h, \
9428 #define DRAW_RECT(ox, oy, ow, oh, or, og, ob, oa) \
9431 ENFN->context_color_set(output, \
9433 (obj->cur.cache.clip.r * or) / 255, \
9434 (obj->cur.cache.clip.g * og) / 255, \
9435 (obj->cur.cache.clip.b * ob) / 255, \
9436 (obj->cur.cache.clip.a * oa) / 255); \
9437 ENFN->rectangle_draw(output, \
9440 obj->cur.geometry.x + ln->x + x + (ox), \
9441 obj->cur.geometry.y + ln->par->y + ln->y + y + (oy), \
9447 #define DRAW_FORMAT_DASHED(oname, oy, oh, dw, dp) \
9450 if (itr->format->oname) \
9452 unsigned char _or, _og, _ob, _oa; \
9453 int _ind, _dx = 0, _dn, _dr; \
9454 _or = itr->format->color.oname.r; \
9455 _og = itr->format->color.oname.g; \
9456 _ob = itr->format->color.oname.b; \
9457 _oa = itr->format->color.oname.a; \
9458 if (!EINA_INLIST_GET(itr)->next) \
9460 _dn = itr->w / (dw + dp); \
9461 _dr = itr->w % (dw + dp); \
9465 _dn = itr->adv / (dw + dp); \
9466 _dr = itr->adv % (dw + dp); \
9468 if (_dr > dw) _dr = dw; \
9469 for (_ind = 0 ; _ind < _dn ; _ind++) \
9471 DRAW_RECT(itr->x + _dx, oy, dw, oh, _or, _og, _ob, _oa); \
9474 DRAW_RECT(itr->x + _dx, oy, _dr, oh, _or, _og, _ob, _oa); \
9479 #define DRAW_FORMAT(oname, oy, oh) \
9482 if (itr->format->oname) \
9484 unsigned char _or, _og, _ob, _oa; \
9485 _or = itr->format->color.oname.r; \
9486 _og = itr->format->color.oname.g; \
9487 _ob = itr->format->color.oname.b; \
9488 _oa = itr->format->color.oname.a; \
9489 if (!EINA_INLIST_GET(itr)->next) \
9491 DRAW_RECT(itr->x, oy, itr->w, oh, _or, _og, _ob, _oa); \
9495 DRAW_RECT(itr->x, oy, itr->adv, oh, _or, _og, _ob, _oa); \
9502 Evas_Coord look_for_y = 0 - (obj->cur.geometry.y + y);
9505 Evas_Coord tmp_lfy = cy - (obj->cur.geometry.y + y);
9506 if (tmp_lfy > look_for_y)
9507 look_for_y = tmp_lfy;
9510 if (look_for_y >= 0)
9511 start = _layout_find_paragraph_by_y(o, look_for_y);
9514 start = o->paragraphs;
9519 DRAW_FORMAT(backing, 0, ln->h);
9523 /* There are size adjustments that depend on the styles drawn here back
9524 * in "_text_item_update_sizes" should not modify one without the other. */
9526 /* prepare everything for text draw */
9531 int shad_dst, shad_sz, dx, dy, haveshad;
9532 Evas_Object_Textblock_Text_Item *ti;
9533 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9536 shad_dst = shad_sz = dx = dy = haveshad = 0;
9537 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
9539 case EVAS_TEXT_STYLE_SHADOW:
9540 case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
9544 case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
9545 case EVAS_TEXT_STYLE_FAR_SHADOW:
9549 case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
9554 case EVAS_TEXT_STYLE_SOFT_SHADOW:
9566 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
9568 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
9572 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
9576 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
9580 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
9584 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
9588 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
9592 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
9596 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
9612 for (j = 0; j < 5; j++)
9614 for (i = 0; i < 5; i++)
9616 if (vals[i][j] != 0)
9618 COLOR_SET_AMUL(shadow, vals[i][j] * 50);
9619 DRAW_TEXT(i - 2 + dx, j - 2 + dy);
9634 Evas_Object_Textblock_Text_Item *ti;
9635 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9638 if (ti->parent.format->style == EVAS_TEXT_STYLE_GLOW)
9640 for (j = 0; j < 5; j++)
9642 for (i = 0; i < 5; i++)
9644 if (vals[i][j] != 0)
9646 COLOR_SET_AMUL(glow, vals[i][j] * 50);
9647 DRAW_TEXT(i - 2, j - 2);
9663 Evas_Object_Textblock_Text_Item *ti;
9664 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9667 if ((ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE) ||
9668 (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
9669 (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW))
9677 else if (ti->parent.format->style == EVAS_TEXT_STYLE_SOFT_OUTLINE)
9679 for (j = 0; j < 5; j++)
9681 for (i = 0; i < 5; i++)
9683 if (((i != 2) || (j != 2)) && (vals[i][j] != 0))
9685 COLOR_SET_AMUL(outline, vals[i][j] * 50);
9686 DRAW_TEXT(i - 2, j - 2);
9694 /* normal text and lines */
9697 Evas_Object_Textblock_Text_Item *ti;
9698 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9707 DRAW_FORMAT(strikethrough, (ln->h / 2), 1);
9710 DRAW_FORMAT(underline, ln->baseline + 1, 1);
9712 /* UNDERLINE DASHED */
9713 DRAW_FORMAT_DASHED(underline_dash, ln->baseline + 1, 1,
9714 ti->parent.format->underline_dash_width,
9715 ti->parent.format->underline_dash_gap);
9718 DRAW_FORMAT(underline2, ln->baseline + 3, 1);
9724 evas_object_textblock_render_pre(Evas_Object *obj)
9726 Evas_Object_Textblock *o;
9729 /* dont pre-render the obj twice! */
9730 if (obj->pre_render_done) return;
9731 obj->pre_render_done = 1;
9732 /* pre-render phase. this does anything an object needs to do just before */
9733 /* rendering. this could mean loading the image data, retrieving it from */
9734 /* elsewhere, decoding video etc. */
9735 /* then when this is done the object needs to figure if it changed and */
9736 /* if so what and where and add the appropriate redraw textblocks */
9737 o = (Evas_Object_Textblock *)(obj->object_data);
9738 if ((o->changed) || (o->content_changed) || (o->format_changed) ||
9739 ((obj->cur.geometry.w != o->last_w) ||
9740 (((o->valign != 0.0) || (o->have_ellipsis)) &&
9741 (obj->cur.geometry.h != o->last_h))))
9745 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9746 is_v = evas_object_is_visible(obj);
9747 was_v = evas_object_was_visible(obj);
9753 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9754 is_v = evas_object_is_visible(obj);
9755 was_v = evas_object_was_visible(obj);
9758 /* if someone is clipping this obj - go calculate the clipper */
9759 if (obj->cur.clipper)
9761 if (obj->cur.cache.clip.dirty)
9762 evas_object_clip_recalc(obj->cur.clipper);
9763 obj->cur.clipper->func->render_pre(obj->cur.clipper);
9765 /* now figure what changed and add draw rects */
9766 /* if it just became visible or invisible */
9767 is_v = evas_object_is_visible(obj);
9768 was_v = evas_object_was_visible(obj);
9771 evas_object_render_pre_visible_change(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9774 if ((obj->cur.map != obj->prev.map) ||
9775 (obj->cur.usemap != obj->prev.usemap))
9777 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9780 /* it's not visible - we accounted for it appearing or not so just abort */
9781 if (!is_v) goto done;
9782 /* clipper changed this is in addition to anything else for obj */
9783 evas_object_render_pre_clipper_change(&obj->layer->evas->clip_changes, obj);
9784 /* if we restacked (layer or just within a layer) and don't clip anyone */
9787 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9790 /* if it changed color */
9791 if ((obj->cur.color.r != obj->prev.color.r) ||
9792 (obj->cur.color.g != obj->prev.color.g) ||
9793 (obj->cur.color.b != obj->prev.color.b) ||
9794 (obj->cur.color.a != obj->prev.color.a))
9796 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9799 /* if it changed geometry - and obviously not visibility or color */
9800 /* calculate differences since we have a constant color fill */
9801 /* we really only need to update the differences */
9802 if ((obj->cur.geometry.x != obj->prev.geometry.x) ||
9803 (obj->cur.geometry.y != obj->prev.geometry.y) ||
9804 (obj->cur.geometry.w != obj->prev.geometry.w) ||
9805 (obj->cur.geometry.h != obj->prev.geometry.h))
9807 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9811 evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9815 evas_object_textblock_render_post(Evas_Object *obj)
9817 /* Evas_Object_Textblock *o; */
9819 /* this moves the current data to the previous state parts of the object */
9820 /* in whatever way is safest for the object. also if we don't need object */
9821 /* data anymore we can free it if the object deems this is a good idea */
9822 /* o = (Evas_Object_Textblock *)(obj->object_data); */
9823 /* remove those pesky changes */
9824 evas_object_clip_changes_clean(obj);
9825 /* move cur to prev safely for object data */
9826 obj->prev = obj->cur;
9827 /* o->prev = o->cur; */
9828 /* o->changed = 0; */
9831 static unsigned int evas_object_textblock_id_get(Evas_Object *obj)
9833 Evas_Object_Textblock *o;
9835 o = (Evas_Object_Textblock *)(obj->object_data);
9837 return MAGIC_OBJ_TEXTBLOCK;
9840 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj)
9842 Evas_Object_Textblock *o;
9844 o = (Evas_Object_Textblock *)(obj->object_data);
9846 return MAGIC_OBJ_CUSTOM;
9849 static void *evas_object_textblock_engine_data_get(Evas_Object *obj)
9851 Evas_Object_Textblock *o;
9853 o = (Evas_Object_Textblock *)(obj->object_data);
9854 if (!o) return NULL;
9855 return o->engine_data;
9859 evas_object_textblock_is_opaque(Evas_Object *obj __UNUSED__)
9861 /* this returns 1 if the internal object data implies that the object is */
9862 /* currently fulyl opque over the entire gradient it occupies */
9867 evas_object_textblock_was_opaque(Evas_Object *obj __UNUSED__)
9869 /* this returns 1 if the internal object data implies that the object was */
9870 /* currently fulyl opque over the entire gradient it occupies */
9875 evas_object_textblock_coords_recalc(Evas_Object *obj)
9877 Evas_Object_Textblock *o;
9879 o = (Evas_Object_Textblock *)(obj->object_data);
9880 if ((obj->cur.geometry.w != o->last_w) ||
9881 (((o->valign != 0.0) || (o->have_ellipsis)) &&
9882 (obj->cur.geometry.h != o->last_h)))
9884 o->formatted.valid = 0;
9890 evas_object_textblock_scale_update(Evas_Object *obj)
9892 Evas_Object_Textblock *o;
9894 o = (Evas_Object_Textblock *)(obj->object_data);
9895 _evas_textblock_invalidate_all(o);
9896 _evas_textblock_changed(o, obj);
9900 _evas_object_textblock_rehint(Evas_Object *obj)
9902 Evas_Object_Textblock *o;
9903 Evas_Object_Textblock_Paragraph *par;
9904 Evas_Object_Textblock_Line *ln;
9906 o = (Evas_Object_Textblock *)(obj->object_data);
9907 EINA_INLIST_FOREACH(o->paragraphs, par)
9909 EINA_INLIST_FOREACH(par->lines, ln)
9911 Evas_Object_Textblock_Item *it;
9913 EINA_INLIST_FOREACH(ln->items, it)
9915 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9917 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
9918 if (ti->parent.format->font.font)
9920 evas_font_load_hinting_set(obj->layer->evas,
9921 ti->parent.format->font.font,
9922 obj->layer->evas->hinting);
9928 _evas_textblock_invalidate_all(o);
9929 _evas_textblock_changed(o, obj);
9937 /* return EINA_FALSE on error, used in unit_testing */
9939 _evas_textblock_check_item_node_link(Evas_Object *obj)
9941 Evas_Object_Textblock *o;
9942 Evas_Object_Textblock_Paragraph *par;
9943 Evas_Object_Textblock_Line *ln;
9944 Evas_Object_Textblock_Item *it;
9946 o = (Evas_Object_Textblock *)(obj->object_data);
9947 if (!o) return EINA_FALSE;
9949 if (!o->formatted.valid) _relayout(obj);
9951 EINA_INLIST_FOREACH(o->paragraphs, par)
9953 EINA_INLIST_FOREACH(par->lines, ln)
9955 EINA_INLIST_FOREACH(ln->items, it)
9957 if (it->text_node != par->text_node)
9966 _evas_textblock_format_offset_get(const Evas_Object_Textblock_Node_Format *n)
9973 /* Good for debugging */
9975 pfnode(Evas_Object_Textblock_Node_Format *n)
9977 printf("Format Node: %p\n", n);
9978 printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
9979 printf("text_node = %p, offset = %u, visible = %d\n", n->text_node, n->offset, n->visible);
9980 printf("'%s'\n", eina_strbuf_string_get(n->format));
9984 ptnode(Evas_Object_Textblock_Node_Text *n)
9986 printf("Text Node: %p\n", n);
9987 printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
9988 printf("format_node = %p\n", n->format_node);
9989 printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
9993 pitem(Evas_Object_Textblock_Item *it)
9995 Evas_Object_Textblock_Text_Item *ti;
9996 Evas_Object_Textblock_Format_Item *fi;
9997 printf("Item: %p\n", it);
9998 printf("Type: %s (%d)\n", (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
9999 "TEXT" : "FORMAT", it->type);
10000 printf("Text pos: %d Visual pos: %d\n", it->text_pos,
10001 #ifdef BIDI_SUPPORT
10007 printf("Coords: x = %d w = %d adv = %d\n", (int) it->x, (int) it->w,
10009 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
10011 ti = _ITEM_TEXT(it);
10012 printf("Text: '%*ls'\n", ti->text_props.text_len, GET_ITEM_TEXT(ti));
10016 fi = _ITEM_FORMAT(it);
10017 printf("Format: '%s'\n", fi->item);
10022 ppar(Evas_Object_Textblock_Paragraph *par)
10024 Evas_Object_Textblock_Item *it;
10026 EINA_LIST_FOREACH(par->logical_items, i, it)
10028 printf("***********************\n");