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 void _evas_textblock_node_text_remove_formats_between(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n, int start, int end);
539 static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur);
540 static size_t _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt);
541 static void _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visual_adjustment);
542 static void _evas_textblock_node_format_free(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n);
543 static void _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n);
544 static void _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj);
545 static void _evas_textblock_invalidate_all(Evas_Object_Textblock *o);
546 static void _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset);
547 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);
552 * Clears the textblock style passed except for the style_text which is replaced.
553 * @param ts The ts to be cleared. Must not be NULL.
554 * @param style_text the style's text.
557 _style_replace(Evas_Textblock_Style *ts, const char *style_text)
559 eina_stringshare_replace(&ts->style_text, style_text);
560 if (ts->default_tag) free(ts->default_tag);
563 Evas_Object_Style_Tag *tag;
565 tag = (Evas_Object_Style_Tag *)ts->tags;
566 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
568 free(tag->tag.replace);
571 ts->default_tag = NULL;
577 * Clears the textblock style passed.
578 * @param ts The ts to be cleared. Must not be NULL.
581 _style_clear(Evas_Textblock_Style *ts)
583 _style_replace(ts, NULL);
588 * Searches inside the tags stored in the style for the tag matching s.
589 * @param ts The ts to be cleared. Must not be NULL.
590 * @param s The tag to be matched.
591 * @param tag_len the length of the tag string.
592 * @param[out] replace_len The length of the replcaement found. - Must not be NULL.
593 * @return The replacement string found.
595 static inline const char *
596 _style_match_tag(const Evas_Textblock_Style *ts, const char *s, size_t tag_len, size_t *replace_len)
598 Evas_Object_Style_Tag *tag;
600 /* Try the style tags */
601 EINA_INLIST_FOREACH(ts->tags, tag)
603 if (tag->tag.tag_len != tag_len) continue;
604 if (!strncmp(tag->tag.tag, s, tag_len))
606 *replace_len = tag->tag.replace_len;
607 return tag->tag.replace;
611 /* Try the default tags */
614 const Evas_Object_Style_Tag_Base *btag;
615 for (btag = default_tags, i = 0 ;
616 i < (sizeof(default_tags) / sizeof(default_tags[0])) ;
619 if (btag->tag_len != tag_len) continue;
620 if (!strncmp(btag->tag, s, tag_len))
622 *replace_len = btag->replace_len;
623 return btag->replace;
634 * Clears all the nodes (text and format) of the textblock object.
635 * @param obj The evas object, must not be NULL.
638 _nodes_clear(const Evas_Object *obj)
640 Evas_Object_Textblock *o;
642 o = (Evas_Object_Textblock *)(obj->object_data);
643 while (o->text_nodes)
645 Evas_Object_Textblock_Node_Text *n;
648 o->text_nodes = _NODE_TEXT(eina_inlist_remove(
649 EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
650 _evas_textblock_node_text_free(n);
652 while (o->format_nodes)
654 Evas_Object_Textblock_Node_Format *n;
657 o->format_nodes = _NODE_FORMAT(eina_inlist_remove(EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
658 _evas_textblock_node_format_free(o, n);
664 * Unrefs and frees (if needed) a textblock format.
665 * @param obj The Evas_Object, Must not be NULL.
666 * @param fmt the format to be cleaned, must not be NULL.
669 _format_unref_free(const Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
672 if (fmt->ref > 0) return;
673 if (fmt->font.fdesc) evas_font_desc_unref(fmt->font.fdesc);
674 if (fmt->font.source) eina_stringshare_del(fmt->font.source);
675 evas_font_free(obj->layer->evas, fmt->font.font);
682 * @param obj The evas object, must not be NULL.
683 * @param ln the layout line on which the item is in, must not be NULL.
684 * @param it the layout item to be freed
687 _item_free(const Evas_Object *obj, Evas_Object_Textblock_Line *ln, Evas_Object_Textblock_Item *it)
689 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
691 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
693 evas_common_text_props_content_unref(&ti->text_props);
697 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
699 if (fi->item) eina_stringshare_del(fi->item);
701 _format_unref_free(obj, it->format);
704 ln->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(
705 EINA_INLIST_GET(ln->items), EINA_INLIST_GET(ln->items));
712 * Free a layout line.
713 * @param obj The evas object, must not be NULL.
714 * @param ln the layout line to be freed, must not be NULL.
717 _line_free(Evas_Object_Textblock_Line *ln)
719 /* Items are freed from the logical list, except for the ellip item */
723 /* table of html escapes (that i can find) this should be ordered with the
724 * most common first as it's a linear search to match - no hash for this.
726 * these are stored as one large string and one additional array that
727 * contains the offsets to the tokens for space efficiency.
731 * @var escape_strings[]
732 * This string consists of NULL terminated pairs of strings, the first of
733 * every pair is an escape and the second is the value of the escape.
735 static const char escape_strings[] =
736 /* most common escaped stuff */
742 " \0" "\xc2\xa0\0"
743 "¡\0" "\xc2\xa1\0"
744 "¢\0" "\xc2\xa2\0"
745 "£\0" "\xc2\xa3\0"
746 "¤\0" "\xc2\xa4\0"
747 "¥\0" "\xc2\xa5\0"
748 "¦\0" "\xc2\xa6\0"
749 "§\0" "\xc2\xa7\0"
750 "¨\0" "\xc2\xa8\0"
751 "©\0" "\xc2\xa9\0"
752 "ª\0" "\xc2\xaa\0"
753 "«\0" "\xc2\xab\0"
754 "¬\0" "\xc2\xac\0"
755 "®\0" "\xc2\xae\0"
756 "¯\0" "\xc2\xaf\0"
757 "°\0" "\xc2\xb0\0"
758 "±\0" "\xc2\xb1\0"
759 "²\0" "\xc2\xb2\0"
760 "³\0" "\xc2\xb3\0"
761 "´\0" "\xc2\xb4\0"
762 "µ\0" "\xc2\xb5\0"
763 "¶\0" "\xc2\xb6\0"
764 "·\0" "\xc2\xb7\0"
765 "¸\0" "\xc2\xb8\0"
766 "¹\0" "\xc2\xb9\0"
767 "º\0" "\xc2\xba\0"
768 "»\0" "\xc2\xbb\0"
769 "¼\0" "\xc2\xbc\0"
770 "½\0" "\xc2\xbd\0"
771 "¾\0" "\xc2\xbe\0"
772 "¿\0" "\xc2\xbf\0"
773 "À\0" "\xc3\x80\0"
774 "Á\0" "\xc3\x81\0"
775 "Â\0" "\xc3\x82\0"
776 "Ã\0" "\xc3\x83\0"
777 "Ä\0" "\xc3\x84\0"
778 "Å\0" "\xc3\x85\0"
779 "&Aelig;\0" "\xc3\x86\0"
780 "Ç\0" "\xc3\x87\0"
781 "È\0" "\xc3\x88\0"
782 "É\0" "\xc3\x89\0"
783 "Ê\0" "\xc3\x8a\0"
784 "Ë\0" "\xc3\x8b\0"
785 "Ì\0" "\xc3\x8c\0"
786 "Í\0" "\xc3\x8d\0"
787 "Î\0" "\xc3\x8e\0"
788 "Ï\0" "\xc3\x8f\0"
789 "&Eth;\0" "\xc3\x90\0"
790 "Ñ\0" "\xc3\x91\0"
791 "Ò\0" "\xc3\x92\0"
792 "Ó\0" "\xc3\x93\0"
793 "Ô\0" "\xc3\x94\0"
794 "Õ\0" "\xc3\x95\0"
795 "Ö\0" "\xc3\x96\0"
796 "×\0" "\xc3\x97\0"
797 "Ø\0" "\xc3\x98\0"
798 "Ù\0" "\xc3\x99\0"
799 "Ú\0" "\xc3\x9a\0"
800 "Û\0" "\xc3\x9b\0"
801 "Ý\0" "\xc3\x9d\0"
802 "&Thorn;\0" "\xc3\x9e\0"
803 "ß\0" "\xc3\x9f\0"
804 "à\0" "\xc3\xa0\0"
805 "á\0" "\xc3\xa1\0"
806 "â\0" "\xc3\xa2\0"
807 "ã\0" "\xc3\xa3\0"
808 "ä\0" "\xc3\xa4\0"
809 "å\0" "\xc3\xa5\0"
810 "æ\0" "\xc3\xa6\0"
811 "ç\0" "\xc3\xa7\0"
812 "è\0" "\xc3\xa8\0"
813 "é\0" "\xc3\xa9\0"
814 "ê\0" "\xc3\xaa\0"
815 "ë\0" "\xc3\xab\0"
816 "ì\0" "\xc3\xac\0"
817 "í\0" "\xc3\xad\0"
818 "î\0" "\xc3\xae\0"
819 "ï\0" "\xc3\xaf\0"
820 "ð\0" "\xc3\xb0\0"
821 "ñ\0" "\xc3\xb1\0"
822 "ò\0" "\xc3\xb2\0"
823 "ó\0" "\xc3\xb3\0"
824 "ô\0" "\xc3\xb4\0"
825 "õ\0" "\xc3\xb5\0"
826 "ö\0" "\xc3\xb6\0"
827 "÷\0" "\xc3\xb7\0"
828 "ø\0" "\xc3\xb8\0"
829 "ù\0" "\xc3\xb9\0"
830 "ú\0" "\xc3\xba\0"
831 "û\0" "\xc3\xbb\0"
832 "ü\0" "\xc3\xbc\0"
833 "ý\0" "\xc3\xbd\0"
834 "þ\0" "\xc3\xbe\0"
835 "ÿ\0" "\xc3\xbf\0"
836 "α\0" "\xce\x91\0"
837 "β\0" "\xce\x92\0"
838 "γ\0" "\xce\x93\0"
839 "δ\0" "\xce\x94\0"
840 "ε\0" "\xce\x95\0"
841 "ζ\0" "\xce\x96\0"
842 "η\0" "\xce\x97\0"
843 "θ\0" "\xce\x98\0"
844 "ι\0" "\xce\x99\0"
845 "κ\0" "\xce\x9a\0"
846 "λ\0" "\xce\x9b\0"
847 "μ\0" "\xce\x9c\0"
848 "ν\0" "\xce\x9d\0"
849 "ξ\0" "\xce\x9e\0"
850 "ο\0" "\xce\x9f\0"
851 "π\0" "\xce\xa0\0"
852 "ρ\0" "\xce\xa1\0"
853 "σ\0" "\xce\xa3\0"
854 "τ\0" "\xce\xa4\0"
855 "υ\0" "\xce\xa5\0"
856 "φ\0" "\xce\xa6\0"
857 "χ\0" "\xce\xa7\0"
858 "ψ\0" "\xce\xa8\0"
859 "ω\0" "\xce\xa9\0"
860 "…\0" "\xe2\x80\xa6\0"
861 "€\0" "\xe2\x82\xac\0"
862 "←\0" "\xe2\x86\x90\0"
863 "↑\0" "\xe2\x86\x91\0"
864 "→\0" "\xe2\x86\x92\0"
865 "↓\0" "\xe2\x86\x93\0"
866 "↔\0" "\xe2\x86\x94\0"
867 "←\0" "\xe2\x87\x90\0"
868 "→\0" "\xe2\x87\x92\0"
869 "∀\0" "\xe2\x88\x80\0"
870 "∃\0" "\xe2\x88\x83\0"
871 "∇\0" "\xe2\x88\x87\0"
872 "∏\0" "\xe2\x88\x8f\0"
873 "∑\0" "\xe2\x88\x91\0"
874 "∧\0" "\xe2\x88\xa7\0"
875 "∨\0" "\xe2\x88\xa8\0"
876 "∫\0" "\xe2\x88\xab\0"
877 "≠\0" "\xe2\x89\xa0\0"
878 "≡\0" "\xe2\x89\xa1\0"
879 "⊕\0" "\xe2\x8a\x95\0"
880 "⊥\0" "\xe2\x8a\xa5\0"
881 "†\0" "\xe2\x80\xa0\0"
882 "‡\0" "\xe2\x80\xa1\0"
883 "•\0" "\xe2\x80\xa2\0"
886 EVAS_MEMPOOL(_mp_obj);
890 * Checks if a char is a whitespace.
891 * @param c the unicode codepoint.
892 * @return EINA_TRUE if the unicode codepoint is a whitespace, EINA_FALSE otherwise.
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 #EINA_TRUE if the item is a format parameter, #EINA_FALSE otherwise.
1624 * @param[in] item the item to check - Not NULL.
1627 _format_is_param(const char *item)
1629 if (strchr(item, '=')) return EINA_TRUE;
1635 * Parse the format item and populate key and val with the stringshares that
1636 * corrospond to the formats parsed.
1637 * It expects item to be of the structure:
1640 * @param[in] item the item to parse - Not NULL.
1641 * @param[out] key where to store the key at - Not NULL.
1642 * @param[out] val where to store the value at - Not NULL.
1645 _format_param_parse(const char *item, const char **key, const char **val)
1647 const char *start, *end, *quote;
1649 start = strchr(item, '=');
1650 *key = eina_stringshare_add_length(item, start - item);
1651 start++; /* Advance after the '=' */
1652 /* If we can find a quote, our new delimiter is a quote, not a space. */
1653 if ((quote = strchr(start, '\'')))
1656 end = strchr(start, '\'');
1660 end = strchr(start, ' ');
1663 /* Null terminate before the spaces */
1666 *val = eina_stringshare_add_length(start, end - start);
1670 *val = eina_stringshare_add(start);
1676 * This function parses the format passed in *s and advances s to point to the
1677 * next format item, while returning the current one as the return value.
1678 * @param s The current and returned position in the format string.
1679 * @return the current item parsed from the string.
1682 _format_parse(const char **s)
1685 const char *s1 = NULL, *s2 = NULL;
1686 Eina_Bool quote = EINA_FALSE;;
1689 if (*p == 0) return NULL;
1694 if (*p != ' ') s1 = p;
1704 if ((p > *s) && (p[-1] != '\\') && (!quote))
1706 if (*p == ' ') s2 = p;
1708 if (*p == 0) s2 = p;
1723 * Parse the format str and populate fmt with the formats found.
1725 * @param obj The evas object - Not NULL.
1726 * @param[out] fmt The format to populate - Not NULL.
1727 * @param[in] str the string to parse.- Not NULL.
1730 _format_fill(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *str)
1737 /* get rid of any spaces at the start of the string */
1738 while (*s == ' ') s++;
1740 while ((item = _format_parse(&s)))
1742 if (_format_is_param(item))
1744 const char *key = NULL, *val = NULL;
1746 _format_param_parse(item, &key, &val);
1747 _format_command(obj, fmt, key, val);
1748 eina_stringshare_del(key);
1749 eina_stringshare_del(val);
1753 /* immediate - not handled here */
1760 * Duplicate a format and return the duplicate.
1762 * @param obj The evas object - Not NULL.
1763 * @param[in] fmt The format to duplicate - Not NULL.
1764 * @return the copy of the format.
1766 static Evas_Object_Textblock_Format *
1767 _format_dup(Evas_Object *obj, const Evas_Object_Textblock_Format *fmt)
1769 Evas_Object_Textblock_Format *fmt2;
1771 fmt2 = calloc(1, sizeof(Evas_Object_Textblock_Format));
1772 memcpy(fmt2, fmt, sizeof(Evas_Object_Textblock_Format));
1774 fmt2->font.fdesc = evas_font_desc_ref(fmt->font.fdesc);
1776 if (fmt->font.source) fmt2->font.source = eina_stringshare_add(fmt->font.source);
1778 /* FIXME: just ref the font here... */
1779 fmt2->font.font = evas_font_load(obj->layer->evas, fmt2->font.fdesc,
1780 fmt2->font.source, (int)(((double) fmt2->font.size) * obj->cur.scale));
1791 * A pack of information that needed to be passed around in the layout engine,
1792 * packed for easier access.
1794 typedef struct _Ctxt Ctxt;
1799 Evas_Object_Textblock *o;
1801 Evas_Object_Textblock_Paragraph *paragraphs;
1802 Evas_Object_Textblock_Paragraph *par;
1803 Evas_Object_Textblock_Line *ln;
1806 Eina_List *format_stack;
1807 Evas_Object_Textblock_Format *fmt;
1812 int maxascent, maxdescent;
1813 int marginl, marginr;
1815 int underline_extend;
1816 int have_underline, have_underline2;
1817 double align, valign;
1818 Eina_Bool align_auto : 1;
1819 Eina_Bool width_changed : 1;
1822 static void _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti, Eina_List *rel);
1823 static void _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti);
1824 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);
1827 * Adjust the ascent/descent of the format and context.
1829 * @param maxascent The ascent to update - Not NUL.
1830 * @param maxdescent The descent to update - Not NUL.
1831 * @param fmt The format to adjust - NOT NULL.
1834 _layout_format_ascent_descent_adjust(const Evas_Object *obj,
1835 Evas_Coord *maxascent, Evas_Coord *maxdescent,
1836 Evas_Object_Textblock_Format *fmt)
1838 int ascent, descent;
1842 // ascent = c->ENFN->font_max_ascent_get(c->ENDT, fmt->font.font);
1843 // descent = c->ENFN->font_max_descent_get(c->ENDT, fmt->font.font);
1844 ascent = ENFN->font_ascent_get(ENDT, fmt->font.font);
1845 descent = ENFN->font_descent_get(ENDT, fmt->font.font);
1846 if (fmt->linesize > 0)
1848 if ((ascent + descent) < fmt->linesize)
1850 ascent = ((fmt->linesize * ascent) / (ascent + descent));
1851 descent = fmt->linesize - ascent;
1854 else if (fmt->linerelsize > 0.0)
1856 descent = descent * fmt->linerelsize;
1857 ascent = ascent * fmt->linerelsize;
1859 descent += fmt->linegap;
1860 descent += ((ascent + descent) * fmt->linerelgap);
1861 if (*maxascent < ascent) *maxascent = ascent;
1862 if (*maxdescent < descent) *maxdescent = descent;
1863 if (fmt->linefill > 0.0)
1867 dh = obj->cur.geometry.h - (*maxascent + *maxdescent);
1869 dh = fmt->linefill * dh;
1870 *maxdescent += dh / 2;
1871 *maxascent += dh - (dh / 2);
1872 // FIXME: set flag that says "if heigh changes - reformat"
1879 * Create a new line using the info from the format and update the format
1882 * @param c The context to work on - Not NULL.
1883 * @param fmt The format to use info from - NOT NULL.
1886 _layout_line_new(Ctxt *c, Evas_Object_Textblock_Format *fmt)
1888 c->ln = calloc(1, sizeof(Evas_Object_Textblock_Line));
1889 c->align = fmt->halign;
1890 c->align_auto = fmt->halign_auto;
1891 c->marginl = fmt->margin.l;
1892 c->marginr = fmt->margin.r;
1893 c->par->lines = (Evas_Object_Textblock_Line *)eina_inlist_append(EINA_INLIST_GET(c->par->lines), EINA_INLIST_GET(c->ln));
1895 c->maxascent = c->maxdescent = 0;
1896 c->ln->line_no = -1;
1897 c->ln->par = c->par;
1900 static inline Evas_Object_Textblock_Paragraph *
1901 _layout_find_paragraph_by_y(Evas_Object_Textblock *o, Evas_Coord y)
1903 Evas_Object_Textblock_Paragraph *start, *par;
1906 start = o->paragraphs;
1908 for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
1910 if (!o->par_index[i] || (o->par_index[i]->y > y))
1914 start = o->par_index[i];
1917 EINA_INLIST_FOREACH(start, par)
1919 if ((par->y <= y) && (y < par->y + par->h))
1926 static inline Evas_Object_Textblock_Paragraph *
1927 _layout_find_paragraph_by_line_no(Evas_Object_Textblock *o, int line_no)
1929 Evas_Object_Textblock_Paragraph *start, *par;
1932 start = o->paragraphs;
1934 for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
1936 if (!o->par_index[i] || (o->par_index[i]->line_no > line_no))
1940 start = o->par_index[i];
1943 EINA_INLIST_FOREACH(start, par)
1945 Evas_Object_Textblock_Paragraph *npar =
1946 (Evas_Object_Textblock_Paragraph *) EINA_INLIST_GET(par)->next;
1947 if ((par->line_no <= line_no) &&
1948 (!npar || (line_no < npar->line_no)))
1954 /* End of rbtree index functios */
1958 * Create a new layout paragraph.
1959 * If c->par is not NULL, the paragraph is appended/prepended according
1960 * to the append parameter. If it is NULL, the paragraph is appended at
1961 * the end of the list.
1963 * @param c The context to work on - Not NULL.
1964 * @param n the associated text node
1965 * @param append true to append, false to prpend.
1968 _layout_paragraph_new(Ctxt *c, Evas_Object_Textblock_Node_Text *n,
1971 Evas_Object_Textblock_Paragraph *rel_par = c->par;
1972 c->par = calloc(1, sizeof(Evas_Object_Textblock_Paragraph));
1973 if (append || !rel_par)
1974 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
1975 eina_inlist_append_relative(EINA_INLIST_GET(c->paragraphs),
1976 EINA_INLIST_GET(c->par),
1977 EINA_INLIST_GET(rel_par));
1979 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
1980 eina_inlist_prepend_relative(EINA_INLIST_GET(c->paragraphs),
1981 EINA_INLIST_GET(c->par),
1982 EINA_INLIST_GET(rel_par));
1985 c->par->text_node = n;
1988 c->par->line_no = -1;
1989 c->par->visible = 1;
1990 c->o->num_paragraphs++;
1996 * Update bidi paragraph props.
1998 * @param par The paragraph to update
2001 _layout_update_bidi_props(const Evas_Object_Textblock *o,
2002 Evas_Object_Textblock_Paragraph *par)
2006 const Eina_Unicode *text;
2007 int *segment_idxs = NULL;
2008 text = eina_ustrbuf_string_get(par->text_node->unicode);
2010 if (o->bidi_delimiters)
2011 segment_idxs = evas_bidi_segment_idxs_get(text, o->bidi_delimiters);
2013 evas_bidi_paragraph_props_unref(par->bidi_props);
2014 par->bidi_props = evas_bidi_paragraph_props_get(text,
2015 eina_ustrbuf_length_get(par->text_node->unicode),
2017 par->direction = EVAS_BIDI_PARAGRAPH_DIRECTION_IS_RTL(par->bidi_props) ?
2018 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
2019 par->is_bidi = !!par->bidi_props;
2020 if (segment_idxs) free(segment_idxs);
2028 * Free the visual lines in the paragraph (logical items are kept)
2031 _paragraph_clear(const Evas_Object *obj __UNUSED__,
2032 Evas_Object_Textblock_Paragraph *par)
2036 Evas_Object_Textblock_Line *ln;
2038 ln = (Evas_Object_Textblock_Line *) par->lines;
2039 par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(par->lines), EINA_INLIST_GET(par->lines));
2046 * Free the layout paragraph and all of it's lines and logical items.
2049 _paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
2051 Evas_Object_Textblock *o;
2052 o = (Evas_Object_Textblock *)(obj->object_data);
2053 _paragraph_clear(obj, par);
2056 Eina_List *i, *i_prev;
2057 Evas_Object_Textblock_Item *it;
2058 EINA_LIST_FOREACH_SAFE(par->logical_items, i, i_prev, it)
2060 _item_free(obj, NULL, it);
2062 eina_list_free(par->logical_items);
2065 if (par->bidi_props)
2066 evas_bidi_paragraph_props_unref(par->bidi_props);
2068 /* If we are the active par of the text node, set to NULL */
2069 if (par->text_node && (par->text_node->par == par))
2070 par->text_node->par = NULL;
2072 o->num_paragraphs--;
2079 * Clear all the paragraphs from the inlist pars.
2081 * @param obj the evas object - Not NULL.
2082 * @param pars the paragraphs to clean - Not NULL.
2085 _paragraphs_clear(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
2087 Evas_Object_Textblock_Paragraph *par;
2089 EINA_INLIST_FOREACH(EINA_INLIST_GET(pars), par)
2091 _paragraph_clear(obj, par);
2097 * Free the paragraphs from the inlist pars, the difference between this and
2098 * _paragraphs_clear is that the latter keeps the logical items and the par
2099 * items, while the former frees them as well.
2101 * @param obj the evas object - Not NULL.
2102 * @param pars the paragraphs to clean - Not NULL.
2105 _paragraphs_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
2107 Evas_Object_Textblock *o;
2108 o = (Evas_Object_Textblock *)(obj->object_data);
2110 o->num_paragraphs = 0;
2114 Evas_Object_Textblock_Paragraph *par;
2116 par = (Evas_Object_Textblock_Paragraph *) pars;
2117 pars = (Evas_Object_Textblock_Paragraph *)eina_inlist_remove(EINA_INLIST_GET(pars), EINA_INLIST_GET(par));
2118 _paragraph_free(obj, par);
2124 * Push fmt to the format stack, if fmt is NULL, will fush a default item.
2126 * @param c the context to work on - Not NULL.
2127 * @param fmt the format to push.
2128 * @see _layout_format_pop()
2130 static Evas_Object_Textblock_Format *
2131 _layout_format_push(Ctxt *c, Evas_Object_Textblock_Format *fmt,
2132 Evas_Object_Textblock_Node_Format *fnode)
2136 fmt = _format_dup(c->obj, fmt);
2137 c->format_stack = eina_list_prepend(c->format_stack, fmt);
2142 fmt = calloc(1, sizeof(Evas_Object_Textblock_Format));
2143 c->format_stack = eina_list_prepend(c->format_stack, fmt);
2146 fmt->halign_auto = EINA_TRUE;
2148 fmt->style = EVAS_TEXT_STYLE_PLAIN;
2151 fmt->linerelsize = 0.0;
2153 fmt->underline_dash_width = 6;
2154 fmt->underline_dash_gap = 2;
2155 fmt->linerelgap = 0.0;
2163 * Pop fmt to the format stack, if there's something in the stack free fmt
2164 * and set it to point to the next item instead, else return fmt.
2166 * @param c the context to work on - Not NULL.
2167 * @param format - the text of the format to free (assured to start with '-').
2168 * @return the next format in the stack, or format if there's none.
2169 * @see _layout_format_push()
2171 static Evas_Object_Textblock_Format *
2172 _layout_format_pop(Ctxt *c, const char *format)
2174 Evas_Object_Textblock_Format *fmt = eina_list_data_get(c->format_stack);
2176 if ((c->format_stack) && (c->format_stack->next))
2178 Eina_List *redo_nodes = NULL;
2180 /* Generic pop, should just pop. */
2181 if (((format[0] == ' ') && !format[1]) ||
2184 _format_unref_free(c->obj, fmt);
2186 eina_list_remove_list(c->format_stack, c->format_stack);
2190 size_t len = strlen(format);
2191 Eina_List *i, *i_next;
2192 /* Remove only the matching format. */
2193 EINA_LIST_FOREACH_SAFE(c->format_stack, i, i_next, fmt)
2195 /* Stop when we reach the base item */
2200 eina_list_remove_list(c->format_stack, c->format_stack);
2202 /* Make sure the ending tag matches the starting tag.
2203 * I.e whole of the ending tag matches the start of the
2204 * starting tag, and the starting tag's next char is either
2205 * NULL or white. Skip the starting '+'. */
2206 if (_FORMAT_IS_CLOSER_OF(
2207 fmt->fnode->orig_format, format, len))
2209 _format_unref_free(c->obj, fmt);
2214 redo_nodes = eina_list_prepend(redo_nodes, fmt->fnode);
2215 _format_unref_free(c->obj, fmt);
2220 /* Redo all the nodes needed to be redone */
2222 Evas_Object_Textblock_Node_Format *fnode;
2223 Eina_List *i, *i_next;
2225 EINA_LIST_FOREACH_SAFE(redo_nodes, i, i_next, fnode)
2227 /* FIXME: Actually do something with the new acquired padding,
2228 * the can be different and affect our padding! */
2229 Evas_Coord style_pad_l, style_pad_r, style_pad_t, style_pad_b;
2230 style_pad_l = style_pad_r = style_pad_t = style_pad_b = 0;
2231 redo_nodes = eina_list_remove_list(redo_nodes, i);
2232 fmt = eina_list_data_get(c->format_stack);
2233 _layout_do_format(c->obj, c, &fmt, fnode,
2234 &style_pad_l, &style_pad_r,
2235 &style_pad_t, &style_pad_b, EINA_FALSE);
2239 fmt = eina_list_data_get(c->format_stack);
2246 * Parse item and fill fmt with the item.
2248 * @param c the context to work on - Not NULL.
2249 * @param fmt the format to fill - not null.
2252 _layout_format_value_handle(Ctxt *c, Evas_Object_Textblock_Format *fmt, const char *item)
2254 const char *key = NULL, *val = NULL;
2256 _format_param_parse(item, &key, &val);
2257 if ((key) && (val)) _format_command(c->obj, fmt, key, val);
2258 if (key) eina_stringshare_del(key);
2259 if (val) eina_stringshare_del(val);
2260 c->align = fmt->halign;
2261 c->align_auto = fmt->halign_auto;
2262 c->marginl = fmt->margin.l;
2263 c->marginr = fmt->margin.r;
2266 #define VSIZE_FULL 0
2267 #define VSIZE_ASCENT 1
2275 * Get the current line's alignment from the context.
2277 * @param c the context to work on - Not NULL.
2279 static inline double
2280 _layout_line_align_get(Ctxt *c)
2283 if (c->align_auto && c->ln)
2285 if (c->ln->items && c->ln->items->text_node &&
2286 (c->ln->par->direction == EVAS_BIDI_DIRECTION_RTL))
2304 * Reorder the items in visual order
2306 * @param line the line to reorder
2309 _layout_line_reorder(Evas_Object_Textblock_Line *line)
2311 /*FIXME: do it a bit more efficient - not very efficient ATM. */
2312 Evas_Object_Textblock_Item *it;
2313 EvasBiDiStrIndex *v_to_l = NULL;
2318 if (line->items && line->items->text_node &&
2319 line->par->bidi_props)
2321 Evas_BiDi_Paragraph_Props *props;
2322 props = line->par->bidi_props;
2323 start = end = line->items->text_pos;
2325 /* Find the first and last positions in the line */
2327 EINA_INLIST_FOREACH(line->items, it)
2329 if (it->text_pos < start)
2331 start = it->text_pos;
2336 tlen = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
2337 _ITEM_TEXT(it)->text_props.text_len : 1;
2338 if (it->text_pos + tlen > end)
2340 end = it->text_pos + tlen;
2346 evas_bidi_props_reorder_line(NULL, start, len, props, &v_to_l);
2348 /* Update visual pos */
2350 Evas_Object_Textblock_Item *i;
2354 i->visual_pos = evas_bidi_position_logical_to_visual(
2355 v_to_l, len, i->text_pos - start);
2356 i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(i)->next;
2360 /*FIXME: not very efficient, sort the items arrays. Anyhow, should only
2361 * reorder if it's a bidi paragraph */
2363 Evas_Object_Textblock_Item *i, *j, *min;
2368 EINA_INLIST_FOREACH(i, j)
2370 if (j->visual_pos < min->visual_pos)
2377 line->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min));
2378 line->items = (Evas_Object_Textblock_Item *) eina_inlist_prepend_relative(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min), EINA_INLIST_GET(i));
2381 i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(min)->next;
2386 if (v_to_l) free(v_to_l);
2388 EINA_INLIST_FOREACH(line->items, it)
2398 _layout_calculate_format_item_size(const Evas_Object *obj,
2399 const Evas_Object_Textblock_Format_Item *fi,
2400 Evas_Coord *maxascent, Evas_Coord *maxdescent,
2401 Evas_Coord *_y, Evas_Coord *_w, Evas_Coord *_h)
2403 /* Adjust sizes according to current line height/scale */
2413 p = strstr(s, " size=");
2417 if (sscanf(p, "%ix%i", &w, &h) == 2)
2419 w = w * obj->cur.scale;
2420 h = h * obj->cur.scale;
2425 p = strstr((char *) s, " relsize=");
2427 if (sscanf(p, "%ix%i", &w, &h) == 2)
2430 if (fi->vsize == VSIZE_FULL)
2432 sz = *maxdescent + *maxascent;
2434 else if (fi->vsize == VSIZE_ASCENT)
2455 if (h > (*maxdescent + *maxascent))
2457 *maxascent += h - (*maxdescent + *maxascent);
2461 *_y = -(h - *maxdescent);
2497 * Order the items in the line, update it's properties and update it's
2498 * corresponding paragraph.
2500 * @param c the context to work on - Not NULL.
2501 * @param fmt the format to use.
2502 * @param add_line true if we should create a line, false otherwise.
2505 _layout_line_finalize(Ctxt *c, Evas_Object_Textblock_Format *fmt)
2507 Evas_Object_Textblock_Item *it;
2510 /* If there are no text items yet, calc ascent/descent
2511 * according to the current format. */
2512 if (c->maxascent + c->maxdescent == 0)
2513 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
2514 &c->maxdescent, fmt);
2516 /* Adjust all the item sizes according to the final line size,
2517 * and update the x positions of all the items of the line. */
2518 EINA_INLIST_FOREACH(c->ln->items, it)
2520 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
2522 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
2523 if (!fi->formatme) goto loop_advance;
2524 _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
2525 &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
2526 fi->parent.adv = fi->parent.w;
2533 if ((it->x + it->adv) > c->ln->w) c->ln->w = it->x + it->adv;
2536 c->ln->y = (c->y - c->par->y) + c->o->style_pad.t;
2537 c->ln->h = c->maxascent + c->maxdescent;
2538 c->ln->baseline = c->maxascent;
2539 if (c->have_underline2)
2541 if (c->maxdescent < 4) c->underline_extend = 4 - c->maxdescent;
2543 else if (c->have_underline)
2545 if (c->maxdescent < 2) c->underline_extend = 2 - c->maxdescent;
2547 c->ln->line_no = c->line_no - c->ln->par->line_no;
2549 c->y += c->maxascent + c->maxdescent;
2552 c->ln->x = c->marginl + c->o->style_pad.l +
2554 c->o->style_pad.l - c->o->style_pad.r -
2555 c->marginl - c->marginr) * _layout_line_align_get(c));
2559 c->ln->x = c->marginl + c->o->style_pad.l;
2562 c->par->h = c->ln->y + c->ln->h;
2563 if (c->ln->w > c->par->w)
2564 c->par->w = c->ln->w;
2567 Evas_Coord new_wmax = c->ln->w +
2568 c->marginl + c->marginr - (c->o->style_pad.l + c->o->style_pad.r);
2569 if (new_wmax > c->wmax)
2576 * Create a new line and append it to the lines in the context.
2578 * @param c the context to work on - Not NULL.
2579 * @param fmt the format to use.
2580 * @param add_line true if we should create a line, false otherwise.
2583 _layout_line_advance(Ctxt *c, Evas_Object_Textblock_Format *fmt)
2585 _layout_line_finalize(c, fmt);
2586 _layout_line_new(c, fmt);
2591 * Create a new text layout item from the string and the format.
2593 * @param c the context to work on - Not NULL.
2594 * @param fmt the format to use.
2595 * @param str the string to use.
2596 * @param len the length of the string.
2598 static Evas_Object_Textblock_Text_Item *
2599 _layout_text_item_new(Ctxt *c __UNUSED__, Evas_Object_Textblock_Format *fmt)
2601 Evas_Object_Textblock_Text_Item *ti;
2603 ti = calloc(1, sizeof(Evas_Object_Textblock_Text_Item));
2604 ti->parent.format = fmt;
2605 ti->parent.format->ref++;
2606 ti->parent.type = EVAS_TEXTBLOCK_ITEM_TEXT;
2612 * Return the cutoff of the text in the text item.
2614 * @param c the context to work on - Not NULL.
2615 * @param fmt the format to use. - Not NULL.
2616 * @param it the item to check - Not null.
2617 * @return -1 if there is no cutoff (either because there is really none,
2618 * or because of an error), cutoff index on success.
2621 _layout_text_cutoff_get(Ctxt *c, Evas_Object_Textblock_Format *fmt,
2622 const Evas_Object_Textblock_Text_Item *ti)
2627 x = c->w - c->o->style_pad.l - c->o->style_pad.r - c->marginl -
2628 c->marginr - c->x - ti->x_adjustment;
2631 return c->ENFN->font_last_up_to_pos(c->ENDT, fmt->font.font,
2632 &ti->text_props, x, 0);
2639 * Split before cut, and strip if str[cut - 1] is a whitespace.
2641 * @param c the context to work on - Not NULL.
2642 * @param ti the item to cut - not null.
2643 * @param lti the logical list item of the item.
2644 * @param cut the cut index.
2645 * @return the second (newly created) item.
2647 static Evas_Object_Textblock_Text_Item *
2648 _layout_item_text_split_strip_white(Ctxt *c,
2649 Evas_Object_Textblock_Text_Item *ti, Eina_List *lti, size_t cut)
2651 const Eina_Unicode *ts;
2652 Evas_Object_Textblock_Text_Item *new_ti = NULL, *white_ti = NULL;
2654 ts = GET_ITEM_TEXT(ti);
2656 if (!IS_AT_END(ti, cut) && (ti->text_props.text_len > 0))
2658 new_ti = _layout_text_item_new(c, ti->parent.format);
2659 new_ti->parent.text_node = ti->parent.text_node;
2660 new_ti->parent.text_pos = ti->parent.text_pos + cut;
2661 new_ti->parent.merge = EINA_TRUE;
2663 evas_common_text_props_split(&ti->text_props,
2664 &new_ti->text_props, cut);
2665 _layout_text_add_logical_item(c, new_ti, lti);
2668 /* Strip the previous white if needed */
2669 if ((cut >= 1) && _is_white(ts[cut - 1]) && (ti->text_props.text_len > 0))
2673 size_t white_cut = cut - 1;
2674 white_ti = _layout_text_item_new(c, ti->parent.format);
2675 white_ti->parent.text_node = ti->parent.text_node;
2676 white_ti->parent.text_pos = ti->parent.text_pos + white_cut;
2677 white_ti->parent.merge = EINA_TRUE;
2678 white_ti->parent.visually_deleted = EINA_TRUE;
2680 evas_common_text_props_split(&ti->text_props,
2681 &white_ti->text_props, white_cut);
2682 _layout_text_add_logical_item(c, white_ti, lti);
2686 /* Mark this one as the visually deleted. */
2687 ti->parent.visually_deleted = EINA_TRUE;
2691 if (new_ti || white_ti)
2693 _text_item_update_sizes(c, ti);
2700 * Merge item2 into item1 and free item2.
2702 * @param c the context to work on - Not NULL.
2703 * @param item1 the item to copy to
2704 * @param item2 the item to copy from
2707 _layout_item_merge_and_free(Ctxt *c,
2708 Evas_Object_Textblock_Text_Item *item1,
2709 Evas_Object_Textblock_Text_Item *item2)
2711 evas_common_text_props_merge(&item1->text_props,
2712 &item2->text_props);
2714 _text_item_update_sizes(c, item1);
2716 item1->parent.merge = EINA_FALSE;
2717 item1->parent.visually_deleted = EINA_FALSE;
2719 _item_free(c->obj, NULL, _ITEM(item2));
2724 * Calculates an item's size.
2726 * @param c the context
2727 * @param it the item itself.
2730 _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti)
2732 int tw, th, inset, advw;
2733 const Evas_Object_Textblock_Format *fmt = ti->parent.format;
2734 int shad_sz = 0, shad_dst = 0, out_sz = 0;
2735 int dx = 0, minx = 0, maxx = 0, shx1, shx2;
2739 c->ENFN->font_string_size_get(c->ENDT, fmt->font.font,
2740 &ti->text_props, &tw, &th);
2743 inset = c->ENFN->font_inset_get(c->ENDT, fmt->font.font,
2747 advw = c->ENFN->font_h_advance_get(c->ENDT, fmt->font.font,
2751 /* These adjustments are calculated and thus heavily linked to those in
2752 * textblock_render!!! Don't change one without the other. */
2754 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
2756 case EVAS_TEXT_STYLE_SHADOW:
2759 case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
2760 case EVAS_TEXT_STYLE_FAR_SHADOW:
2764 case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
2769 case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
2773 case EVAS_TEXT_STYLE_SOFT_SHADOW:
2777 case EVAS_TEXT_STYLE_GLOW:
2778 case EVAS_TEXT_STYLE_SOFT_OUTLINE:
2781 case EVAS_TEXT_STYLE_OUTLINE:
2787 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
2789 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
2790 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
2791 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
2794 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
2795 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
2796 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
2798 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
2799 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
2806 shx1 = dx * shad_dst;
2808 shx2 = dx * shad_dst;
2810 if (shx1 < minx) minx = shx1;
2811 if (shx2 > maxx) maxx = shx2;
2813 ti->x_adjustment = maxx - minx;
2816 ti->parent.w = tw + ti->x_adjustment;
2818 ti->parent.adv = advw;
2824 * Adds the item to the list, updates the item's properties (e.g, x,w,h)
2826 * @param c the context
2827 * @param it the item itself.
2828 * @param rel item ti will be appened after, NULL = last.
2831 _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti,
2834 _text_item_update_sizes(c, ti);
2836 c->par->logical_items = eina_list_append_relative_list(
2837 c->par->logical_items, ti, rel);
2842 * Appends the text from node n starting at start ending at off to the layout.
2843 * It uses the fmt for the formatting.
2845 * @param c the current context- NOT NULL.
2846 * @param fmt the format to use.
2847 * @param n the text node. - Not null.
2848 * @param start the start position. - in range.
2849 * @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.
2850 * @param repch a replacement char to print instead of the original string, for example, * when working with passwords.
2853 _layout_text_append(Ctxt *c, Evas_Object_Textblock_Format *fmt, Evas_Object_Textblock_Node_Text *n, int start, int off, const char *repch)
2855 const Eina_Unicode *str = EINA_UNICODE_EMPTY_STRING;
2856 const Eina_Unicode *tbase;
2857 Evas_Object_Textblock_Text_Item *ti;
2859 Eina_Unicode urepch = 0;
2861 /* prepare a working copy of the string, either filled by the repch or
2862 * filled with the true values */
2868 /* Figure out if we want to bail, work with an empty string,
2869 * or continue with a slice of the passed string */
2870 len = eina_ustrbuf_length_get(n->unicode);
2871 if (off == 0) return;
2872 else if (off < 0) off = len - start;
2878 else if ((start == 0) && (off == 0) && (orig_off == -1))
2880 /* Special case that means that we need to add an empty
2882 str = EINA_UNICODE_EMPTY_STRING;
2885 else if ((start >= len) || (start + off > len))
2890 /* If we work with a replacement char, create a string which is the same
2891 * but with replacement chars instead of regular chars. */
2892 if ((fmt->password) && (repch) && (eina_ustrbuf_length_get(n->unicode)))
2897 tbase = str = ptr = alloca((off + 1) * sizeof(Eina_Unicode));
2899 urepch = eina_unicode_utf8_get_next(repch, &ind);
2900 for (i = 0 ; i < off; ptr++, i++)
2904 /* Use the string, just cut the relevant parts */
2907 str = eina_ustrbuf_string_get(n->unicode) + start;
2916 /* If there's no parent text node, only create an empty item */
2919 ti = _layout_text_item_new(c, fmt);
2920 ti->parent.text_node = NULL;
2921 ti->parent.text_pos = 0;
2922 _layout_text_add_logical_item(c, ti, NULL);
2929 Evas_Font_Instance *script_fi = NULL;
2930 int script_len, tmp_cut;
2931 Evas_Script_Type script;
2933 script_len = cur_len;
2935 tmp_cut = evas_common_language_script_end_of_run_get(str,
2936 c->par->bidi_props, start + str - tbase, script_len);
2939 script_len = tmp_cut;
2941 cur_len -= script_len;
2943 script = evas_common_language_script_type_get(str, script_len);
2946 while (script_len > 0)
2948 Evas_Font_Instance *cur_fi = NULL;
2949 int run_len = script_len;
2950 ti = _layout_text_item_new(c, fmt);
2951 ti->parent.text_node = n;
2952 ti->parent.text_pos = start + str - tbase;
2954 if (ti->parent.format->font.font)
2956 run_len = c->ENFN->font_run_end_get(c->ENDT,
2957 ti->parent.format->font.font, &script_fi, &cur_fi,
2958 script, str, script_len);
2961 evas_common_text_props_bidi_set(&ti->text_props,
2962 c->par->bidi_props, ti->parent.text_pos);
2963 evas_common_text_props_script_set(&ti->text_props, script);
2967 c->ENFN->font_text_props_info_create(c->ENDT,
2968 cur_fi, str, &ti->text_props, c->par->bidi_props,
2969 ti->parent.text_pos, run_len);
2972 script_len -= run_len;
2974 _layout_text_add_logical_item(c, ti, NULL);
2981 * Add a format item from the format node n and the item item.
2983 * @param c the current context- NOT NULL.
2984 * @param n the source format node - not null.
2985 * @param item the format text.
2987 * @return the new format item.
2989 static Evas_Object_Textblock_Format_Item *
2990 _layout_format_item_add(Ctxt *c, Evas_Object_Textblock_Node_Format *n, const char *item, Evas_Object_Textblock_Format *fmt)
2992 Evas_Object_Textblock_Format_Item *fi;
2994 fi = calloc(1, sizeof(Evas_Object_Textblock_Format_Item));
2995 fi->item = eina_stringshare_add(item);
2996 fi->parent.type = EVAS_TEXTBLOCK_ITEM_FORMAT;
2997 fi->parent.format = fmt;
2998 fi->parent.format->ref++;
2999 c->par->logical_items = eina_list_append(c->par->logical_items, fi);
3002 fi->parent.text_node = n->text_node;
3003 /* FIXME: make it more efficient */
3004 fi->parent.text_pos = _evas_textblock_node_format_pos_get(n);
3006 fi->bidi_dir = (evas_bidi_is_rtl_char(
3009 fi->parent.text_pos)) ?
3010 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
3012 fi->bidi_dir = EVAS_BIDI_DIRECTION_LTR;
3020 * Should be call after we finish filling a format.
3024 _format_finalize(Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
3028 of = fmt->font.font;
3030 fmt->font.font = evas_font_load(obj->layer->evas, fmt->font.fdesc,
3031 fmt->font.source, (int)(((double) fmt->font.size) * obj->cur.scale));
3032 if (of) evas_font_free(obj->layer->evas, of);
3037 * Returns true if the item is a tab
3038 * @def _IS_TAB(item)
3040 #define _IS_TAB(item) \
3041 (!strcmp(item, "tab") || !strcmp(item, "\t") || !strcmp(item, "\\t"))
3044 * Returns true if the item is a line spearator, false otherwise
3045 * @def _IS_LINE_SEPARATOR(item)
3047 #define _IS_LINE_SEPARATOR(item) \
3048 (!strcmp(item, "br") || !strcmp(item, "\n") || !strcmp(item, "\\n"))
3051 * Returns true if the item is a paragraph separator, false otherwise
3052 * @def _IS_PARAGRAPH_SEPARATOR(item)
3054 #define _IS_PARAGRAPH_SEPARATOR_SIMPLE(item) \
3055 (!strcmp(item, "ps"))
3058 * Returns true if the item is a paragraph separator, false otherwise
3059 * takes legacy mode into account.
3060 * @def _IS_PARAGRAPH_SEPARATOR(item)
3062 #define _IS_PARAGRAPH_SEPARATOR(o, item) \
3063 (_IS_PARAGRAPH_SEPARATOR_SIMPLE(item) || \
3064 (o->legacy_newline && _IS_LINE_SEPARATOR(item))) /* Paragraph separator */
3068 * Handles a format by processing a format node. It returns the relevant format
3069 * through _fmt and updates the padding through style_pad_*. If needed,
3070 * it creates a format item.
3072 * @param obj the evas object - NOT NULL.
3073 * @param c the current context- NOT NULL.
3074 * @param _fmt the format that holds the result.
3075 * @param n the source format node - not null.
3076 * @param style_pad_l the pad to update.
3077 * @param style_pad_r the pad to update.
3078 * @param style_pad_t the pad to update.
3079 * @param style_pad_b the pad to update.
3080 * @param create_item Create a new format item if true, only process otherwise.
3083 _layout_do_format(const Evas_Object *obj __UNUSED__, Ctxt *c,
3084 Evas_Object_Textblock_Format **_fmt, Evas_Object_Textblock_Node_Format *n,
3085 int *style_pad_l, int *style_pad_r, int *style_pad_t, int *style_pad_b,
3086 Eina_Bool create_item)
3088 Evas_Object_Textblock_Format *fmt = *_fmt;
3089 /* FIXME: comment the algo */
3096 if (!strncmp(s, "item ", 5))
3099 // item size=20x10 href=name
3100 // item relsize=20x10 href=name
3101 // item abssize=20x10 href=name
3103 // optional arguments:
3107 // size == item size (modifies line size) - can be multiplied by
3109 // relsize == relative size (height is current font height, width
3110 // modified accordingly keeping aspect)
3111 // abssize == absolute size (modifies line size) - never mulitplied by
3113 // href == name of item - to be found and matched later and used for
3115 Evas_Object_Textblock_Format_Item *fi;
3117 int vsize = 0, size = 0;
3121 //href = strstr(s, " href=");
3122 p = strstr(s, " vsize=");
3126 if (!strncmp(p, "full", 4)) vsize = VSIZE_FULL;
3127 else if (!strncmp(p, "ascent", 6)) vsize = VSIZE_ASCENT;
3129 p = strstr(s, " size=");
3133 if (sscanf(p, "%ix%i", &w, &h) == 2)
3135 /* this is handled somewhere else because it depends
3136 * on the current scaling factor of the object which
3137 * may change and break because the results of this
3138 * function are cached */
3144 p = strstr(s, " absize=");
3148 if (sscanf(p, "%ix%i", &w, &h) == 2)
3155 p = strstr(s, " relsize=");
3158 /* this is handled somewhere else because it depends
3159 * on the line it resides in, which is not defined
3160 * at this point and will change anyway, which will
3161 * break because the results of this function are
3170 fi = _layout_format_item_add(c, n, s, fmt);
3174 /* For formats items it's usually
3175 the same, we don't handle the
3176 special cases yet. */
3177 fi->parent.w = fi->parent.adv = w;
3180 /* Not sure if it's the best handling, but will do it for now. */
3181 fmt = _layout_format_push(c, fmt, n);
3187 Eina_Bool push_fmt = EINA_FALSE;
3188 if (n->opener && !n->own_closer)
3190 fmt = _layout_format_push(c, fmt, n);
3191 push_fmt = EINA_TRUE;
3193 else if (!n->opener)
3195 fmt = _layout_format_pop(c, n->orig_format);
3197 while ((item = _format_parse(&s)))
3199 if (_format_is_param(item))
3201 /* Only handle it if it's a push format, otherwise,
3202 * don't let overwrite the format stack.. */
3205 _layout_format_value_handle(c, fmt, item);
3208 else if (create_item)
3210 if ((_IS_PARAGRAPH_SEPARATOR(c->o, item)) ||
3211 (_IS_LINE_SEPARATOR(item)))
3213 Evas_Object_Textblock_Format_Item *fi;
3215 fi = _layout_format_item_add(c, n, item, fmt);
3217 fi->parent.w = fi->parent.adv = 0;
3219 else if (_IS_TAB(item))
3221 Evas_Object_Textblock_Format_Item *fi;
3223 fi = _layout_format_item_add(c, n, item, fmt);
3224 fi->parent.w = fi->parent.adv = fmt->tabstops;
3229 _format_finalize(c->obj, fmt);
3233 Evas_Coord pad_l, pad_r, pad_t, pad_b;
3234 pad_l = pad_r = pad_t = pad_b = 0;
3235 evas_text_style_pad_get(fmt->style, &pad_l, &pad_r, &pad_t, &pad_b);
3236 if (pad_l > *style_pad_l) *style_pad_l = pad_l;
3237 if (pad_r > *style_pad_r) *style_pad_r = pad_r;
3238 if (pad_t > *style_pad_t) *style_pad_t = pad_t;
3239 if (pad_b > *style_pad_b) *style_pad_b = pad_b;
3242 if (fmt->underline2)
3243 c->have_underline2 = 1;
3244 else if (fmt->underline || fmt->underline_dash)
3245 c->have_underline = 1;
3250 _layout_update_par(Ctxt *c)
3252 Evas_Object_Textblock_Paragraph *last_par;
3253 last_par = (Evas_Object_Textblock_Paragraph *)
3254 EINA_INLIST_GET(c->par)->prev;
3257 c->par->y = last_par->y + last_par->h;
3265 /* -1 means no wrap */
3267 _layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3268 const Evas_Object_Textblock_Item *it, size_t line_start,
3273 size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
3274 /* Currently not being used, because it doesn't contain relevant
3279 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3282 wrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
3286 uwrap = (size_t) wrap + it->text_pos;
3290 if ((uwrap == line_start) && (it->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3292 uwrap = it->text_pos +
3293 (size_t) evas_common_text_props_cluster_next(
3294 &_ITEM_TEXT(it)->text_props, wrap);
3296 if ((uwrap <= line_start) || (uwrap > len))
3302 /* -1 means no wrap */
3303 #ifdef HAVE_LINEBREAK
3305 /* Allow break means: if we can break after the current char */
3306 #define ALLOW_BREAK(i) \
3307 (breaks[i] <= LINEBREAK_ALLOWBREAK)
3311 #define ALLOW_BREAK(i) \
3316 _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3317 const Evas_Object_Textblock_Item *it, Eina_Bool mixed_wrap,
3318 size_t line_start, const char *breaks)
3320 Eina_Bool wrap_after = EINA_FALSE;
3323 const Eina_Unicode *str = eina_ustrbuf_string_get(
3324 it->text_node->unicode);
3325 int item_start = it->text_pos;
3326 size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
3327 #ifndef HAVE_LINEBREAK
3328 /* Not used without liblinebreak ATM. */
3334 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3337 swrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
3338 /* Avoiding too small textblocks to even contain one char.
3339 * FIXME: This can cause breaking inside ligatures. */
3344 orig_wrap = wrap = swrap + item_start;
3347 if (wrap > line_start)
3349 /* The wrapping point found is the first char of the next string
3350 the rest works on the last char of the previous string.
3351 If it's a whitespace, then it's ok, and no need to go back
3352 because we'll remove it anyway. */
3353 if (!_is_white(str[wrap]))
3354 MOVE_PREV_UNTIL(line_start, wrap);
3355 /* If there's a breakable point inside the text, scan backwards until
3357 while (wrap > line_start)
3359 if (ALLOW_BREAK(wrap))
3364 if ((wrap > line_start) ||
3365 ((wrap == line_start) && (ALLOW_BREAK(wrap)) && (wrap < len)))
3367 /* We found a suitable wrapping point, break here. */
3368 MOVE_NEXT_UNTIL(len, wrap);
3375 return ((orig_wrap >= line_start) && (orig_wrap < len)) ?
3376 ((int) orig_wrap) : -1;
3380 /* Scan forward to find the next wrapping point */
3382 wrap_after = EINA_TRUE;
3387 /* If we need to find the position after the cutting point */
3388 if ((wrap == line_start) || (wrap_after))
3392 return _layout_get_charwrap(c, fmt, it,
3393 line_start, breaks);
3399 if (ALLOW_BREAK(wrap))
3405 if ((wrap < len) && (wrap > line_start))
3407 MOVE_NEXT_UNTIL(len, wrap);
3420 /* -1 means no wrap */
3422 _layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3423 const Evas_Object_Textblock_Item *it, size_t line_start,
3426 return _layout_get_word_mixwrap_common(c, fmt, it, EINA_FALSE, line_start,
3430 /* -1 means no wrap */
3432 _layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3433 const Evas_Object_Textblock_Item *it, size_t line_start,
3436 return _layout_get_word_mixwrap_common(c, fmt, it, EINA_TRUE, line_start,
3440 /* Should be moved inside _layout_ellipsis_item_new once we fix the hack in
3441 * textblock render */
3442 static const Eina_Unicode _ellip_str[2] = { 0x2026, '\0' };
3444 static Evas_Object_Textblock_Text_Item *
3445 _layout_ellipsis_item_new(Ctxt *c, const Evas_Object_Textblock_Item *cur_it)
3447 Evas_Object_Textblock_Text_Item *ellip_ti;
3448 Evas_Script_Type script;
3449 Evas_Font_Instance *script_fi = NULL, *cur_fi;
3450 size_t len = 1; /* The length of _ellip_str */
3452 /* We can free it here, cause there's only one ellipsis item per tb. */
3453 if (c->o->ellip_ti) _item_free(c->obj, NULL, _ITEM(c->o->ellip_ti));
3454 c->o->ellip_ti = ellip_ti = _layout_text_item_new(c,
3455 eina_list_data_get(eina_list_last(c->format_stack)));
3456 ellip_ti->parent.text_node = cur_it->text_node;
3457 ellip_ti->parent.text_pos = cur_it->text_pos;
3458 script = evas_common_language_script_type_get(_ellip_str, len);
3460 evas_common_text_props_bidi_set(&ellip_ti->text_props,
3461 c->par->bidi_props, ellip_ti->parent.text_pos);
3462 evas_common_text_props_script_set (&ellip_ti->text_props, script);
3464 if (ellip_ti->parent.format->font.font)
3466 /* It's only 1 char anyway, we don't need the run end. */
3467 (void) c->ENFN->font_run_end_get(c->ENDT,
3468 ellip_ti->parent.format->font.font, &script_fi, &cur_fi,
3469 script, _ellip_str, len);
3471 c->ENFN->font_text_props_info_create(c->ENDT,
3472 cur_fi, _ellip_str, &ellip_ti->text_props,
3473 c->par->bidi_props, ellip_ti->parent.text_pos, len);
3476 _text_item_update_sizes(c, ellip_ti);
3478 if (cur_it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3480 ellip_ti->parent.text_pos += _ITEM_TEXT(cur_it)->text_props.text_len
3485 ellip_ti->parent.text_pos++;
3496 _layout_handle_ellipsis(Ctxt *c, Evas_Object_Textblock_Item *it, Eina_List *i)
3498 Evas_Object_Textblock_Text_Item *ellip_ti;
3499 Evas_Object_Textblock_Item *last_it;
3502 ellip_ti = _layout_ellipsis_item_new(c, it);
3506 c->w -= ellip_ti->parent.w;
3508 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3510 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3512 wrap = _layout_text_cutoff_get(c, last_it->format, ti);
3513 if ((wrap > 0) && !IS_AT_END(ti, (size_t) wrap))
3515 _layout_item_text_split_strip_white(c, ti, i, wrap);
3517 else if ((wrap == 0) && (c->ln->items))
3519 last_it = _ITEM(EINA_INLIST_GET(c->ln->items)->last);
3522 else if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3524 /* We don't want to add this format item. */
3529 c->w += ellip_ti->parent.w;
3530 /* If we should add this item, do it */
3533 c->ln->items = (Evas_Object_Textblock_Item *)
3534 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3535 EINA_INLIST_GET(it));
3536 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3538 Evas_Object_Textblock_Format_Item *fi;
3539 fi = _ITEM_FORMAT(it);
3543 c->ln->items = (Evas_Object_Textblock_Item *)
3544 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3545 EINA_INLIST_GET(_ITEM(ellip_ti)));
3546 _layout_line_finalize(c, ellip_ti->parent.format);
3551 _layout_paragraph_reorder_lines(Evas_Object_Textblock_Paragraph *par)
3553 Evas_Object_Textblock_Line *ln;
3555 EINA_INLIST_FOREACH(EINA_INLIST_GET(par->lines), ln)
3557 _layout_line_reorder(ln);
3563 _layout_paragraph_render(Evas_Object_Textblock *o,
3564 Evas_Object_Textblock_Paragraph *par)
3568 par->rendered = EINA_TRUE;
3573 _layout_update_bidi_props(o, par);
3574 _layout_paragraph_reorder_lines(par);
3575 /* Clear the bidi props because we don't need them anymore. */
3576 if (par->bidi_props)
3578 evas_bidi_paragraph_props_unref(par->bidi_props);
3579 par->bidi_props = NULL;
3587 /* 0 means go ahead, 1 means break without an error, 2 means
3588 * break with an error, should probably clean this a bit (enum/macro)
3591 _layout_par(Ctxt *c)
3593 Evas_Object_Textblock_Item *it;
3597 char *line_breaks = NULL;
3599 if (!c->par->logical_items)
3602 /* We want to show it. */
3603 c->par->visible = 1;
3605 /* Check if we need to skip this paragraph because it's already layouted
3606 * correctly, and mark handled nodes as dirty. */
3607 c->par->line_no = c->line_no;
3609 if (c->par->text_node)
3611 /* Skip this paragraph if width is the same, there is no ellipsis
3612 * and we aren't just calculating. */
3613 if (!c->par->text_node->is_new && !c->par->text_node->dirty &&
3614 !c->width_changed && c->par->lines &&
3615 !c->o->have_ellipsis)
3617 Evas_Object_Textblock_Line *ln;
3618 /* Update c->line_no */
3619 ln = (Evas_Object_Textblock_Line *)
3620 EINA_INLIST_GET(c->par->lines)->last;
3622 c->line_no = c->par->line_no + ln->line_no + 1;
3625 c->par->text_node->dirty = EINA_FALSE;
3626 c->par->text_node->is_new = EINA_FALSE;
3627 c->par->rendered = EINA_FALSE;
3629 /* Merge back and clear the paragraph */
3631 Eina_List *itr, *itr_next;
3632 Evas_Object_Textblock_Item *ititr, *prev_it = NULL;
3633 _paragraph_clear(c->obj, c->par);
3634 EINA_LIST_FOREACH_SAFE(c->par->logical_items, itr, itr_next, ititr)
3636 if (ititr->merge && prev_it &&
3637 (prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
3638 (ititr->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3640 _layout_item_merge_and_free(c, _ITEM_TEXT(prev_it),
3642 c->par->logical_items =
3643 eina_list_remove_list(c->par->logical_items, itr);
3655 it = _ITEM(eina_list_data_get(c->par->logical_items));
3656 _layout_line_new(c, it->format);
3657 /* We walk on our own because we want to be able to add items from
3658 * inside the list and then walk them on the next iteration. */
3659 for (i = c->par->logical_items ; i ; )
3663 it = _ITEM(eina_list_data_get(i));
3664 /* Skip visually deleted items */
3665 if (it->visually_deleted)
3667 i = eina_list_next(i);
3671 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3673 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3674 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3675 &c->maxdescent, ti->parent.format);
3679 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
3682 /* If there are no text items yet, calc ascent/descent
3683 * according to the current format. */
3684 if (c->maxascent + c->maxdescent == 0)
3685 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3686 &c->maxdescent, it->format);
3688 _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
3689 &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
3690 fi->parent.adv = fi->parent.w;
3695 /* Check if we need to wrap, i.e the text is bigger than the width,
3696 or we already found a wrap point. */
3698 (((c->x + it->adv) >
3699 (c->w - c->o->style_pad.l - c->o->style_pad.r -
3700 c->marginl - c->marginr)) || (wrap > 0)))
3702 /* Handle ellipsis here. If we don't have more width left
3703 * and no height left, or no more width left and no wrapping. */
3704 if ((it->format->ellipsis == 1.0) && (c->h >= 0) &&
3705 ((2 * it->h + c->y >
3706 c->h - c->o->style_pad.t - c->o->style_pad.b) ||
3707 (!it->format->wrap_word && !it->format->wrap_char &&
3708 !it->format->wrap_mixed)))
3710 _layout_handle_ellipsis(c, it, i);
3714 /* If we want to wrap and it's worth checking for wrapping
3715 * (i.e there's actually text). */
3716 else if ((it->format->wrap_word || it->format->wrap_char ||
3717 it->format->wrap_mixed) && it->text_node)
3722 it_len = (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) ?
3723 1 : _ITEM_TEXT(it)->text_props.text_len;
3726 #ifdef HAVE_LINEBREAK
3727 /* If we haven't calculated the linebreaks yet,
3731 /* Only relevant in those cases */
3732 if (it->format->wrap_word || it->format->wrap_mixed)
3735 lang = (it->format->font.fdesc) ?
3736 it->format->font.fdesc->lang : "";
3738 eina_ustrbuf_length_get(
3739 it->text_node->unicode);
3740 line_breaks = malloc(len);
3741 set_linebreaks_utf32((const utf32_t *)
3742 eina_ustrbuf_string_get(
3743 it->text_node->unicode),
3744 len, lang, line_breaks);
3749 line_start = c->ln->items->text_pos;
3751 line_start = it->text_pos;
3754 /* If we don't already have a wrap point from before */
3757 if (it->format->wrap_word)
3758 wrap = _layout_get_wordwrap(c, it->format, it,
3759 line_start, line_breaks);
3760 else if (it->format->wrap_char)
3761 wrap = _layout_get_charwrap(c, it->format, it,
3762 line_start, line_breaks);
3763 else if (it->format->wrap_mixed)
3764 wrap = _layout_get_mixedwrap(c, it->format, it,
3765 line_start, line_breaks);
3770 /* If it's before the item, rollback and apply.
3771 if it's in the item, cut.
3772 If it's after the item, delay the cut */
3775 size_t uwrap = (size_t) wrap;
3776 if (uwrap < it->text_pos)
3778 /* Rollback latest additions, and cut that
3780 i = eina_list_prev(i);
3781 it = eina_list_data_get(i);
3782 while (uwrap < it->text_pos)
3784 c->ln->items = _ITEM(
3786 EINA_INLIST_GET(c->ln->items),
3787 EINA_INLIST_GET(it)));
3788 i = eina_list_prev(i);
3789 it = eina_list_data_get(i);
3792 c->ln->items = _ITEM(
3794 EINA_INLIST_GET(c->ln->items),
3795 EINA_INLIST_GET(it)));
3798 /* If it points to the end, it means the previous
3799 * char is a whitespace we should remove, so this
3800 * is a wanted cutting point. */
3801 else if (uwrap > it->text_pos + it_len)
3803 /* FIXME: Should redo the ellipsis handling.
3804 * If we can do ellipsis, just cut here. */
3805 if (it->format->ellipsis == 1.0)
3807 _layout_handle_ellipsis(c, it, i);
3813 /* Delay the cut in a smart way i.e use the
3814 item_pos as the line_start, because
3815 there's already no cut before*/
3820 wrap -= it->text_pos; /* Cut here */
3825 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3827 _layout_item_text_split_strip_white(c,
3828 _ITEM_TEXT(it), i, wrap);
3833 /* Should wrap before the item */
3836 _layout_line_advance(c, it->format);
3843 if (!redo_item && !it->visually_deleted)
3845 c->ln->items = (Evas_Object_Textblock_Item *)
3846 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3847 EINA_INLIST_GET(it));
3848 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3850 Evas_Object_Textblock_Format_Item *fi;
3851 fi = _ITEM_FORMAT(it);
3853 /* If it's a newline, and we are not in newline compat
3854 * mode, or we are in newline compat mode, and this is
3855 * not used as a paragraph separator, advance */
3856 if (fi->item && _IS_LINE_SEPARATOR(fi->item) &&
3857 (!c->o->legacy_newline ||
3864 i = eina_list_next(i);
3868 /* Each line is according to the first item in it, and here
3869 * i is already the next item (or the current if we redo it) */
3872 it = _ITEM(eina_list_data_get(i));
3874 _layout_line_advance(c, it->format);
3879 /* Here 'it' is the last format used */
3880 _layout_line_finalize(c, it->format);
3884 #ifdef HAVE_LINEBREAK
3894 * Invalidate text nodes according to format changes
3895 * This goes through all the new format changes and marks the text nodes
3896 * that should be invalidated because of format changes.
3898 * @param c the working context.
3901 _format_changes_invalidate_text_nodes(Ctxt *c)
3903 Evas_Object_Textblock_Node_Format *fnode = c->o->format_nodes;
3904 Evas_Object_Textblock_Node_Text *start_n = NULL;
3905 Eina_List *fstack = NULL;
3911 const char *fstr = fnode->orig_format;
3912 /* balance < 0 means we gave up and everything should be
3914 if (fnode->opener && !fnode->own_closer)
3918 start_n = fnode->text_node;
3919 fstack = eina_list_prepend(fstack, fnode);
3921 else if (!fnode->opener)
3924 fstr_len = strlen(fstr);
3925 /* Generic popper, just pop */
3926 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
3928 fstack = eina_list_remove_list(fstack, fstack);
3931 /* Find the matching format and pop it, if the matching format
3932 * is out format, i.e the last one, pop and break. */
3936 Evas_Object_Textblock_Node_Format *fnode2;
3937 EINA_LIST_FOREACH(fstack, i, fnode2)
3939 if (_FORMAT_IS_CLOSER_OF(
3940 fnode2->orig_format, fstr, fstr_len))
3942 fstack = eina_list_remove_list(fstack, i);
3951 Evas_Object_Textblock_Node_Text *f_tnode =
3955 start_n->dirty = EINA_TRUE;
3956 if (start_n == f_tnode)
3959 _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3964 else if (!fnode->visible)
3969 /* if we don't already have a starting point, use the
3970 * current paragraph. */
3972 start_n = fnode->text_node;
3976 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
3983 start_n->dirty = EINA_TRUE;
3984 start_n = _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3990 /** FIXME: Document */
3992 _layout_pre(Ctxt *c, int *style_pad_l, int *style_pad_r, int *style_pad_t,
3995 Evas_Object *obj = c->obj;
3996 Evas_Object_Textblock *o = c->o;
3997 /* Mark text nodes as dirty if format have changed. */
3998 if (c->o->format_changed)
4000 _format_changes_invalidate_text_nodes(c);
4003 if (o->content_changed)
4005 Evas_Object_Textblock_Node_Text *n;
4006 c->o->have_ellipsis = 0;
4007 c->par = c->paragraphs = o->paragraphs;
4008 /* Go through all the text nodes to create the logical layout */
4009 EINA_INLIST_FOREACH(c->o->text_nodes, n)
4011 Evas_Object_Textblock_Node_Format *fnode;
4015 /* If it's not a new paragraph, either update it or skip it.
4016 * Remove all the paragraphs that were deleted */
4019 /* Remove all the deleted paragraphs at this point */
4020 while (c->par->text_node != n)
4022 Evas_Object_Textblock_Paragraph *tmp_par =
4023 (Evas_Object_Textblock_Paragraph *)
4024 EINA_INLIST_GET(c->par)->next;
4026 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4027 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4028 EINA_INLIST_GET(c->par));
4029 _paragraph_free(obj, c->par);
4034 /* If it's dirty, remove and recreate, if it's clean,
4035 * skip to the next. */
4038 Evas_Object_Textblock_Paragraph *prev_par = c->par;
4040 _layout_paragraph_new(c, n, EINA_TRUE);
4042 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4043 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4044 EINA_INLIST_GET(prev_par));
4045 _paragraph_free(obj, prev_par);
4049 c->par = (Evas_Object_Textblock_Paragraph *)
4050 EINA_INLIST_GET(c->par)->next;
4052 /* Update the format stack according to the node's
4054 fnode = n->format_node;
4055 while (fnode && (fnode->text_node == n))
4057 /* Only do this if this actually changes format */
4058 if (fnode->format_change)
4059 _layout_do_format(obj, c, &c->fmt, fnode,
4060 style_pad_l, style_pad_r,
4061 style_pad_t, style_pad_b, EINA_FALSE);
4062 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4069 /* If it's a new paragraph, just add it. */
4070 _layout_paragraph_new(c, n, EINA_FALSE);
4074 _layout_update_bidi_props(c->o, c->par);
4077 /* For each text node to thorugh all of it's format nodes
4078 * append text from the start to the offset of the next format
4079 * using the last format got. if needed it also creates format
4080 * items this is the core algorithm of the layout mechanism.
4081 * Skip the unicode replacement chars when there are because
4082 * we don't want to print them. */
4083 fnode = n->format_node;
4085 while (fnode && (fnode->text_node == n))
4087 off += fnode->offset;
4088 /* No need to skip on the first run, or a non-visible one */
4089 _layout_text_append(c, c->fmt, n, start, off, o->repch);
4090 _layout_do_format(obj, c, &c->fmt, fnode, style_pad_l,
4091 style_pad_r, style_pad_t, style_pad_b, EINA_TRUE);
4092 if ((c->have_underline2) || (c->have_underline))
4094 if (*style_pad_b < c->underline_extend)
4095 *style_pad_b = c->underline_extend;
4096 c->have_underline = 0;
4097 c->have_underline2 = 0;
4098 c->underline_extend = 0;
4110 fnode->is_new = EINA_FALSE;
4111 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4113 _layout_text_append(c, c->fmt, n, start, -1, o->repch);
4115 /* Clear the bidi props because we don't need them anymore. */
4116 if (c->par->bidi_props)
4118 evas_bidi_paragraph_props_unref(c->par->bidi_props);
4119 c->par->bidi_props = NULL;
4122 c->par = (Evas_Object_Textblock_Paragraph *)
4123 EINA_INLIST_GET(c->par)->next;
4126 /* Delete the rest of the layout paragraphs */
4129 Evas_Object_Textblock_Paragraph *tmp_par =
4130 (Evas_Object_Textblock_Paragraph *)
4131 EINA_INLIST_GET(c->par)->next;
4133 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4134 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4135 EINA_INLIST_GET(c->par));
4136 _paragraph_free(obj, c->par);
4140 o->paragraphs = c->paragraphs;
4148 * Create the layout from the nodes.
4150 * @param obj the evas object - NOT NULL.
4151 * @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.
4152 * @param w the object's w, -1 means no wrapping (i.e infinite size)
4153 * @param h the object's h, -1 means inifinte size.
4154 * @param w_ret the object's calculated w.
4155 * @param h_ret the object's calculated h.
4158 _layout(const Evas_Object *obj, int w, int h, int *w_ret, int *h_ret)
4160 Evas_Object_Textblock *o;
4162 int style_pad_l = 0, style_pad_r = 0, style_pad_t = 0, style_pad_b = 0;
4165 o = (Evas_Object_Textblock *)(obj->object_data);
4167 c->obj = (Evas_Object *)obj;
4169 c->paragraphs = c->par = NULL;
4170 c->format_stack = NULL;
4175 c->wmax = c->hmax = 0;
4176 c->maxascent = c->maxdescent = 0;
4177 c->marginl = c->marginr = 0;
4178 c->have_underline = 0;
4179 c->have_underline2 = 0;
4180 c->underline_extend = 0;
4183 c->align_auto = EINA_TRUE;
4185 c->width_changed = (obj->cur.geometry.w != o->last_w);
4187 /* Start of logical layout creation */
4188 /* setup default base style */
4190 Eina_Bool finalize = EINA_FALSE;
4191 if ((c->o->style) && (c->o->style->default_tag))
4193 c->fmt = _layout_format_push(c, NULL, NULL);
4194 _format_fill(c->obj, c->fmt, c->o->style->default_tag);
4195 finalize = EINA_TRUE;
4198 if ((c->o->style_user) && (c->o->style_user->default_tag))
4202 c->fmt = _layout_format_push(c, NULL, NULL);
4204 _format_fill(c->obj, c->fmt, c->o->style_user->default_tag);
4205 finalize = EINA_TRUE;
4209 _format_finalize(c->obj, c->fmt);
4213 if (w_ret) *w_ret = 0;
4214 if (h_ret) *h_ret = 0;
4218 _layout_pre(c, &style_pad_l, &style_pad_r, &style_pad_t, &style_pad_b);
4219 c->paragraphs = o->paragraphs;
4221 /* If there are no paragraphs, create the minimum needed,
4222 * if the last paragraph has no lines/text, create that as well */
4225 _layout_paragraph_new(c, NULL, EINA_TRUE);
4226 o->paragraphs = c->paragraphs;
4228 c->par = (Evas_Object_Textblock_Paragraph *)
4229 EINA_INLIST_GET(c->paragraphs)->last;
4230 if (!c->par->logical_items)
4232 Evas_Object_Textblock_Text_Item *ti;
4233 ti = _layout_text_item_new(c, c->fmt);
4234 ti->parent.text_node = c->par->text_node;
4235 ti->parent.text_pos = 0;
4236 _layout_text_add_logical_item(c, ti, NULL);
4239 /* End of logical layout creation */
4241 /* Start of visual layout creation */
4243 Evas_Object_Textblock_Paragraph *last_vis_par = NULL;
4244 int par_index_step = o->num_paragraphs / TEXTBLOCK_PAR_INDEX_SIZE;
4245 int par_count = 1; /* Force it to take the first one */
4246 int par_index_pos = 0;
4248 if (par_index_step == 0) par_index_step = 1;
4250 /* Clear all of the index */
4251 memset(o->par_index, 0, sizeof(o->par_index));
4253 EINA_INLIST_FOREACH(c->paragraphs, c->par)
4255 _layout_update_par(c);
4257 /* Break if we should stop here. */
4260 last_vis_par = c->par;
4264 if ((par_index_pos < TEXTBLOCK_PAR_INDEX_SIZE) && (--par_count == 0))
4266 par_count = par_index_step;
4268 o->par_index[par_index_pos++] = c->par;
4272 /* Mark all the rest of the paragraphs as invisible */
4275 c->par = (Evas_Object_Textblock_Paragraph *)
4276 EINA_INLIST_GET(c->par)->next;
4279 c->par->visible = 0;
4280 c->par = (Evas_Object_Textblock_Paragraph *)
4281 EINA_INLIST_GET(c->par)->next;
4285 /* Get the last visible paragraph in the layout */
4286 if (!last_vis_par && c->paragraphs)
4287 last_vis_par = (Evas_Object_Textblock_Paragraph *)
4288 EINA_INLIST_GET(c->paragraphs)->last;
4291 c->hmax = last_vis_par->y + last_vis_par->h;
4294 /* Clean the rest of the format stack */
4295 while (c->format_stack)
4297 c->fmt = c->format_stack->data;
4298 c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
4299 _format_unref_free(c->obj, c->fmt);
4302 if (w_ret) *w_ret = c->wmax;
4303 if (h_ret) *h_ret = c->hmax;
4305 /* Vertically align the textblock */
4306 if ((o->valign > 0.0) && (c->h > c->hmax))
4308 Evas_Coord adjustment = (c->h - c->hmax) * o->valign;
4309 Evas_Object_Textblock_Paragraph *par;
4310 EINA_INLIST_FOREACH(c->paragraphs, par)
4312 par->y += adjustment;
4316 if ((o->style_pad.l != style_pad_l) || (o->style_pad.r != style_pad_r) ||
4317 (o->style_pad.t != style_pad_t) || (o->style_pad.b != style_pad_b))
4319 o->style_pad.l = style_pad_l;
4320 o->style_pad.r = style_pad_r;
4321 o->style_pad.t = style_pad_t;
4322 o->style_pad.b = style_pad_b;
4323 _paragraphs_clear(obj, c->paragraphs);
4324 _layout(obj, w, h, w_ret, h_ret);
4330 * Relayout the object according to current object size.
4332 * @param obj the evas object - NOT NULL.
4335 _relayout(const Evas_Object *obj)
4337 Evas_Object_Textblock *o;
4339 o = (Evas_Object_Textblock *)(obj->object_data);
4340 _layout(obj, obj->cur.geometry.w, obj->cur.geometry.h,
4341 &o->formatted.w, &o->formatted.h);
4342 o->formatted.valid = 1;
4343 o->last_w = obj->cur.geometry.w;
4344 o->last_h = obj->cur.geometry.h;
4346 o->content_changed = 0;
4347 o->format_changed = EINA_FALSE;
4353 * Find the layout item and line that match the text node and position passed.
4355 * @param obj the evas object - NOT NULL.
4356 * @param n the text node - Not null.
4357 * @param pos the position to look for - valid.
4358 * @param[out] lnr the line found - not null.
4359 * @param[out] tir the item found - not null.
4360 * @see _find_layout_format_item_line_match()
4363 _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)
4365 Evas_Object_Textblock_Paragraph *found_par;
4366 Evas_Object_Textblock_Line *ln;
4367 Evas_Object_Textblock *o;
4369 o = (Evas_Object_Textblock *)(obj->object_data);
4370 if (!o->formatted.valid) _relayout(obj);
4375 _layout_paragraph_render(o, found_par);
4376 EINA_INLIST_FOREACH(found_par->lines, ln)
4378 Evas_Object_Textblock_Item *it;
4380 EINA_INLIST_FOREACH(ln->items, it)
4382 /* FIXME: p should be size_t, same goes for pos */
4383 int p = (int) it->text_pos;
4385 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
4387 Evas_Object_Textblock_Text_Item *ti =
4390 p += (int) ti->text_props.text_len;
4397 if (((pos >= (int) it->text_pos) && (pos < p)))
4415 * Return the line number 'line'.
4417 * @param obj the evas object - NOT NULL.
4418 * @param line the line to find
4419 * @return the line of line number or NULL if no line found.
4421 static Evas_Object_Textblock_Line *
4422 _find_layout_line_num(const Evas_Object *obj, int line)
4424 Evas_Object_Textblock_Paragraph *par;
4425 Evas_Object_Textblock_Line *ln;
4426 Evas_Object_Textblock *o;
4428 o = (Evas_Object_Textblock *)(obj->object_data);
4430 par = _layout_find_paragraph_by_line_no(o, line);
4433 _layout_paragraph_render(o, par);
4434 EINA_INLIST_FOREACH(par->lines, ln)
4436 if (par->line_no + ln->line_no == line) return ln;
4443 evas_object_textblock_add(Evas *e)
4447 MAGIC_CHECK(e, Evas, MAGIC_EVAS);
4450 obj = evas_object_new(e);
4451 evas_object_textblock_init(obj);
4452 evas_object_inject(obj, e);
4456 EAPI Evas_Textblock_Style *
4457 evas_textblock_style_new(void)
4459 Evas_Textblock_Style *ts;
4461 ts = calloc(1, sizeof(Evas_Textblock_Style));
4466 evas_textblock_style_free(Evas_Textblock_Style *ts)
4479 evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
4485 /* If the style wasn't really changed, abort. */
4486 if ((!ts->style_text && !text) ||
4487 (ts->style_text && text && !strcmp(text, ts->style_text)))
4490 EINA_LIST_FOREACH(ts->objects, l, obj)
4492 Evas_Object_Textblock *o;
4494 o = (Evas_Object_Textblock *)(obj->object_data);
4495 _evas_textblock_invalidate_all(o);
4496 _evas_textblock_changed(o, obj);
4499 _style_replace(ts, text);
4503 // format MUST be KEY='VALUE'[KEY='VALUE']...
4505 const char *key_start, *key_stop, *val_start;
4507 key_start = key_stop = val_start = NULL;
4513 if (!isspace((unsigned char)(*p)))
4518 if ((*p == '=') || (isspace((unsigned char)(*p))))
4521 else if (!val_start)
4523 if (((*p) == '\'') && (*(p + 1)))
4528 if ((key_start) && (key_stop) && (val_start))
4530 char *tags, *replaces = NULL;
4531 Evas_Object_Style_Tag *tag;
4532 const char *val_stop = NULL;
4537 Eina_Strbuf *buf = eina_strbuf_new();
4538 val_stop = val_start;
4543 /* Break if we found the tag end */
4546 eina_strbuf_append_length(buf, val_stop,
4552 eina_strbuf_append_length(buf, val_stop,
4554 eina_strbuf_append_char(buf, '\'');
4560 replaces = eina_strbuf_string_steal(buf);
4561 eina_strbuf_free(buf);
4563 /* If we didn't find an end, just aboart. */
4566 if (replaces) free(replaces);
4570 tag_len = key_stop - key_start;
4571 replace_len = val_stop - val_start;
4573 tags = malloc(tag_len + 1);
4576 memcpy(tags, key_start, tag_len);
4580 if ((tags) && (replaces))
4582 if (!strcmp(tags, "DEFAULT"))
4584 ts->default_tag = replaces;
4589 tag = calloc(1, sizeof(Evas_Object_Style_Tag));
4592 tag->tag.tag = tags;
4593 tag->tag.replace = replaces;
4594 tag->tag.tag_len = tag_len;
4595 tag->tag.replace_len = replace_len;
4596 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
4607 if (tags) free(tags);
4608 if (replaces) free(replaces);
4610 key_start = key_stop = val_start;
4618 evas_textblock_style_get(const Evas_Textblock_Style *ts)
4620 if (!ts) return NULL;
4621 return ts->style_text;
4624 /* textblock styles */
4627 _textblock_style_generic_set(Evas_Object *obj, Evas_Textblock_Style *ts,
4628 Evas_Textblock_Style **obj_ts)
4631 if (ts == *obj_ts) return;
4632 if ((ts) && (ts->delete_me)) return;
4635 Evas_Textblock_Style *old_ts;
4638 free(o->markup_text);
4639 o->markup_text = NULL;
4643 old_ts->objects = eina_list_remove(old_ts->objects, obj);
4644 if ((old_ts->delete_me) && (!old_ts->objects))
4645 evas_textblock_style_free(old_ts);
4649 ts->objects = eina_list_append(ts->objects, obj);
4653 _evas_textblock_invalidate_all(o);
4654 _evas_textblock_changed(o, obj);
4658 evas_object_textblock_style_set(Evas_Object *obj, Evas_Textblock_Style *ts)
4661 _textblock_style_generic_set(obj, ts, &(o->style));
4664 EAPI const Evas_Textblock_Style *
4665 evas_object_textblock_style_get(const Evas_Object *obj)
4667 TB_HEAD_RETURN(NULL);
4672 evas_object_textblock_style_user_set(Evas_Object *obj, Evas_Textblock_Style *ts)
4675 _textblock_style_generic_set(obj, ts, &(o->style_user));
4678 EAPI const Evas_Textblock_Style *
4679 evas_object_textblock_style_user_get(const Evas_Object *obj)
4681 TB_HEAD_RETURN(NULL);
4682 return o->style_user;
4686 evas_object_textblock_replace_char_set(Evas_Object *obj, const char *ch)
4689 if (o->repch) eina_stringshare_del(o->repch);
4690 if (ch) o->repch = eina_stringshare_add(ch);
4691 else o->repch = NULL;
4692 _evas_textblock_invalidate_all(o);
4693 _evas_textblock_changed(o, obj);
4697 evas_object_textblock_legacy_newline_set(Evas_Object *obj, Eina_Bool mode)
4700 if (o->legacy_newline == mode)
4703 o->legacy_newline = mode;
4704 /* FIXME: Should recreate all the textnodes... For now, it's just
4705 * for new text inserted. */
4709 evas_object_textblock_legacy_newline_get(const Evas_Object *obj)
4711 TB_HEAD_RETURN(EINA_FALSE);
4712 return o->legacy_newline;
4716 evas_object_textblock_valign_set(Evas_Object *obj, double align)
4719 if (align < 0.0) align = 0.0;
4720 else if (align > 1.0) align = 1.0;
4721 if (o->valign == align) return;
4723 _evas_textblock_changed(o, obj);
4727 evas_object_textblock_valign_get(const Evas_Object *obj)
4729 TB_HEAD_RETURN(0.0);
4734 evas_object_textblock_bidi_delimiters_set(Evas_Object *obj, const char *delim)
4737 eina_stringshare_replace(&o->bidi_delimiters, delim);
4741 evas_object_textblock_bidi_delimiters_get(const Evas_Object *obj)
4743 TB_HEAD_RETURN(NULL);
4744 return o->bidi_delimiters;
4748 evas_object_textblock_replace_char_get(Evas_Object *obj)
4750 TB_HEAD_RETURN(NULL);
4756 * Advance p_buff to point after the end of the string. It's used with the
4757 * @ref escaped_strings[] variable.
4759 * @param p_buff the pointer to the current string.
4762 _escaped_advance_after_end_of_string(const char **p_buf)
4764 while (**p_buf != 0) (*p_buf)++;
4770 * Advance p_buff to point after the end of the string. It's used with the
4771 * @ref escaped_strings[] variable. Also chec if matches.
4774 * @param p_buff the pointer to the current string.
4777 _escaped_is_eq_and_advance(const char *s, const char *s_end,
4778 const char **p_m, const char *m_end)
4780 Eina_Bool reached_end;
4781 for (;((s < s_end) && (*p_m < m_end)); s++, (*p_m)++)
4785 _escaped_advance_after_end_of_string(p_m);
4790 reached_end = !**p_m;
4792 _escaped_advance_after_end_of_string(p_m);
4794 return ((s == s_end) && reached_end);
4800 * @param s the string to match
4802 static inline const char *
4803 _escaped_char_match(const char *s, int *adv)
4805 const char *map_itr, *map_end, *mc, *sc;
4807 map_itr = escape_strings;
4808 map_end = map_itr + sizeof(escape_strings);
4810 while (map_itr < map_end)
4816 _escaped_advance_after_end_of_string(&map_itr);
4817 if (map_itr >= map_end) break;
4822 while ((*mc) && (*sc))
4824 if ((unsigned char)*sc < (unsigned char)*mc) return NULL;
4835 *adv = mc - map_itr;
4838 _escaped_advance_after_end_of_string(&map_itr);
4847 * @param s the string to match
4849 static inline const char *
4850 _escaped_char_get(const char *s, const char *s_end)
4852 /* Handle numeric escape codes. */
4855 static char utf8_escape[7]; /* Support up to 6 bytes utf8 */
4857 Eina_Unicode uchar[2] = { 0, 0 };
4861 s += 2; /* Skip "&#" */
4863 if (tolower((unsigned char)(*s)) == 'x')
4870 if (len >= sizeof(ustr) + 1)
4873 memcpy(ustr, s, len);
4875 uchar[0] = strtol(ustr, NULL, base);
4880 utf8_char = eina_unicode_unicode_to_utf8(uchar, NULL);
4881 strcpy(utf8_escape, utf8_char);
4888 const char *map_itr, *map_end;
4890 map_itr = escape_strings;
4891 map_end = map_itr + sizeof(escape_strings);
4893 while (map_itr < map_end)
4895 if (_escaped_is_eq_and_advance(s, s_end, &map_itr, map_end))
4897 if (map_itr < map_end)
4898 _escaped_advance_after_end_of_string(&map_itr);
4906 evas_textblock_escape_string_get(const char *escape)
4909 return _escaped_char_get(escape, escape + strlen(escape));
4913 evas_textblock_escape_string_range_get(const char *escape_start, const char *escape_end)
4915 return _escaped_char_get(escape_start, escape_end);
4919 evas_textblock_string_escape_get(const char *string, int *len_ret)
4922 return _escaped_char_match(string, len_ret);
4927 * Appends the escaped char beteewn s and s_end to the curosr
4930 * @param s the start of the string
4931 * @param s_end the end of the string.
4934 _append_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4939 escape = _escaped_char_get(s, s_end);
4941 evas_textblock_cursor_text_append(cur, escape);
4946 * prepends the escaped char beteewn s and s_end to the curosr
4949 * @param s the start of the string
4950 * @param s_end the end of the string.
4953 _prepend_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4958 escape = _escaped_char_get(s, s_end);
4960 evas_textblock_cursor_text_prepend(cur, escape);
4965 evas_object_textblock_text_markup_set(Evas_Object *obj, const char *text)
4968 if ((text != o->markup_text) && (o->markup_text))
4970 free(o->markup_text);
4971 o->markup_text = NULL;
4974 if (!o->style && !o->style_user)
4976 if (text != o->markup_text)
4978 if (text) o->markup_text = strdup(text);
4982 evas_textblock_cursor_paragraph_first(o->cursor);
4984 evas_object_textblock_text_markup_prepend(o->cursor, text);
4985 /* Point all the cursors to the starrt */
4988 Evas_Textblock_Cursor *data;
4990 evas_textblock_cursor_paragraph_first(o->cursor);
4991 EINA_LIST_FOREACH(o->cursors, l, data)
4992 evas_textblock_cursor_paragraph_first(data);
4997 evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char *text)
4999 Evas_Object *obj = cur->obj;
5004 char *tag_start, *tag_end, *esc_start, *esc_end;
5006 tag_start = tag_end = esc_start = esc_end = NULL;
5009 /* This loop goes through all of the mark up text until it finds format
5010 * tags, escape sequences or the terminating NULL. When it finds either
5011 * of those, it appends the text found up until that point to the textblock
5012 * proccesses whatever found. It repeats itself until the termainating
5013 * NULL is reached. */
5017 /* If we got to the end of string or just finished/started tag
5018 * or escape sequence handling. */
5020 (tag_end) || (esc_end) ||
5021 (tag_start) || (esc_start))
5025 /* If we reached to a tag ending, analyze the tag */
5027 size_t ttag_len = tag_end - tag_start;
5030 ttag = malloc(ttag_len + 1);
5033 memcpy(ttag, tag_start, ttag_len);
5035 evas_textblock_cursor_format_prepend(cur, ttag);
5038 tag_start = tag_end = NULL;
5042 _prepend_escaped_char(cur, esc_start, esc_end + 1);
5043 esc_start = esc_end = NULL;
5047 _prepend_text_run(cur, s, p);
5057 /* Append the text prior to this to the textblock and mark
5058 * the start of the tag */
5061 _prepend_text_run(cur, s, p);
5077 /* Append the text prior to this to the textblock and mark
5078 * the start of the escape sequence */
5081 _prepend_text_run(cur, s, p);
5093 /* Unicode object replcament char */
5094 else if (!strncmp(_REPLACEMENT_CHAR_UTF8, p,
5095 text_len = strlen(_REPLACEMENT_CHAR_UTF8)) ||
5096 !strncmp(_NEWLINE_UTF8, p,
5097 text_len = strlen(_NEWLINE_UTF8)) ||
5098 !strncmp(_TAB_UTF8, p,
5099 text_len = strlen(_TAB_UTF8)) ||
5100 !strncmp(_PARAGRAPH_SEPARATOR_UTF8, p,
5101 text_len = strlen(_PARAGRAPH_SEPARATOR_UTF8)))
5103 /*FIXME: currently just remove them, maybe do something
5104 * fancier in the future, atm it breaks if this char
5106 _prepend_text_run(cur, s, p);
5107 /* it's also advanced later in this loop need +text_len
5110 s = p + 1; /* One after the end of the replacement char */
5115 _evas_textblock_changed(o, obj);
5121 * An helper function to markup get. Appends the format from fnode to the strbugf txt.
5123 * @param o the textblock object.
5124 * @param txt the strbuf to append to.
5125 * @param fnode the format node to process.
5128 _markup_get_format_append(Eina_Strbuf *txt, Evas_Object_Textblock_Node_Format *fnode)
5130 eina_strbuf_append_char(txt, '<');
5134 // FIXME: need to escape
5135 s = fnode->orig_format;
5136 if (!fnode->opener && !fnode->own_closer)
5137 eina_strbuf_append_char(txt, '/');
5138 eina_strbuf_append(txt, s);
5139 if (fnode->own_closer)
5140 eina_strbuf_append_char(txt, '/');
5142 eina_strbuf_append_char(txt, '>');
5147 * An helper function to markup get. Appends the text in text.
5149 * @param txt the strbuf to append to.
5150 * @param text the text to process.
5153 _markup_get_text_append(Eina_Strbuf *txt, const Eina_Unicode *text)
5155 char *p = eina_unicode_unicode_to_utf8(text, NULL);
5162 escape = _escaped_char_match(p, &adv);
5166 eina_strbuf_append(txt, escape);
5170 eina_strbuf_append_char(txt, *p);
5177 evas_object_textblock_text_markup_get(const Evas_Object *obj)
5179 Evas_Object_Textblock_Node_Text *n;
5180 Eina_Strbuf *txt = NULL;
5182 TB_HEAD_RETURN(NULL);
5183 if (o->markup_text) return(o->markup_text);
5184 txt = eina_strbuf_new();
5185 EINA_INLIST_FOREACH(o->text_nodes, n)
5187 Evas_Object_Textblock_Node_Format *fnode;
5188 Eina_Unicode *text_base, *text;
5191 /* For each text node to thorugh all of it's format nodes
5192 * append text from the start to the offset of the next format
5193 * using the last format got. if needed it also creates format items
5194 * this is the core algorithm of the layout mechanism.
5195 * Skip the unicode replacement chars when there are because
5196 * we don't want to print them. */
5198 eina_unicode_strndup(eina_ustrbuf_string_get(n->unicode),
5199 eina_ustrbuf_length_get(n->unicode));
5200 fnode = n->format_node;
5202 while (fnode && (fnode->text_node == n))
5204 Eina_Unicode tmp_ch;
5205 off += fnode->offset;
5206 /* No need to skip on the first run */
5208 text[off] = 0; /* Null terminate the part of the string */
5209 _markup_get_text_append(txt, text);
5210 _markup_get_format_append(txt, fnode);
5211 text[off] = tmp_ch; /* Restore the char */
5222 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
5224 /* Add the rest, skip replacement */
5225 _markup_get_text_append(txt, text);
5230 o->markup_text = eina_strbuf_string_steal(txt);
5231 eina_strbuf_free(txt);
5232 return o->markup_text;
5236 evas_textblock_text_markup_to_utf8(const Evas_Object *obj, const char *text)
5238 /* FIXME: Redundant and awful, should be merged with markup_prepend */
5241 char *tag_start, *tag_end, *esc_start, *esc_end;
5243 if (!text) return NULL;
5246 tag_start = tag_end = esc_start = esc_end = NULL;
5247 sbuf = eina_strbuf_new();
5250 /* This loop goes through all of the mark up text until it finds format
5251 * tags, escape sequences or the terminating NULL. When it finds either
5252 * of those, it appends the text found up until that point to the textblock
5253 * proccesses whatever found. It repeats itself until the termainating
5254 * NULL is reached. */
5257 /* If we got to the end of string or just finished/started tag
5258 * or escape sequence handling. */
5260 (tag_end) || (esc_end) ||
5261 (tag_start) || (esc_start))
5265 /* If we reached to a tag ending, analyze the tag */
5269 tag_start++; /* Skip the < */
5270 tag_end--; /* Skip the > */
5271 if ((tag_end > tag_start) && (*(tag_end - 1) == '/'))
5273 tag_end --; /* Skip the terminating '/' */
5274 while (*(tag_end - 1) == ' ')
5275 tag_end--; /* skip trailing ' ' */
5278 ttag_len = tag_end - tag_start;
5280 ttag = malloc(ttag_len + 1);
5283 const char *match = NULL;
5285 memcpy(ttag, tag_start, ttag_len);
5291 match = _style_match_tag(
5292 evas_object_textblock_style_get(obj),
5293 ttag, ttag_len, &replace_len);
5296 if (!match) match = ttag;
5298 if (_IS_PARAGRAPH_SEPARATOR_SIMPLE(match))
5299 eina_strbuf_append(sbuf, _PARAGRAPH_SEPARATOR_UTF8);
5300 else if (_IS_LINE_SEPARATOR(match))
5301 eina_strbuf_append(sbuf, _NEWLINE_UTF8);
5302 else if (_IS_TAB(match))
5303 eina_strbuf_append(sbuf, _TAB_UTF8);
5304 else if (!strncmp(match, "item", 4))
5305 eina_strbuf_append(sbuf, _REPLACEMENT_CHAR_UTF8);
5309 tag_start = tag_end = NULL;
5315 escape = _escaped_char_get(esc_start, esc_end + 1);
5316 if (escape) eina_strbuf_append(sbuf, escape);
5317 esc_start = esc_end = NULL;
5321 eina_strbuf_append_length(sbuf, s, p - s);
5331 /* Append the text prior to this to the textblock and
5332 * mark the start of the tag */
5335 eina_strbuf_append_length(sbuf, s, p - s);
5351 /* Append the text prior to this to the textblock and mark
5352 * the start of the escape sequence */
5355 eina_strbuf_append_length(sbuf, s, p - s);
5370 ret = eina_strbuf_string_steal(sbuf);
5371 eina_strbuf_free(sbuf);
5376 evas_textblock_text_utf8_to_markup(const Evas_Object *obj, const char *text)
5380 int ch, pos = 0, pos2 = 0;
5384 if (!text) return NULL;
5386 sbuf = eina_strbuf_new();
5391 pos2 = evas_string_char_next_get(text, pos2, &ch);
5392 if ((ch <= 0) || (pos2 <= 0)) break;
5395 eina_strbuf_append(sbuf, "<br/>");
5396 else if (ch == _TAB)
5397 eina_strbuf_append(sbuf, "<tab/>");
5399 eina_strbuf_append(sbuf, "<");
5401 eina_strbuf_append(sbuf, ">");
5403 eina_strbuf_append(sbuf, "&");
5404 else if (ch == _PARAGRAPH_SEPARATOR)
5405 eina_strbuf_append(sbuf, "<ps/>");
5406 else if (ch == _REPLACEMENT_CHAR)
5407 eina_strbuf_append(sbuf, "");
5410 eina_strbuf_append_length(sbuf, text + pos, pos2 - pos);
5413 str = eina_strbuf_string_steal(sbuf);
5414 eina_strbuf_free(sbuf);
5423 * Merge the current node with the next, no need to remove PS, already
5426 * @param o the text block object.
5427 * @param to merge into to.
5430 _evas_textblock_nodes_merge(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *to)
5432 Evas_Object_Textblock_Node_Format *itr;
5433 Evas_Object_Textblock_Node_Format *pnode;
5434 Evas_Object_Textblock_Node_Text *from;
5435 const Eina_Unicode *text;
5439 from = _NODE_TEXT(EINA_INLIST_GET(to)->next);
5441 to_len = eina_ustrbuf_length_get(to->unicode);
5442 text = eina_ustrbuf_string_get(from->unicode);
5443 len = eina_ustrbuf_length_get(from->unicode);
5444 eina_ustrbuf_append_length(to->unicode, text, len);
5446 itr = from->format_node;
5447 if (itr && (itr->text_node == from))
5449 pnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->prev);
5450 if (pnode && (pnode->text_node == to))
5452 itr->offset += to_len - _evas_textblock_node_format_pos_get(pnode);
5456 itr->offset += to_len;
5460 while (itr && (itr->text_node == from))
5462 itr->text_node = to;
5463 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
5465 if (!to->format_node || (to->format_node->text_node != to))
5467 to->format_node = from->format_node;
5470 /* When it comes to how we handle it, merging is like removing both nodes
5471 * and creating a new one, se we need to do the needed cleanups. */
5473 to->par->text_node = NULL;
5476 to->is_new = EINA_TRUE;
5478 _evas_textblock_cursors_set_node(o, from, to);
5479 _evas_textblock_node_text_remove(o, from);
5484 * Merge the current node with the next, no need to remove PS, already
5487 * @param cur the cursor that points to the current node
5490 _evas_textblock_cursor_nodes_merge(Evas_Textblock_Cursor *cur)
5492 Evas_Object_Textblock_Node_Text *nnode;
5493 Evas_Object_Textblock *o;
5497 len = eina_ustrbuf_length_get(cur->node->unicode);
5499 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5500 nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5501 _evas_textblock_nodes_merge(o, cur->node);
5502 _evas_textblock_cursors_update_offset(cur, nnode, 0, len);
5503 _evas_textblock_cursors_set_node(o, nnode, cur->node);
5504 if (nnode == o->cursor->node)
5506 o->cursor->node = cur->node;
5507 o->cursor->pos += len;
5513 * Return the format at a specific position.
5515 * @param cur the cursor to the position.
5516 * @return the format node at the specific position or NULL if not found.
5518 static Evas_Object_Textblock_Node_Format *
5519 _evas_textblock_cursor_node_format_at_pos_get(const Evas_Textblock_Cursor *cur)
5521 Evas_Object_Textblock_Node_Format *node;
5522 Evas_Object_Textblock_Node_Format *itr;
5525 if (!cur->node) return NULL;
5527 node = cur->node->format_node;
5528 if (!node) return NULL;
5529 /* If there is no exclusive format node to this paragraph return the
5530 * previous's node */
5531 /* Find the main format node */
5532 EINA_INLIST_FOREACH(node, itr)
5534 if (itr->text_node != cur->node)
5538 if ((position + itr->offset) == cur->pos)
5542 position += itr->offset;
5549 * Return the last format node at the position of the format node n.
5551 * @param n a format node at the position.
5552 * @return the last format node at the position of n.
5554 static Evas_Object_Textblock_Node_Format *
5555 _evas_textblock_node_format_last_at_off(const Evas_Object_Textblock_Node_Format *n)
5557 const Evas_Object_Textblock_Node_Format *nnode;
5558 const Evas_Object_Textblock_Node_Text *tnode;
5559 if (!n) return NULL;
5561 tnode = n->text_node;
5565 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5567 while (nnode && (nnode->text_node == tnode) && (nnode->offset == 0));
5569 return (Evas_Object_Textblock_Node_Format *) n;
5574 * Returns the visible format at a specific location.
5576 * @param n a format at the specific position.
5577 * @return the format node at the specific position or NULL if not found.
5579 static Evas_Object_Textblock_Node_Format *
5580 _evas_textblock_node_visible_at_pos_get(const Evas_Object_Textblock_Node_Format *n)
5582 const Evas_Object_Textblock_Node_Format *nnode;
5583 if (!n) return NULL;
5584 /* The visible format is the last one, because it inserts a replacement
5585 * char that advances the next formats. */
5591 if (n->visible) return (Evas_Object_Textblock_Node_Format *) n;
5592 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5594 while (nnode && (nnode->offset == 0));
5601 * Return the last format that applies to a specific cursor or at the specific
5602 * position the cursor points to. This means either a cursor at or before the
5603 * position of the cursor in the text node is returned or the previous's text
5604 * node's format node.
5606 * @param cur the position to look at.
5607 * @return the format node found.
5609 static Evas_Object_Textblock_Node_Format *
5610 _evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur)
5612 Evas_Object_Textblock_Node_Format *node, *pitr = NULL;
5613 Evas_Object_Textblock_Node_Format *itr;
5614 size_t position = 0;
5616 if (!cur->node) return NULL;
5618 node = cur->node->format_node;
5619 if (!node) return NULL;
5620 /* If there is no exclusive format node to this paragraph return the
5621 * previous's node */
5622 if (node->text_node != cur->node)
5626 else if (node->offset > cur->pos)
5628 return _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5630 /* Find the main format node */
5631 pitr = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5632 EINA_INLIST_FOREACH(node, itr)
5634 if ((itr->text_node != cur->node) ||
5635 ((position + itr->offset) > cur->pos))
5639 else if ((position + itr->offset) == cur->pos)
5644 position += itr->offset;
5651 * Find the layout item and line that match the cursor.
5653 * @param cur the cursor we are currently at. - NOT NULL.
5654 * @param[out] lnr the line found - not null.
5655 * @param[out] itr the item found - not null.
5656 * @return EINA_TRUE if we matched the previous format, EINA_FALSE otherwise.
5659 _find_layout_item_match(const Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
5661 Evas_Textblock_Cursor cur2;
5662 Eina_Bool previous_format = EINA_FALSE;
5664 cur2.obj = cur->obj;
5665 evas_textblock_cursor_copy(cur, &cur2);
5671 if (_evas_textblock_cursor_is_at_the_end(cur) &&
5672 evas_textblock_cursor_format_is_visible_get(&cur2))
5674 _find_layout_item_line_match(cur2.obj, cur2.node, cur2.pos, lnr, itr);
5675 previous_format = EINA_TRUE;
5679 _find_layout_item_line_match(cur->obj, cur->node, cur->pos, lnr, itr);
5681 return previous_format;
5684 EAPI Evas_Textblock_Cursor *
5685 evas_object_textblock_cursor_get(const Evas_Object *obj)
5687 TB_HEAD_RETURN(NULL);
5691 EAPI Evas_Textblock_Cursor *
5692 evas_object_textblock_cursor_new(const Evas_Object *obj)
5694 Evas_Textblock_Cursor *cur;
5696 TB_HEAD_RETURN(NULL);
5697 cur = calloc(1, sizeof(Evas_Textblock_Cursor));
5698 cur->obj = (Evas_Object *) obj;
5699 cur->node = o->text_nodes;
5702 o->cursors = eina_list_append(o->cursors, cur);
5707 evas_textblock_cursor_free(Evas_Textblock_Cursor *cur)
5709 Evas_Object_Textblock *o;
5712 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5713 if (cur == o->cursor) return;
5714 o->cursors = eina_list_remove(o->cursors, cur);
5719 evas_textblock_cursor_is_format(const Evas_Textblock_Cursor *cur)
5721 if (!cur || !cur->node) return EINA_FALSE;
5722 return (_evas_textblock_cursor_node_format_at_pos_get(cur)) ?
5723 EINA_TRUE : EINA_FALSE;
5726 EAPI const Eina_List *
5727 evas_textblock_node_format_list_get(const Evas_Object *obj, const char *anchor)
5729 TB_HEAD_RETURN(NULL);
5730 if (!strcmp(anchor, "a"))
5731 return o->anchors_a;
5732 else if (!strcmp(anchor, "item"))
5733 return o->anchors_item;
5738 EAPI const Evas_Object_Textblock_Node_Format *
5739 evas_textblock_node_format_first_get(const Evas_Object *obj)
5741 TB_HEAD_RETURN(NULL);
5742 return o->format_nodes;
5745 EAPI const Evas_Object_Textblock_Node_Format *
5746 evas_textblock_node_format_last_get(const Evas_Object *obj)
5748 TB_HEAD_RETURN(NULL);
5749 if (o->format_nodes)
5751 return _NODE_FORMAT(EINA_INLIST_GET(o->format_nodes)->last);
5756 EAPI const Evas_Object_Textblock_Node_Format *
5757 evas_textblock_node_format_next_get(const Evas_Object_Textblock_Node_Format *n)
5759 return _NODE_FORMAT(EINA_INLIST_GET(n)->next);
5762 EAPI const Evas_Object_Textblock_Node_Format *
5763 evas_textblock_node_format_prev_get(const Evas_Object_Textblock_Node_Format *n)
5765 return _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
5769 evas_textblock_node_format_remove_pair(Evas_Object *obj,
5770 Evas_Object_Textblock_Node_Format *n)
5772 Evas_Object_Textblock_Node_Text *tnode1;
5773 Evas_Object_Textblock_Node_Format *fmt, *found_node = NULL;
5774 Eina_List *fstack = NULL;
5783 const char *fstr = fmt->orig_format;
5785 if (fmt->opener && !fmt->own_closer)
5787 fstack = eina_list_prepend(fstack, fmt);
5789 else if (fstr && !fmt->opener)
5792 fstr_len = strlen(fstr);
5793 /* Generic popper, just pop */
5794 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
5796 fstack = eina_list_remove_list(fstack, fstack);
5803 /* Find the matching format and pop it, if the matching format
5804 * is out format, i.e the last one, pop and break. */
5808 Evas_Object_Textblock_Node_Format *fnode;
5809 EINA_LIST_FOREACH(fstack, i, fnode)
5811 if (_FORMAT_IS_CLOSER_OF(
5812 fnode->orig_format, fstr, fstr_len))
5814 /* Last one, this is our item! */
5815 if (!eina_list_next(i))
5820 fstack = eina_list_remove_list(fstack, i);
5827 fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
5829 while (fmt && fstack);
5833 fstack = eina_list_free(fstack);
5837 size_t ind = _evas_textblock_node_format_pos_get(n);
5838 const char *format = n->format;
5839 Evas_Textblock_Cursor cur;
5842 eina_ustrbuf_remove(n->text_node->unicode, ind, ind + 1);
5843 if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
5845 evas_textblock_cursor_at_format_set(&cur, n);
5846 _evas_textblock_cursor_nodes_merge(&cur);
5848 _evas_textblock_cursors_update_offset(&cur, n->text_node, ind, -1);
5850 tnode1 = n->text_node;
5851 _evas_textblock_node_format_remove(o, n, 0);
5852 if (found_node && (found_node != n))
5854 Evas_Object_Textblock_Node_Text *tnode2;
5855 tnode2 = found_node->text_node;
5856 /* found_node can never be visible! (it's the closing format) */
5857 _evas_textblock_node_format_remove(o, found_node, 0);
5859 /* FIXME: Should be unified in the layout, for example, added to a list
5860 * that checks this kind of removals. But until then, this is very fast
5862 /* Mark all the text nodes in between the removed formats as dirty. */
5865 tnode1->dirty = EINA_TRUE;
5866 if (tnode1 == tnode2)
5869 _NODE_TEXT(EINA_INLIST_GET(tnode1)->next);
5873 _evas_textblock_changed(o, obj);
5877 evas_textblock_cursor_paragraph_first(Evas_Textblock_Cursor *cur)
5879 Evas_Object_Textblock *o;
5881 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5882 cur->node = o->text_nodes;
5888 evas_textblock_cursor_paragraph_last(Evas_Textblock_Cursor *cur)
5890 Evas_Object_Textblock *o;
5891 Evas_Object_Textblock_Node_Text *node;
5894 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5895 node = o->text_nodes;
5898 node = _NODE_TEXT(EINA_INLIST_GET(node)->last);
5902 evas_textblock_cursor_paragraph_char_last(cur);
5913 evas_textblock_cursor_paragraph_next(Evas_Textblock_Cursor *cur)
5915 if (!cur) return EINA_FALSE;
5916 if (!cur->node) return EINA_FALSE;
5917 /* If there is a current text node, return the next text node (if exists)
5918 * otherwise, just return False. */
5921 Evas_Object_Textblock_Node_Text *nnode;
5922 nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5935 evas_textblock_cursor_paragraph_prev(Evas_Textblock_Cursor *cur)
5937 Evas_Object_Textblock_Node_Text *node;
5938 if (!cur) return EINA_FALSE;
5939 if (!cur->node) return EINA_FALSE;
5940 /* If the current node is a text node, just get the prev if any,
5941 * if it's a format, get the current text node out of the format and return
5942 * the prev text node if any. */
5944 /* If there is a current text node, return the prev text node
5945 * (if exists) otherwise, just return False. */
5948 Evas_Object_Textblock_Node_Text *pnode;
5949 pnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->prev);
5953 evas_textblock_cursor_paragraph_char_last(cur);
5961 evas_textblock_cursor_set_at_format(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *n)
5963 evas_textblock_cursor_at_format_set(cur, n);
5967 evas_textblock_cursor_format_next(Evas_Textblock_Cursor *cur)
5969 Evas_Object_Textblock_Node_Format *node;
5971 if (!cur) return EINA_FALSE;
5972 if (!cur->node) return EINA_FALSE;
5973 /* If the current node is a format node, just get the next if any,
5974 * if it's a text, get the current format node out of the text and return
5975 * the next format node if any. */
5976 node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
5977 node = _evas_textblock_node_format_last_at_off(node);
5980 if (cur->node->format_node)
5982 cur->pos = _evas_textblock_node_format_pos_get(node);
5986 /* If there is a current text node, return the next format node (if exists)
5987 * otherwise, just return False. */
5990 Evas_Object_Textblock_Node_Format *nnode;
5991 nnode = _NODE_FORMAT(EINA_INLIST_GET(node)->next);
5994 cur->node = nnode->text_node;
5995 cur->pos = _evas_textblock_node_format_pos_get(nnode);
6004 evas_textblock_cursor_format_prev(Evas_Textblock_Cursor *cur)
6006 const Evas_Object_Textblock_Node_Format *node;
6007 if (!cur) return EINA_FALSE;
6008 if (!cur->node) return EINA_FALSE;
6009 node = evas_textblock_cursor_format_get(cur);
6012 node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6015 cur->node = node->text_node;
6016 cur->pos = _evas_textblock_node_format_pos_get(node);
6021 /* If there is a current text node, return the next text node (if exists)
6022 * otherwise, just return False. */
6025 Evas_Object_Textblock_Node_Format *pnode;
6026 pnode = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
6029 cur->node = pnode->text_node;
6030 cur->pos = _evas_textblock_node_format_pos_get(pnode);
6038 #ifdef HAVE_LINEBREAK
6040 /* BREAK_AFTER: true if we can break after the current char.
6041 * Both macros assume str[i] is not the terminating nul */
6042 #define BREAK_AFTER(i) \
6043 (breaks[i] == WORDBREAK_BREAK)
6047 #define BREAK_AFTER(i) \
6049 (_is_white(str[i]) && !_is_white(str[i + 1])) || \
6050 (!_is_white(str[i]) && _is_white(str[i + 1])))
6055 evas_textblock_cursor_word_start(Evas_Textblock_Cursor *cur)
6057 const Eina_Unicode *text;
6059 #ifdef HAVE_LINEBREAK
6063 if (!cur) return EINA_FALSE;
6064 if (!cur->node) return EINA_FALSE;
6066 text = eina_ustrbuf_string_get(cur->node->unicode);
6068 #ifdef HAVE_LINEBREAK
6070 const char *lang = ""; /* FIXME: get lang */
6071 size_t len = eina_ustrbuf_length_get(cur->node->unicode);
6072 breaks = malloc(len);
6073 set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
6079 /* Skip the first one. This ensures we don't point to the nul, and also
6080 * we just don't care about it anyway. */
6083 for ( ; i > 0 ; i--)
6087 /* Advance to the current char */
6095 #ifdef HAVE_LINEBREAK
6102 evas_textblock_cursor_word_end(Evas_Textblock_Cursor *cur)
6104 const Eina_Unicode *text;
6106 #ifdef HAVE_LINEBREAK
6110 if (!cur) return EINA_FALSE;
6111 if (!cur->node) return EINA_FALSE;
6113 text = eina_ustrbuf_string_get(cur->node->unicode);
6115 #ifdef HAVE_LINEBREAK
6117 const char *lang = ""; /* FIXME: get lang */
6118 size_t len = eina_ustrbuf_length_get(cur->node->unicode);
6119 breaks = malloc(len);
6120 set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
6126 for ( ; text[i] ; i++)
6130 /* This is the one to break after. */
6137 #ifdef HAVE_LINEBREAK
6144 evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur)
6147 const Eina_Unicode *text;
6149 if (!cur) return EINA_FALSE;
6150 if (!cur->node) return EINA_FALSE;
6153 text = eina_ustrbuf_string_get(cur->node->unicode);
6154 if (text[ind]) ind++;
6155 /* Only allow pointing a null if it's the last paragraph.
6156 * because we don't have a PS there. */
6164 if (!evas_textblock_cursor_paragraph_next(cur))
6166 /* If we already were at the end, that means we don't have
6167 * where to go next we should return FALSE */
6168 if (cur->pos == (size_t) ind)
6182 evas_textblock_cursor_char_prev(Evas_Textblock_Cursor *cur)
6184 if (!cur) return EINA_FALSE;
6185 if (!cur->node) return EINA_FALSE;
6192 return evas_textblock_cursor_paragraph_prev(cur);
6196 evas_textblock_cursor_paragraph_char_first(Evas_Textblock_Cursor *cur)
6204 evas_textblock_cursor_paragraph_char_last(Evas_Textblock_Cursor *cur)
6209 if (!cur->node) return;
6210 ind = eina_ustrbuf_length_get(cur->node->unicode);
6211 /* If it's not the last paragraph, go back one, because we want to point
6212 * to the PS, not the NULL */
6213 if (EINA_INLIST_GET(cur->node)->next)
6224 evas_textblock_cursor_line_char_first(Evas_Textblock_Cursor *cur)
6226 Evas_Object_Textblock *o;
6227 Evas_Object_Textblock_Line *ln = NULL;
6228 Evas_Object_Textblock_Item *it = NULL;
6231 if (!cur->node) return;
6232 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6233 if (!o->formatted.valid) _relayout(cur->obj);
6235 _find_layout_item_match(cur, &ln, &it);
6240 Evas_Object_Textblock_Item *i;
6242 EINA_INLIST_FOREACH(ln->items, i)
6244 if (it->text_pos > i->text_pos)
6252 cur->pos = it->text_pos;
6253 cur->node = it->text_node;
6258 evas_textblock_cursor_line_char_last(Evas_Textblock_Cursor *cur)
6260 Evas_Object_Textblock *o;
6261 Evas_Object_Textblock_Line *ln = NULL;
6262 Evas_Object_Textblock_Item *it = NULL;
6265 if (!cur->node) return;
6266 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6267 if (!o->formatted.valid) _relayout(cur->obj);
6269 _find_layout_item_match(cur, &ln, &it);
6274 Evas_Object_Textblock_Item *i;
6276 EINA_INLIST_FOREACH(ln->items, i)
6278 if (it->text_pos < i->text_pos)
6288 cur->node = it->text_node;
6289 cur->pos = it->text_pos;
6290 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
6292 ind = _ITEM_TEXT(it)->text_props.text_len - 1;
6293 if (!IS_AT_END(_ITEM_TEXT(it), ind)) ind++;
6296 else if (!EINA_INLIST_GET(ln)->next && !EINA_INLIST_GET(ln->par)->next)
6305 * checks if a format (as a string) is visible/changes format and sets the
6306 * fnode properties accordingly.
6308 * @param fnode the format node
6309 * @param s the string.
6312 _evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format *fnode,
6316 Eina_Bool is_opener = EINA_TRUE;
6318 fnode->visible = fnode->format_change = EINA_FALSE;
6319 fnode->anchor = ANCHOR_NONE;
6322 if (!fnode->own_closer)
6324 is_opener = fnode->opener;
6325 fnode->format_change = EINA_TRUE;
6328 while ((item = _format_parse(&s)))
6330 int itlen = s - item;
6331 /* We care about all of the formats even after a - except for
6332 * item which we don't care after a - because it's just a standard
6334 if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) ||
6335 (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) ||
6336 (!strncmp(item, "br", itlen) && (itlen >= 2)) ||
6337 (!strncmp(item, "tab", itlen) && (itlen >= 3)) ||
6338 (!strncmp(item, "ps", itlen) && (itlen >= 2)) ||
6339 (!strncmp(item, "item", itlen) && (itlen >= 4) && is_opener))
6341 fnode->visible = EINA_TRUE;
6344 if (is_opener && !strncmp(item, "a", itlen))
6346 fnode->anchor = ANCHOR_A;
6348 else if (is_opener && !strncmp(item, "item", itlen) && (itlen >= 4))
6350 fnode->anchor = ANCHOR_ITEM;
6356 * Sets the cursor to the position of where the fmt points to.
6358 * @param cur the cursor to update.
6359 * @param fmt the format to set according to.
6362 static void __UNUSED__
6363 _evas_textblock_cursor_node_text_at_format(Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Node_Format *fmt)
6365 Evas_Object_Textblock_Node_Text *text;
6366 Evas_Object_Textblock_Node_Format *base_format;
6367 Evas_Object_Textblock_Node_Format *itr;
6368 size_t position = 0;
6370 if (!cur || !fmt) return;
6371 /* Find the main format node */
6372 text = fmt->text_node;
6374 base_format = text->format_node;
6375 EINA_INLIST_FOREACH(base_format, itr)
6381 position += itr->offset;
6383 cur->pos = position;
6390 * Remove pairs of + and - formats and also remove formats without + or -
6391 * i.e formats that pair to themselves. Only removes invisible formats
6392 * that pair themselves, if you want to remove invisible formats that pair
6393 * themselves, please first change fmt->visible to EINA_FALSE.
6395 * @param o the textblock object.
6396 * @param fmt the current format.
6399 _evas_textblock_node_format_remove_matching(Evas_Object_Textblock *o,
6400 Evas_Object_Textblock_Node_Format *fmt)
6402 Evas_Object_Textblock_Node_Text *tnode;
6403 Eina_List *formats = NULL;
6408 tnode = fmt->text_node;
6412 Evas_Object_Textblock_Node_Format *nnode;
6413 const char *fstr = fmt->orig_format;
6415 nnode = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6418 offset = nnode->offset;
6422 if (fmt->opener && !fmt->own_closer)
6424 formats = eina_list_prepend(formats, fmt);
6426 else if (fstr && !fmt->opener)
6428 Evas_Object_Textblock_Node_Format *fnode;
6430 fstr_len = strlen(fstr);
6431 /* Generic popper, just pop (if there's anything to pop). */
6432 if (formats && (((fstr[0] == ' ') && !fstr[1]) || !fstr[0]))
6434 fnode = eina_list_data_get(formats);
6435 formats = eina_list_remove_list(formats, formats);
6436 _evas_textblock_node_format_remove(o, fnode, 0);
6437 _evas_textblock_node_format_remove(o, fmt, 0);
6439 /* Find the matching format and pop it, if the matching format
6440 * is our format, i.e the last one, pop and break. */
6443 Eina_List *i, *next;
6444 EINA_LIST_FOREACH_SAFE(formats, i, next, fnode)
6446 if (_FORMAT_IS_CLOSER_OF(
6447 fnode->orig_format, fstr, fstr_len))
6449 fnode = eina_list_data_get(i);
6450 formats = eina_list_remove_list(formats, i);
6451 _evas_textblock_node_format_remove(o, fnode, 0);
6452 _evas_textblock_node_format_remove(o, fmt, 0);
6458 else if (!fmt->visible)
6460 _evas_textblock_node_format_remove(o, fmt, 0);
6464 while (fmt && (offset == 0) && (fmt->text_node == tnode));
6465 eina_list_free(formats);
6469 * Add the offset (may be negative) to the first node after fmt which is
6470 * pointing to the text node tnode or to o->format_nodes if fmt is null
6471 * and it points to tnode.
6473 * @param o the textblock object.
6474 * @param tnode the text node the format should point to.
6475 * @param fmt the current format.
6476 * @param offset the offest to add (may be negative).
6479 _evas_textblock_node_format_adjust_offset(Evas_Object_Textblock *o,
6480 Evas_Object_Textblock_Node_Text *tnode,
6481 Evas_Object_Textblock_Node_Format *fmt, int offset)
6485 fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6489 fmt = o->format_nodes;
6491 if (fmt && (tnode == fmt->text_node))
6493 fmt->offset += offset;
6499 * Removes a format node updating the offset of the next format node and the
6500 * text nodes pointing to this node.
6502 * @param o the textblock object.
6503 * @param n the fromat node to remove
6506 _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visible_adjustment)
6508 /* Update the text nodes about the change */
6510 Evas_Object_Textblock_Node_Format *nnode;
6511 nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->next);
6512 /* If there's a next node that belongs to the same text node
6513 * and the curret node was the main one, advance the format node */
6514 if (nnode && (nnode->text_node == n->text_node))
6516 if (nnode->text_node->format_node == n)
6518 nnode->text_node->format_node = nnode;
6523 Evas_Object_Textblock_Node_Text *tnode;
6524 /* If there's no next one update the text nodes */
6525 nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
6526 tnode = n->text_node;
6527 /* Even if it's not the current text_node's main node
6528 * it can still be the next's. */
6529 if (tnode && (tnode->format_node != n))
6531 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6533 while (tnode && (tnode->format_node == n))
6535 tnode->format_node = nnode;
6536 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6540 _evas_textblock_node_format_adjust_offset(o, n->text_node, n,
6541 n->offset - visible_adjustment);
6543 o->format_nodes = _NODE_FORMAT(eina_inlist_remove(
6544 EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
6545 _evas_textblock_node_format_free(o, n);
6550 * Sets all the offsets of the format nodes between start and end in the text
6551 * node n to 0 and sets visibility to EINA_FALSE.
6552 * If end == -1 end means the end of the string.
6553 * Assumes there is a prev node or the current node will be preserved.
6555 * @param n the text node the positinos refer to.
6556 * @param start the start of where to delete from.
6557 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6558 * @returns #EINA_TRUE if removed a PS, false otherwise.
6561 _evas_textblock_node_text_adjust_offsets_to_start(Evas_Object_Textblock *o,
6562 Evas_Object_Textblock_Node_Text *n, size_t start, int end)
6564 Evas_Object_Textblock_Node_Format *last_node, *itr;
6565 Evas_Object_Textblock_Node_Text *new_node;
6569 int update_format_node;
6573 itr = n->format_node;
6574 if (!itr || (itr->text_node != n)) return EINA_FALSE;
6577 if ((end < 0) || ((size_t) end == eina_ustrbuf_length_get(n->unicode)))
6583 /* We don't want the last one */
6587 /* If we are not removing the text node, all should stay in this text
6588 * node, otherwise, everything should move to the previous node */
6589 if ((start == 0) && !use_end)
6591 new_node = _NODE_TEXT(EINA_INLIST_GET(n)->prev);
6602 /* Find the first node after start */
6603 while (itr && (itr->text_node == n))
6610 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6613 if (!itr || (itr->text_node != n))
6618 update_format_node = ((itr == n->format_node) && (new_node != n));
6619 delta = orig_end - pos;
6620 itr->offset -= pos - start;
6622 while (itr && (itr->text_node == n))
6625 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6629 pos += last_node->offset;
6632 /* start is negative when this gets relevant */
6633 if (use_end && (pos > (size_t) end))
6635 last_node->offset -= delta;
6639 delta = orig_end - pos;
6642 last_node->offset = 0;
6648 last_node->visible = EINA_FALSE;
6650 if (!itr || (itr && (itr->text_node != n)))
6652 /* Remove the PS, and return since it's the end of the node */
6653 if (_IS_PARAGRAPH_SEPARATOR(o, last_node->format))
6655 _evas_textblock_node_format_remove(o, last_node, 0);
6660 last_node->text_node = new_node;
6661 if (update_format_node)
6663 n->format_node = last_node;
6672 * Removes all the format nodes between start and end in the text node n.
6673 * This function updates the offset of the next format node and the
6674 * text nodes pointing to it. if end == -1 end means the end of the string.
6676 * @param o the textblock object.
6677 * @param n the text node the positinos refer to.
6678 * @param start the start of where to delete from.
6679 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6682 _evas_textblock_node_text_remove_formats_between(Evas_Object_Textblock *o,
6683 Evas_Object_Textblock_Node_Text *n, int start, int end)
6685 Evas_Object_Textblock_Node_Format *itr;
6687 int offset = end - start;
6688 itr = n->format_node;
6691 start -= itr->offset;
6692 if (offset < 0) offset = 0;
6693 if (end < 0) use_end = 0;
6694 while (itr && (itr->text_node == n))
6696 Evas_Object_Textblock_Node_Format *nnode;
6699 /* start is negative when this gets relevant */
6700 if ((offset + start < 0) && use_end)
6704 nnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6707 tmp_offset = nnode->offset;
6711 /* Don't do visible adjustments because we are removing the visual
6712 * chars anyway and taking those into account */
6713 _evas_textblock_node_format_remove(o, itr, 0);
6715 start -= tmp_offset;
6722 * Returns the first format in the range between start and end in the textblock
6725 * @param o the textblock object.
6726 * @param n the text node the positinos refer to.
6727 * @param start the start of where to delete from.
6728 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6730 static Evas_Object_Textblock_Node_Format *
6731 _evas_textblock_node_text_get_first_format_between(
6732 Evas_Object_Textblock_Node_Text *n, int start, int end)
6734 Evas_Object_Textblock_Node_Format *itr;
6736 itr = n->format_node;
6737 if (end < 0) use_end = 0;
6738 while (itr && (itr->text_node == n))
6740 start -= itr->offset;
6742 if ((end <= 0) && use_end)
6750 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6756 * Removes a text node and the corresponding format nodes.
6758 * @param o the textblock objec.t
6759 * @param n the node to remove.
6762 _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n)
6764 _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
6766 o->text_nodes = _NODE_TEXT(eina_inlist_remove(
6767 EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
6768 _evas_textblock_node_text_free(n);
6773 * Return the position where the formats starts at.
6775 * @param fmt the format to return the position of.
6776 * @return the position of the format in the text node it points to.
6779 _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt)
6781 Evas_Object_Textblock_Node_Text *text;
6782 Evas_Object_Textblock_Node_Format *base_format;
6783 Evas_Object_Textblock_Node_Format *itr;
6784 size_t position = 0;
6787 /* Find the main format node */
6788 text = fmt->text_node;
6789 base_format = text->format_node;
6790 EINA_INLIST_FOREACH(base_format, itr)
6796 position += itr->offset;
6798 return position + fmt->offset;
6802 evas_textblock_cursor_pos_get(const Evas_Textblock_Cursor *cur)
6804 Evas_Object_Textblock *o;
6805 Evas_Object_Textblock_Node_Text *n;
6808 if (!cur) return -1;
6809 if (!cur->node) return 0;
6810 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6812 while (n != cur->node)
6814 npos += eina_ustrbuf_length_get(n->unicode);
6815 n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6817 return npos + cur->pos;
6821 evas_textblock_cursor_pos_set(Evas_Textblock_Cursor *cur, int _pos)
6823 Evas_Object_Textblock *o;
6824 Evas_Object_Textblock_Node_Text *n;
6828 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6836 pos = (size_t) _pos;
6840 while (n && (pos >= eina_ustrbuf_length_get(n->unicode)))
6842 pos -= eina_ustrbuf_length_get(n->unicode);
6843 n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6851 else if (o->text_nodes)
6853 /* In case we went pass the last node, we need to put the cursor
6854 * at the absolute end. */
6855 Evas_Object_Textblock_Node_Text *last_n;
6857 last_n = _NODE_TEXT(EINA_INLIST_GET(o->text_nodes)->last);
6858 pos = eina_ustrbuf_length_get(last_n->unicode);
6867 evas_textblock_cursor_line_set(Evas_Textblock_Cursor *cur, int line)
6869 Evas_Object_Textblock *o;
6870 Evas_Object_Textblock_Line *ln;
6871 Evas_Object_Textblock_Item *it;
6873 if (!cur) return EINA_FALSE;
6874 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6875 if (!o->formatted.valid) _relayout(cur->obj);
6877 ln = _find_layout_line_num(cur->obj, line);
6878 if (!ln) return EINA_FALSE;
6879 it = (Evas_Object_Textblock_Item *)ln->items;
6882 cur->pos = it->text_pos;
6883 cur->node = it->text_node;
6889 cur->node = o->text_nodes;
6895 evas_textblock_cursor_compare(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
6897 Eina_Inlist *l1, *l2;
6899 if (!cur1) return 0;
6900 if (!cur2) return 0;
6901 if (cur1->obj != cur2->obj) return 0;
6902 if ((!cur1->node) || (!cur2->node)) return 0;
6903 if (cur1->node == cur2->node)
6905 if (cur1->pos < cur2->pos) return -1; /* cur1 < cur2 */
6906 else if (cur1->pos > cur2->pos) return 1; /* cur2 < cur1 */
6909 for (l1 = EINA_INLIST_GET(cur1->node),
6910 l2 = EINA_INLIST_GET(cur1->node); (l1) || (l2);)
6912 if (l1 == EINA_INLIST_GET(cur2->node)) return 1; /* cur2 < cur 1 */
6913 else if (l2 == EINA_INLIST_GET(cur2->node)) return -1; /* cur1 < cur 2 */
6914 else if (!l1) return -1; /* cur1 < cur 2 */
6915 else if (!l2) return 1; /* cur2 < cur 1 */
6923 evas_textblock_cursor_copy(const Evas_Textblock_Cursor *cur, Evas_Textblock_Cursor *cur_dest)
6926 if (!cur_dest) return;
6927 if (cur->obj != cur_dest->obj) return;
6928 cur_dest->pos = cur->pos;
6929 cur_dest->node = cur->node;
6937 * Free a text node. Shouldn't be used usually, it's better to use
6938 * @ref _evas_textblock_node_text_remove for most cases .
6940 * @param n the text node to free
6941 * @see _evas_textblock_node_text_remove
6944 _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n)
6947 eina_ustrbuf_free(n->unicode);
6951 n->par->text_node = NULL;
6957 * Create a new text node
6959 * @return the new text node.
6961 static Evas_Object_Textblock_Node_Text *
6962 _evas_textblock_node_text_new(void)
6964 Evas_Object_Textblock_Node_Text *n;
6966 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Text));
6967 n->unicode = eina_ustrbuf_new();
6968 /* We want to layout each paragraph at least once. */
6969 n->dirty = EINA_TRUE;
6970 n->is_new = EINA_TRUE;
6977 * Break a paragraph. This does not add a PS but only splits the paragraph
6978 * where a ps was just added!
6980 * @param cur the cursor to break at.
6981 * @param fnode the format node of the PS just added.
6982 * @return Returns no value.
6985 _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur,
6986 Evas_Object_Textblock_Node_Format *fnode)
6988 Evas_Object_Textblock *o;
6989 Evas_Object_Textblock_Node_Text *n;
6992 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6994 n = _evas_textblock_node_text_new();
6995 o->text_nodes = _NODE_TEXT(eina_inlist_append_relative(
6996 EINA_INLIST_GET(o->text_nodes),
6998 EINA_INLIST_GET(cur->node)));
6999 /* Handle text and format changes. */
7002 Evas_Object_Textblock_Node_Format *nnode;
7004 const Eina_Unicode *text;
7006 /* If there was a format node in the delete range,
7007 * make it our format and update the text_node fields,
7008 * otherwise, use the paragraph separator
7009 * of the previous paragraph. */
7010 nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7011 if (nnode && (nnode->text_node == cur->node))
7013 n->format_node = nnode;
7014 nnode->offset--; /* We don't have to take the replacement char
7015 into account anymore */
7016 while (nnode && (nnode->text_node == cur->node))
7018 nnode->text_node = n;
7019 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
7024 n->format_node = fnode;
7027 /* cur->pos now points to the PS, move after. */
7028 start = cur->pos + 1;
7029 len = eina_ustrbuf_length_get(cur->node->unicode) - start;
7032 text = eina_ustrbuf_string_get(cur->node->unicode);
7033 eina_ustrbuf_append_length(n->unicode, text + start, len);
7034 eina_ustrbuf_remove(cur->node->unicode, start, start + len);
7035 cur->node->dirty = EINA_TRUE;
7040 fnode = o->format_nodes;
7043 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->last);
7045 n->format_node = fnode;
7051 * Set the node and offset of all the curs after cur.
7053 * @param cur the cursor.
7054 * @param n the current textblock node.
7055 * @param new_node the new node to set.
7058 _evas_textblock_cursors_set_node(Evas_Object_Textblock *o,
7059 const Evas_Object_Textblock_Node_Text *n,
7060 Evas_Object_Textblock_Node_Text *new_node)
7063 Evas_Textblock_Cursor *data;
7065 if (n == o->cursor->node)
7068 o->cursor->node = new_node;
7070 EINA_LIST_FOREACH(o->cursors, l, data)
7072 if (n == data->node)
7075 data->node = new_node;
7082 * Update the offset of all the cursors after cur.
7084 * @param cur the cursor.
7085 * @param n the current textblock node.
7086 * @param start the starting pos.
7087 * @param offset how much to adjust (can be negative).
7090 _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur,
7091 const Evas_Object_Textblock_Node_Text *n,
7092 size_t start, int offset)
7095 Evas_Textblock_Cursor *data;
7096 Evas_Object_Textblock *o;
7097 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7099 if (cur != o->cursor)
7101 if ((n == o->cursor->node) &&
7102 (o->cursor->pos > start))
7104 if ((offset < 0) && (o->cursor->pos <= (size_t) (-1 * offset)))
7110 o->cursor->pos += offset;
7114 EINA_LIST_FOREACH(o->cursors, l, data)
7118 if ((n == data->node) &&
7119 (data->pos > start))
7121 if ((offset < 0) && (data->pos <= (size_t) (-1 * offset)))
7127 data->pos += offset;
7130 else if (!data->node)
7132 data->node = o->text_nodes;
7141 * Mark that the textblock has changed.
7143 * @param o the textblock object.
7144 * @param obj the evas object.
7147 _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj)
7149 o->formatted.valid = 0;
7150 o->native.valid = 0;
7151 o->content_changed = 1;
7154 free(o->markup_text);
7155 o->markup_text = NULL;
7158 evas_object_change(obj);
7162 _evas_textblock_invalidate_all(Evas_Object_Textblock *o)
7164 Evas_Object_Textblock_Node_Text *n;
7166 EINA_INLIST_FOREACH(o->text_nodes, n)
7168 n->dirty = EINA_TRUE;
7173 evas_textblock_cursor_text_append(Evas_Textblock_Cursor *cur, const char *_text)
7175 Evas_Object_Textblock *o;
7176 Evas_Object_Textblock_Node_Text *n;
7177 Evas_Object_Textblock_Node_Format *fnode = NULL;
7182 text = eina_unicode_utf8_to_unicode(_text, &len);
7183 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7188 Evas_Object_Textblock_Node_Format *nnode;
7189 fnode = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7190 fnode = _evas_textblock_node_format_last_at_off(fnode);
7191 /* find the node after the current in the same paragraph
7192 * either we find one and then take the next, or we try to get
7193 * the first for the paragraph which must be after our position */
7196 if (!evas_textblock_cursor_format_is_visible_get(cur))
7198 nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7199 if (nnode && (nnode->text_node == n))
7211 fnode = n->format_node;
7214 else if (o->text_nodes)
7216 n = cur->node = o->text_nodes;
7221 n = _evas_textblock_node_text_new();
7222 o->text_nodes = _NODE_TEXT(eina_inlist_append(
7223 EINA_INLIST_GET(o->text_nodes),
7224 EINA_INLIST_GET(n)));
7228 eina_ustrbuf_insert_length(n->unicode, text, len, cur->pos);
7229 /* Advance the formats */
7230 if (fnode && (fnode->text_node == cur->node))
7231 fnode->offset += len;
7233 /* Update all the cursors after our position. */
7234 _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, len);
7236 _evas_textblock_changed(o, cur->obj);
7237 n->dirty = EINA_TRUE;
7240 if (!o->cursor->node)
7241 o->cursor->node = o->text_nodes;
7246 evas_textblock_cursor_text_prepend(Evas_Textblock_Cursor *cur, const char *_text)
7249 /*append is essentially prepend without advancing */
7250 len = evas_textblock_cursor_text_append(cur, _text);
7251 cur->pos += len; /*Advance */
7257 * Free a format node
7259 * @param o the textblock object
7260 * @param n the format node to free
7263 _evas_textblock_node_format_free(Evas_Object_Textblock *o,
7264 Evas_Object_Textblock_Node_Format *n)
7267 eina_stringshare_del(n->format);
7268 eina_stringshare_del(n->orig_format);
7269 if (n->anchor == ANCHOR_ITEM)
7270 o->anchors_item = eina_list_remove(o->anchors_item, n);
7271 else if (n->anchor == ANCHOR_A)
7272 o->anchors_a = eina_list_remove(o->anchors_a, n);
7278 * Create a new format node.
7280 * @param format the text to create the format node from.
7281 * @param o the textblock object.
7282 * @return Returns the new format node
7284 static Evas_Object_Textblock_Node_Format *
7285 _evas_textblock_node_format_new(Evas_Object_Textblock *o, const char *_format)
7287 Evas_Object_Textblock_Node_Format *n;
7288 const char *format = _format;
7289 const char *pre_stripped_format = NULL;
7291 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Format));
7292 /* Create orig_format and format */
7293 if (format[0] == '<')
7299 format++; /* Advance after '<' */
7300 format_len = strlen(format);
7301 if ((format_len > 0) && format[format_len - 1] == '>')
7303 format_len--; /* We don't care about '>' */
7304 /* Check if it closes itself. Skip the </> case. */
7305 if ((format_len > 1) && format[format_len - 1] == '/')
7307 format_len--; /* We don't care about '/' */
7308 n->own_closer = EINA_TRUE;
7312 if (!o->style_user || !(match = _style_match_tag(o->style_user, format,
7313 format_len, &replace_len)))
7315 match = _style_match_tag(o->style, format, format_len,
7321 if (match[0] != '-')
7323 n->opener = EINA_TRUE;
7324 if (match[0] != '+')
7326 n->own_closer = EINA_TRUE;
7330 pre_stripped_format = match;
7334 if (format[0] == '/')
7341 n->opener = EINA_TRUE;
7345 n->orig_format = eina_stringshare_add_length(format, format_len);
7347 if (!pre_stripped_format)
7348 pre_stripped_format = n->orig_format;
7350 /* Just use as is, it's a special format. */
7353 const char *tmp = format;
7354 if (format[0] != '-')
7356 n->opener = EINA_TRUE;
7357 if (format[0] != '+')
7359 n->own_closer = EINA_TRUE;
7362 if ((*tmp == '+') || (*tmp == '-'))
7365 while (*tmp == ' ') tmp++;
7367 n->orig_format = eina_stringshare_add(tmp);
7368 pre_stripped_format = n->orig_format;
7373 const char *tmp = pre_stripped_format;
7374 if ((*tmp == '+') || (*tmp == '-'))
7377 while (*tmp == ' ') tmp++;
7379 n->format = eina_stringshare_add(tmp);
7383 _evas_textblock_format_is_visible(n, format);
7384 if (n->anchor == ANCHOR_A)
7386 o->anchors_a = eina_list_append(o->anchors_a, n);
7388 else if (n->anchor == ANCHOR_ITEM)
7390 o->anchors_item = eina_list_append(o->anchors_item, n);
7392 n->is_new = EINA_TRUE;
7398 _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur)
7400 const Eina_Unicode *text;
7402 if (!cur) return EINA_FALSE;
7403 if (!cur->node) return EINA_FALSE;
7404 text = eina_ustrbuf_string_get(cur->node->unicode);
7405 return ((text[cur->pos] == 0) && (!EINA_INLIST_GET(cur->node)->next)) ?
7406 EINA_TRUE : EINA_FALSE;
7410 evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format)
7412 Evas_Object_Textblock *o;
7413 Evas_Object_Textblock_Node_Format *n;
7414 Eina_Bool is_visible;
7416 if (!cur) return EINA_FALSE;
7417 if ((!format) || (format[0] == 0)) return EINA_FALSE;
7418 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7419 /* We should always have at least one text node */
7422 evas_textblock_cursor_text_prepend(cur, "");
7425 n = _evas_textblock_node_format_new(o, format);
7426 is_visible = n->visible;
7430 o->format_nodes = _NODE_FORMAT(eina_inlist_append(
7431 EINA_INLIST_GET(o->format_nodes),
7432 EINA_INLIST_GET(n)));
7434 n->text_node = (EINA_INLIST_GET(n)->prev) ?
7435 _NODE_FORMAT(EINA_INLIST_GET(n)->prev)->text_node :
7437 cur->node = n->text_node;
7441 Evas_Object_Textblock_Node_Format *fmt;
7442 fmt = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7443 n->text_node = cur->node;
7446 o->format_nodes = _NODE_FORMAT(eina_inlist_prepend(
7447 EINA_INLIST_GET(o->format_nodes),
7448 EINA_INLIST_GET(n)));
7449 n->offset = cur->pos;
7453 if (evas_textblock_cursor_format_is_visible_get(cur))
7455 o->format_nodes = _NODE_FORMAT(eina_inlist_prepend_relative(
7456 EINA_INLIST_GET(o->format_nodes),
7458 EINA_INLIST_GET(fmt)
7460 n->offset = fmt->offset;
7461 if (fmt->text_node->format_node == fmt)
7463 fmt->text_node->format_node = n;
7468 fmt = _evas_textblock_node_format_last_at_off(fmt);
7469 o->format_nodes = _NODE_FORMAT(eina_inlist_append_relative(
7470 EINA_INLIST_GET(o->format_nodes),
7472 EINA_INLIST_GET(fmt)
7474 if (fmt->text_node != cur->node)
7476 n->offset = cur->pos;
7480 n->offset = cur->pos -
7481 _evas_textblock_node_format_pos_get(fmt);
7485 /* Adjust differently if we insert a format char */
7488 _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7493 _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7497 if (!fmt || (fmt->text_node != cur->node))
7499 cur->node->format_node = n;
7502 if (is_visible && cur->node)
7504 Eina_Unicode insert_char;
7505 /* Insert a visual representation according to the type of the
7507 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7508 insert_char = _PARAGRAPH_SEPARATOR;
7509 else if (_IS_LINE_SEPARATOR(format))
7510 insert_char = _NEWLINE;
7511 else if (_IS_TAB(format))
7514 insert_char = _REPLACEMENT_CHAR;
7516 eina_ustrbuf_insert_char(cur->node->unicode, insert_char, cur->pos);
7518 /* Advance all the cursors after our cursor */
7519 _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, 1);
7520 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7522 _evas_textblock_cursor_break_paragraph(cur, n);
7526 /* Handle visible format nodes here */
7527 cur->node->dirty = EINA_TRUE;
7528 n->is_new = EINA_FALSE;
7533 o->format_changed = EINA_TRUE;
7536 _evas_textblock_changed(o, cur->obj);
7538 if (!o->cursor->node)
7539 o->cursor->node = o->text_nodes;
7544 evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor *cur, const char *format)
7546 Eina_Bool is_visible;
7547 /* append is essentially prepend without advancing */
7548 is_visible = evas_textblock_cursor_format_append(cur, format);
7551 /* Advance after the replacement char */
7552 evas_textblock_cursor_char_next(cur);
7560 evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur)
7562 Evas_Object_Textblock *o;
7563 Evas_Object_Textblock_Node_Text *n, *n2;
7564 const Eina_Unicode *text;
7567 if (!cur || !cur->node) return;
7568 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7571 text = eina_ustrbuf_string_get(n->unicode);
7578 if (chr == 0) return;
7580 eina_ustrbuf_remove(n->unicode, cur->pos, ind);
7581 /* Remove a format node if needed, and remove the char only if the
7582 * fmt node is not visible */
7584 Eina_Bool should_merge = EINA_FALSE;
7585 Evas_Object_Textblock_Node_Format *fmt, *fmt2;
7586 fmt = _evas_textblock_cursor_node_format_at_pos_get(cur);
7589 const char *format = NULL;
7590 Evas_Object_Textblock_Node_Format *last_fmt;
7591 /* If there's a PS it must be the last become it delimits paragraphs */
7592 last_fmt = _evas_textblock_node_format_last_at_off(fmt);
7593 format = last_fmt->format;
7594 if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
7596 /* If it was a paragraph separator, we should merge the
7597 * current with the next, there must be a next. */
7598 should_merge = EINA_TRUE;
7600 /* If a singnular, mark as invisible, so we'll delete it. */
7601 if (!format || last_fmt->own_closer)
7603 last_fmt->visible = EINA_FALSE;
7607 fmt2 = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7608 fmt2 = _evas_textblock_node_format_last_at_off(fmt2);
7609 _evas_textblock_node_format_adjust_offset(o, cur->node, fmt2,
7614 _evas_textblock_cursor_nodes_merge(cur);
7617 _evas_textblock_node_format_remove_matching(o, fmt);
7620 if (cur->pos == eina_ustrbuf_length_get(n->unicode))
7622 n2 = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7630 _evas_textblock_cursors_update_offset(cur, n, ppos, -(ind - ppos));
7631 _evas_textblock_changed(o, cur->obj);
7632 cur->node->dirty = EINA_TRUE;
7636 evas_textblock_cursor_range_delete(Evas_Textblock_Cursor *cur1, Evas_Textblock_Cursor *cur2)
7638 Evas_Object_Textblock_Node_Format *fnode = NULL;
7639 Evas_Object_Textblock *o;
7640 Evas_Object_Textblock_Node_Text *n1, *n2;
7641 Eina_Bool should_merge = EINA_FALSE, reset_cursor = EINA_FALSE;
7643 if (!cur1 || !cur1->node) return;
7644 if (!cur2 || !cur2->node) return;
7645 if (cur1->obj != cur2->obj) return;
7646 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7647 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7649 Evas_Textblock_Cursor *tc;
7657 if ((evas_textblock_cursor_compare(o->cursor, cur1) >= 0) &&
7658 (evas_textblock_cursor_compare(cur2, o->cursor) >= 0))
7660 reset_cursor = EINA_TRUE;
7666 if ((cur1->pos == 0) &&
7667 (cur2->pos == eina_ustrbuf_length_get(n1->unicode)))
7669 _evas_textblock_node_text_remove_formats_between(o, n1, 0, -1);
7673 should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o,
7674 n1, cur1->pos, cur2->pos);
7676 eina_ustrbuf_remove(n1->unicode, cur1->pos, cur2->pos);
7677 _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos, - (cur2->pos - cur1->pos));
7681 Evas_Object_Textblock_Node_Text *n;
7683 _evas_textblock_node_text_adjust_offsets_to_start(o, n1, cur1->pos, -1);
7684 n = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7685 /* Remove all the text nodes between */
7686 while (n && (n != n2))
7688 Evas_Object_Textblock_Node_Text *nnode;
7690 nnode = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7691 _evas_textblock_cursors_set_node(o, n, n1);
7692 _evas_textblock_node_text_remove(o, n);
7695 should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o, n2,
7698 /* Remove the formats and the strings in the first and last nodes */
7699 len = eina_ustrbuf_length_get(n1->unicode);
7700 eina_ustrbuf_remove(n1->unicode, cur1->pos, len);
7701 eina_ustrbuf_remove(n2->unicode, 0, cur2->pos);
7702 /* Merge the nodes because we removed the PS */
7703 _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos,
7705 _evas_textblock_cursors_update_offset(cur2, cur2->node, 0, - cur2->pos);
7706 _evas_textblock_nodes_merge(o, n1);
7708 fnode = _evas_textblock_cursor_node_format_at_pos_get(cur1);
7710 n1->dirty = n2->dirty = EINA_TRUE;
7713 /* We call this function instead of the cursor one because we already
7714 * updated the cursors */
7715 _evas_textblock_nodes_merge(o, n1);
7717 _evas_textblock_node_format_remove_matching(o, fnode);
7719 evas_textblock_cursor_copy(cur1, cur2);
7721 evas_textblock_cursor_copy(cur1, o->cursor);
7723 _evas_textblock_changed(o, cur1->obj);
7728 evas_textblock_cursor_content_get(const Evas_Textblock_Cursor *cur)
7730 if (!cur || !cur->node) return NULL;
7731 if (evas_textblock_cursor_format_is_visible_get(cur))
7734 Evas_Object_Textblock_Node_Format *fnode;
7736 fnode = _evas_textblock_node_visible_at_pos_get(
7737 evas_textblock_cursor_format_get(cur));
7739 buf = eina_strbuf_new();
7740 _markup_get_format_append(buf, fnode);
7741 ret = eina_strbuf_string_steal(buf);
7742 eina_strbuf_free(buf);
7748 const Eina_Unicode *ustr;
7749 Eina_Unicode buf[2];
7752 ustr = eina_ustrbuf_string_get(cur->node->unicode);
7753 buf[0] = ustr[cur->pos];
7755 s = eina_unicode_unicode_to_utf8(buf, NULL);
7762 _evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7764 Evas_Object_Textblock_Node_Text *tnode;
7766 Evas_Textblock_Cursor *cur2;
7767 buf = eina_strbuf_new();
7769 if (!cur1 || !cur1->node) return NULL;
7770 if (!_cur2 || !_cur2->node) return NULL;
7771 if (cur1->obj != _cur2->obj) return NULL;
7772 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7774 const Evas_Textblock_Cursor *tc;
7780 /* Work on a local copy of the cur */
7781 cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7782 cur2->obj = _cur2->obj;
7783 evas_textblock_cursor_copy(_cur2, cur2);
7785 /* Parse the text between the cursors. */
7786 for (tnode = cur1->node ; tnode ;
7787 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next))
7789 Evas_Object_Textblock_Node_Format *fnode;
7790 Eina_Unicode *text_base, *text;
7794 eina_unicode_strndup(eina_ustrbuf_string_get(tnode->unicode),
7795 eina_ustrbuf_length_get(tnode->unicode));
7796 if (tnode == cur2->node)
7798 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7799 cur1->pos, cur2->pos);
7801 else if (tnode == cur1->node)
7803 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7808 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7811 /* Init the offset so the first one will count starting from cur1->pos
7812 * and not the previous format node */
7813 if (tnode == cur1->node)
7817 off = _evas_textblock_node_format_pos_get(fnode) -
7818 cur1->pos - fnode->offset;
7826 while (fnode && (fnode->text_node == tnode))
7828 Eina_Unicode tmp_ch;
7829 off += fnode->offset;
7830 if ((tnode == cur2->node) &&
7831 ((size_t) (text - text_base + off) >= cur2->pos))
7835 /* No need to skip on the first run */
7837 text[off] = 0; /* Null terminate the part of the string */
7838 _markup_get_text_append(buf, text);
7839 _markup_get_format_append(buf, fnode);
7840 text[off] = tmp_ch; /* Restore the char */
7851 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7853 /* If we got to the last node, stop and add the rest outside */
7854 if (cur2->node == tnode)
7856 /* Add the rest, skip replacement */
7857 /* Don't go past the second cursor pos */
7858 text_base[cur2->pos] = '\0';
7859 _markup_get_text_append(buf, text);
7865 /* Add the rest, skip replacement */
7866 _markup_get_text_append(buf, text);
7870 /* return the string */
7873 ret = eina_strbuf_string_steal(buf);
7874 eina_strbuf_free(buf);
7880 _evas_textblock_cursor_range_text_plain_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7883 Evas_Object_Textblock_Node_Text *n1, *n2;
7884 Evas_Textblock_Cursor *cur2;
7886 buf = eina_ustrbuf_new();
7888 if (!cur1 || !cur1->node) return NULL;
7889 if (!_cur2 || !_cur2->node) return NULL;
7890 if (cur1->obj != _cur2->obj) return NULL;
7891 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7893 const Evas_Textblock_Cursor *tc;
7901 /* Work on a local copy of the cur */
7902 cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7903 cur2->obj = _cur2->obj;
7904 evas_textblock_cursor_copy(_cur2, cur2);
7909 const Eina_Unicode *tmp;
7910 tmp = eina_ustrbuf_string_get(n1->unicode);
7911 eina_ustrbuf_append_length(buf, tmp + cur1->pos, cur2->pos - cur1->pos);
7915 const Eina_Unicode *tmp;
7916 tmp = eina_ustrbuf_string_get(n1->unicode);
7917 eina_ustrbuf_append(buf, tmp + cur1->pos);
7918 n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7921 tmp = eina_ustrbuf_string_get(n1->unicode);
7922 eina_ustrbuf_append_length(buf, tmp,
7923 eina_ustrbuf_length_get(n1->unicode));
7924 n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7926 tmp = eina_ustrbuf_string_get(n2->unicode);
7927 eina_ustrbuf_append_length(buf, tmp, cur2->pos);
7930 /* Free and return */
7933 ret = eina_unicode_unicode_to_utf8(eina_ustrbuf_string_get(buf), NULL);
7934 eina_ustrbuf_free(buf);
7940 evas_textblock_cursor_range_formats_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
7942 Evas_Object *obj = cur1->obj;
7943 Eina_List *ret = NULL;
7944 Evas_Object_Textblock_Node_Text *n1, *n2;
7945 Evas_Object_Textblock_Node_Format *first, *last;
7946 TB_HEAD_RETURN(NULL);
7947 if (!cur1 || !cur1->node) return NULL;
7948 if (!cur2 || !cur2->node) return NULL;
7949 if (cur1->obj != cur2->obj) return NULL;
7950 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7952 const Evas_Textblock_Cursor *tc;
7961 /* FIXME: Change first and last getting to format_before_or_at_pos_get */
7963 last = n2->format_node;
7965 /* If n2->format_node is NULL, we don't have formats in the tb/range. */
7968 /* If the found format is on our text node, we should go to the last
7969 * one, otherwise, the one we found is good enough. */
7970 if (last->text_node == n2)
7972 Evas_Object_Textblock_Node_Format *fnode = last;
7973 while (fnode && (fnode->text_node == n2))
7976 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7980 /* If the first format node is within the range (i.e points to n1) or if
7981 * we have other formats in the range, go through them */
7982 first = n1->format_node;
7983 if ((first->text_node == n1) || (first != last))
7985 Evas_Object_Textblock_Node_Format *fnode = first;
7986 /* Go to the first one in the range */
7987 if (first->text_node != n1)
7989 first = _NODE_FORMAT(EINA_INLIST_GET(first)->next);
7994 ret = eina_list_append(ret, fnode);
7997 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
8006 evas_textblock_cursor_range_text_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2, Evas_Textblock_Text_Type format)
8008 if (format == EVAS_TEXTBLOCK_TEXT_MARKUP)
8009 return _evas_textblock_cursor_range_text_markup_get(cur1, cur2);
8010 else if (format == EVAS_TEXTBLOCK_TEXT_PLAIN)
8011 return _evas_textblock_cursor_range_text_plain_get(cur1, cur2);
8013 return NULL; /* Not yet supported */
8017 evas_textblock_cursor_paragraph_text_get(const Evas_Textblock_Cursor *cur)
8019 Evas_Textblock_Cursor cur1, cur2;
8020 if (!cur) return NULL;
8021 if (!cur->node) return NULL;
8022 if (cur->node->utf8)
8024 free(cur->node->utf8);
8026 cur1.obj = cur2.obj = cur->obj;
8027 cur1.node = cur2.node = cur->node;
8028 evas_textblock_cursor_paragraph_char_first(&cur1);
8029 evas_textblock_cursor_paragraph_char_last(&cur2);
8031 cur->node->utf8 = evas_textblock_cursor_range_text_get(&cur1, &cur2,
8032 EVAS_TEXTBLOCK_TEXT_MARKUP);
8033 return cur->node->utf8;
8037 evas_textblock_cursor_paragraph_text_length_get(const Evas_Textblock_Cursor *cur)
8040 if (!cur) return -1;
8041 if (!cur->node) return -1;
8042 len = eina_ustrbuf_length_get(cur->node->unicode);
8044 if (EINA_INLIST_GET(cur->node)->next)
8045 return len - 1; /* Remove the paragraph separator */
8050 EAPI const Evas_Object_Textblock_Node_Format *
8051 evas_textblock_cursor_format_get(const Evas_Textblock_Cursor *cur)
8053 if (!cur) return NULL;
8054 if (!cur->node) return NULL;
8055 return _evas_textblock_cursor_node_format_at_pos_get(cur);
8059 evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format *fmt)
8061 static char *ret = NULL;
8064 if (!fmt) return NULL;
8067 ret = malloc(strlen(fmt->orig_format) + 2 + 1);
8070 if (fmt->opener && !fmt->own_closer)
8075 else if (!fmt->opener)
8080 strcpy(tmp, fmt->orig_format);
8085 evas_textblock_cursor_at_format_set(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *fmt)
8087 if (!fmt || !cur) return;
8088 cur->node = fmt->text_node;
8089 cur->pos = _evas_textblock_node_format_pos_get(fmt);
8093 evas_textblock_cursor_format_is_visible_get(const Evas_Textblock_Cursor *cur)
8095 const Eina_Unicode *text;
8097 if (!cur) return EINA_FALSE;
8098 if (!cur->node) return EINA_FALSE;
8099 if (!evas_textblock_cursor_is_format(cur)) return EINA_FALSE;
8100 text = eina_ustrbuf_string_get(cur->node->unicode);
8101 return EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(text[cur->pos]);
8105 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)
8108 const Evas_Textblock_Cursor *dir_cur;
8109 Evas_Textblock_Cursor cur2;
8110 Evas_Object_Textblock *o;
8111 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8112 if (!o->formatted.valid) _relayout(cur->obj);
8115 if (ctype == EVAS_TEXTBLOCK_CURSOR_UNDER)
8117 ret = evas_textblock_cursor_pen_geometry_get(cur, cx, cy, cw, ch);
8119 else if (ctype == EVAS_TEXTBLOCK_CURSOR_BEFORE)
8121 /* In the case of a "before cursor", we should get the coordinates
8122 * of just after the previous char (which in bidi text may not be
8123 * just before the current char). */
8124 Evas_Coord x, y, h, w;
8125 Evas_Object_Textblock_Node_Format *fmt;
8127 /* If it's at the end of the line, we want to get the position, not
8128 * the position of the previous */
8129 if ((cur->pos > 0) && !_evas_textblock_cursor_is_at_the_end(cur))
8132 Eina_Bool before_char = EINA_FALSE;
8134 cur2.obj = cur->obj;
8135 evas_textblock_cursor_copy(cur, &cur2);
8136 evas_textblock_cursor_char_prev(&cur2);
8138 fmt = _evas_textblock_cursor_node_format_at_pos_get(&cur2);
8140 if (!fmt || !_IS_LINE_SEPARATOR(fmt->format))
8144 before_char = EINA_FALSE;
8150 before_char = EINA_TRUE;
8153 ret = evas_textblock_cursor_pen_geometry_get(
8154 dir_cur, &x, &y, &w, &h);
8156 /* Adjust if the char is an rtl char */
8159 Eina_Bool is_rtl = EINA_FALSE;
8160 if (dir_cur->node->par->is_bidi)
8162 Evas_Object_Textblock_Line *ln;
8163 Evas_Object_Textblock_Item *it;
8164 _find_layout_item_match(dir_cur, &ln, &it);
8165 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8166 (_ITEM_TEXT(it)->text_props.bidi.dir ==
8167 EVAS_BIDI_DIRECTION_RTL))
8169 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8170 (_ITEM_FORMAT(it)->bidi_dir ==
8171 EVAS_BIDI_DIRECTION_RTL))
8175 if ((!before_char && is_rtl) ||
8176 (before_char && !is_rtl))
8178 /* Just don't advance the width */
8184 else if (cur->pos == 0)
8186 ret = evas_textblock_cursor_pen_geometry_get(
8187 dir_cur, &x, &y, &w, &h);
8189 Eina_Bool is_rtl = EINA_FALSE;
8190 if (dir_cur->node && dir_cur->node->par->is_bidi)
8192 Evas_Object_Textblock_Line *ln;
8193 Evas_Object_Textblock_Item *it;
8194 _find_layout_item_match(dir_cur, &ln, &it);
8195 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8196 (_ITEM_TEXT(it)->text_props.bidi.dir ==
8197 EVAS_BIDI_DIRECTION_RTL))
8199 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8200 (_ITEM_FORMAT(it)->bidi_dir ==
8201 EVAS_BIDI_DIRECTION_RTL))
8205 /* Adjust if the char is an rtl char */
8206 if ((ret >= 0) && (!is_rtl))
8208 /* Just don't advance the width */
8215 ret = evas_textblock_cursor_pen_geometry_get(
8216 dir_cur, &x, &y, &w, &h);
8220 if (cx) *cx = x + w;
8227 if (dir && dir_cur && dir_cur->node)
8230 Eina_Bool is_rtl = EINA_FALSE;
8231 if (dir_cur->node->par->is_bidi)
8233 Evas_Object_Textblock_Line *ln;
8234 Evas_Object_Textblock_Item *it;
8235 _find_layout_item_match(dir_cur, &ln, &it);
8236 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8237 (_ITEM_TEXT(it)->text_props.bidi.dir ==
8238 EVAS_BIDI_DIRECTION_RTL))
8240 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8241 (_ITEM_FORMAT(it)->bidi_dir ==
8242 EVAS_BIDI_DIRECTION_RTL))
8246 if (_evas_textblock_cursor_is_at_the_end(dir_cur) && (dir_cur->pos > 0))
8249 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
8251 else if (dir_cur->pos > 0)
8254 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
8259 *dir = EVAS_BIDI_DIRECTION_LTR;
8267 * Returns the geometry/pen position (depending on query_func) of the char
8270 * @param cur the position of the char.
8271 * @param query_func the query function to use.
8272 * @param cx the x of the char (or pen_x in the case of pen position).
8273 * @param cy the y of the char.
8274 * @param cw the w of the char (or advance in the case pen position).
8275 * @param ch the h of the char.
8276 * @return line number of the char on success, -1 on error.
8279 _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)
8281 Evas_Object_Textblock *o;
8282 Evas_Object_Textblock_Line *ln = NULL;
8283 Evas_Object_Textblock_Item *it = NULL;
8284 Evas_Object_Textblock_Text_Item *ti = NULL;
8285 Evas_Object_Textblock_Format_Item *fi = NULL;
8286 int x = 0, y = 0, w = 0, h = 0;
8288 Eina_Bool previous_format;
8290 if (!cur) return -1;
8291 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8292 if (!o->formatted.valid) _relayout(cur->obj);
8298 if (!o->paragraphs) return -1;
8299 ln = o->paragraphs->lines;
8301 if (cx) *cx = ln->x;
8302 if (cy) *cy = ln->par->y + ln->y;
8303 if (cw) *cw = ln->w;
8304 if (ch) *ch = ln->h;
8305 return ln->par->line_no + ln->line_no;
8311 previous_format = _find_layout_item_match(cur, &ln, &it);
8316 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8318 ti = _ITEM_TEXT(it);
8322 fi = _ITEM_FORMAT(it);
8327 pos = cur->pos - ti->parent.text_pos;
8329 if (pos < 0) pos = 0;
8330 if (ti->parent.format->font.font)
8332 query_func(cur->ENDT,
8333 ti->parent.format->font.font,
8339 x += ln->x + _ITEM(ti)->x;
8345 y = ln->par->y + ln->y;
8350 if (previous_format)
8352 if (_IS_LINE_SEPARATOR(fi->item))
8355 y = ln->par->y + ln->y + ln->h;
8360 if (ln->par->direction == EVAS_BIDI_DIRECTION_RTL)
8369 y = ln->par->y + ln->y;
8376 x = ln->x + _ITEM(fi)->x;
8377 y = ln->par->y + ln->y;
8390 return ln->par->line_no + ln->line_no;
8394 evas_textblock_cursor_char_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8396 return _evas_textblock_cursor_char_pen_geometry_common_get(
8397 cur->ENFN->font_char_coords_get, cur, cx, cy, cw, ch);
8401 evas_textblock_cursor_pen_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8403 return _evas_textblock_cursor_char_pen_geometry_common_get(
8404 cur->ENFN->font_pen_coords_get, cur, cx, cy, cw, ch);
8408 evas_textblock_cursor_line_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8410 Evas_Object_Textblock *o;
8411 Evas_Object_Textblock_Line *ln = NULL;
8412 Evas_Object_Textblock_Item *it = NULL;
8415 if (!cur) return -1;
8416 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8417 if (!o->formatted.valid) _relayout(cur->obj);
8420 ln = o->paragraphs->lines;
8424 _find_layout_item_match(cur, &ln, &it);
8428 y = ln->par->y + ln->y;
8435 return ln->par->line_no + ln->line_no;
8439 evas_textblock_cursor_visible_range_get(Evas_Textblock_Cursor *start, Evas_Textblock_Cursor *end)
8443 Evas_Object *obj = start->obj;
8444 TB_HEAD_RETURN(EINA_FALSE);
8445 e = evas_object_evas_get(obj);
8446 cy = 0 - obj->cur.geometry.y;
8448 evas_textblock_cursor_line_coord_set(start, cy);
8449 evas_textblock_cursor_line_coord_set(end, cy + ch);
8450 evas_textblock_cursor_line_char_last(end);
8456 evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
8458 Evas_Object_Textblock *o;
8459 Evas_Object_Textblock_Paragraph *found_par;
8460 Evas_Object_Textblock_Line *ln;
8461 Evas_Object_Textblock_Item *it = NULL;
8463 if (!cur) return EINA_FALSE;
8464 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8465 if (!o->formatted.valid) _relayout(cur->obj);
8466 x += o->style_pad.l;
8467 y += o->style_pad.t;
8469 found_par = _layout_find_paragraph_by_y(o, y);
8472 _layout_paragraph_render(o, found_par);
8473 EINA_INLIST_FOREACH(found_par->lines, ln)
8475 if (ln->par->y + ln->y > y) break;
8476 if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8478 /* If before or after the line, go to start/end according
8479 * to paragraph direction. */
8482 cur->pos = ln->items->text_pos;
8483 cur->node = found_par->text_node;
8484 if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8486 evas_textblock_cursor_line_char_last(cur);
8490 evas_textblock_cursor_line_char_first(cur);
8494 else if (x >= ln->x + ln->w)
8496 cur->pos = ln->items->text_pos;
8497 cur->node = found_par->text_node;
8498 if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8500 evas_textblock_cursor_line_char_first(cur);
8504 evas_textblock_cursor_line_char_last(cur);
8509 EINA_INLIST_FOREACH(ln->items, it)
8511 if (((it->x + ln->x) <= x) && (((it->x + ln->x) + it->adv) > x))
8513 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8517 Evas_Object_Textblock_Text_Item *ti;
8518 ti = _ITEM_TEXT(it);
8521 if (ti->parent.format->font.font)
8522 pos = cur->ENFN->font_char_at_coords_get(
8524 ti->parent.format->font.font,
8526 x - it->x - ln->x, 0,
8527 &cx, &cy, &cw, &ch);
8530 cur->pos = pos + it->text_pos;
8531 cur->node = it->text_node;
8536 Evas_Object_Textblock_Format_Item *fi;
8537 fi = _ITEM_FORMAT(it);
8538 cur->pos = fi->parent.text_pos;
8539 cur->node = found_par->text_node;
8547 else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8549 /* If we are after the last paragraph, use the last position in the
8551 evas_textblock_cursor_paragraph_last(cur);
8554 else if (o->paragraphs && (y < o->paragraphs->y))
8556 evas_textblock_cursor_paragraph_first(cur);
8564 evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
8566 Evas_Object_Textblock *o;
8567 Evas_Object_Textblock_Paragraph *found_par;
8568 Evas_Object_Textblock_Line *ln;
8570 if (!cur) return -1;
8571 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8572 if (!o->formatted.valid) _relayout(cur->obj);
8573 y += o->style_pad.t;
8575 found_par = _layout_find_paragraph_by_y(o, y);
8579 _layout_paragraph_render(o, found_par);
8580 EINA_INLIST_FOREACH(found_par->lines, ln)
8582 if (ln->par->y + ln->y > y) break;
8583 if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8585 evas_textblock_cursor_line_set(cur, ln->par->line_no +
8587 return ln->par->line_no + ln->line_no;
8591 else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8594 /* If we are after the last paragraph, use the last position in the
8596 evas_textblock_cursor_paragraph_last(cur);
8597 if (cur->node && cur->node->par)
8599 line_no = cur->node->par->line_no;
8600 if (cur->node->par->lines)
8602 line_no += ((Evas_Object_Textblock_Line *)
8603 EINA_INLIST_GET(cur->node->par->lines)->last)->line_no;
8608 else if (o->paragraphs && (y < o->paragraphs->y))
8611 evas_textblock_cursor_paragraph_first(cur);
8612 if (cur->node && cur->node->par)
8614 line_no = cur->node->par->line_no;
8623 * Updates x and w according to the text direction, position in text and
8624 * if it's a special case switch
8626 * @param ti the text item we are working on
8627 * @param x the current x (we get) and the x we return
8628 * @param w the current w (we get) and the w we return
8629 * @param start if this is the first item or not
8630 * @param switch_items toogles item switching (rtl cases)
8633 _evas_textblock_range_calc_x_w(const Evas_Object_Textblock_Item *it,
8634 Evas_Coord *x, Evas_Coord *w, Eina_Bool start, Eina_Bool switch_items)
8636 if ((start && !switch_items) || (!start && switch_items))
8639 if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8640 _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8642 ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8643 _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8657 if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8658 _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8660 ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8661 _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8678 * Returns the geometry of the range in line ln. Cur1 is the start cursor,
8679 * cur2 is the end cursor, NULL means from the start or to the end accordingly.
8680 * Assumes that ln is valid, and that at least one of cur1 and cur2 is not NULL.
8682 * @param ln the line to work on.
8683 * @param cur1 the start cursor
8684 * @param cur2 the end cursor
8685 * @return Returns the geometry of the range
8688 _evas_textblock_cursor_range_in_line_geometry_get(
8689 const Evas_Object_Textblock_Line *ln, const Evas_Textblock_Cursor *cur1,
8690 const Evas_Textblock_Cursor *cur2)
8692 Evas_Object_Textblock_Item *it;
8693 Evas_Object_Textblock_Item *it1, *it2;
8694 Eina_List *rects = NULL;
8695 Evas_Textblock_Rectangle *tr;
8697 Eina_Bool switch_items;
8698 const Evas_Textblock_Cursor *cur;
8700 cur = (cur1) ? cur1 : cur2;
8702 if (!cur) return NULL;
8704 /* Find the first and last items */
8707 EINA_INLIST_FOREACH(ln->items, it)
8710 item_len = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
8711 _ITEM_TEXT(it)->text_props.text_len
8713 if ((!cur1 || (cur1->pos < it->text_pos + item_len)) &&
8714 (!cur2 || (cur2->pos >= it->text_pos)))
8719 start = item_len; /* start stores the first item_len */
8722 end = item_len; /* end stores the last item_len */
8726 /* If we couldn't find even one item, return */
8727 if (!it1) return NULL;
8729 /* If the first item is logically before or equal the second item
8730 * we have to set start and end differently than in the other case */
8731 if (it1->text_pos <= it2->text_pos)
8733 start = (cur1) ? (cur1->pos - it1->text_pos) : 0;
8734 end = (cur2) ? (cur2->pos - it2->text_pos) : end;
8735 switch_items = EINA_FALSE;
8739 start = (cur2) ? (cur2->pos - it1->text_pos) : start;
8740 end = (cur1) ? (cur1->pos - it2->text_pos) : 0;
8741 switch_items = EINA_TRUE;
8744 /* IMPORTANT: Don't use cur1/cur2 past this point (because they probably
8745 * don't make sense anymore. That's why there are start and end),
8746 * unless you know what you are doing */
8748 /* Special case when they share the same item and it's a text item */
8749 if ((it1 == it2) && (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT))
8751 Evas_Coord x1, w1, x2, w2;
8752 Evas_Coord x, w, y, h;
8753 Evas_Object_Textblock_Text_Item *ti;
8756 ti = _ITEM_TEXT(it1);
8757 if (ti->parent.format->font.font)
8759 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8760 ti->parent.format->font.font,
8769 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8770 ti->parent.format->font.font,
8779 /* Make x2 the one on the right */
8793 if (ti->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8806 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8807 rects = eina_list_append(rects, tr);
8808 tr->x = ln->x + it1->x + x;
8809 tr->y = ln->par->y + ln->y;
8814 else if (it1 != it2)
8816 /* Get the middle items */
8817 Evas_Coord min_x, max_x;
8819 it = _ITEM(EINA_INLIST_GET(it1)->next);
8820 min_x = max_x = it->x;
8822 if (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8825 Evas_Object_Textblock_Text_Item *ti;
8827 ti = _ITEM_TEXT(it1);
8829 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8830 ti->parent.format->font.font,
8836 /* BUG! Skip the first item */
8841 _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8849 _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8854 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8855 rects = eina_list_append(rects, tr);
8856 tr->x = ln->x + it1->x + x;
8857 tr->y = ln->par->y + ln->y;
8862 while (it && (it != it2))
8864 max_x = it->x + it->adv;
8865 it = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(it)->next;
8869 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8870 rects = eina_list_append(rects, tr);
8871 tr->x = ln->x + min_x;
8872 tr->y = ln->par->y + ln->y;
8874 tr->w = max_x - min_x;
8876 if (it2->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8879 Evas_Object_Textblock_Text_Item *ti;
8881 ti = _ITEM_TEXT(it2);
8883 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8884 ti->parent.format->font.font,
8890 /* BUG! skip the last item */
8895 _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8903 _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8908 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8909 rects = eina_list_append(rects, tr);
8910 tr->x = ln->x + it2->x + x;
8911 tr->y = ln->par->y + ln->y;
8920 evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
8922 Evas_Object_Textblock *o;
8923 Evas_Object_Textblock_Line *ln1, *ln2;
8924 Evas_Object_Textblock_Item *it1, *it2;
8925 Eina_List *rects = NULL;
8926 Evas_Textblock_Rectangle *tr;
8928 if (!cur1 || !cur1->node) return NULL;
8929 if (!cur2 || !cur2->node) return NULL;
8930 if (cur1->obj != cur2->obj) return NULL;
8931 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
8932 if (!o->formatted.valid) _relayout(cur1->obj);
8933 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
8935 const Evas_Textblock_Cursor *tc;
8944 _find_layout_item_match(cur1, &ln1, &it1);
8945 if (!ln1 || !it1) return NULL;
8946 _find_layout_item_match(cur2, &ln2, &it2);
8947 if (!ln2 || !it2) return NULL;
8951 rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8956 Evas_Object_Textblock_Line *plni, *lni;
8957 Eina_List *rects2 = NULL;
8958 /* Handle the first line */
8959 rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8962 /* Handle the lines between the first and the last line */
8963 lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(ln1)->next;
8964 if (!lni && (ln1->par != ln2->par))
8966 lni = ((Evas_Object_Textblock_Paragraph *)
8967 EINA_INLIST_GET(ln1->par)->next)->lines;
8969 while (lni && (lni != ln2))
8971 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8972 rects = eina_list_append(rects, tr);
8974 tr->y = lni->par->y + lni->y;
8978 lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(lni)->next;
8979 if (!lni && (plni->par != ln2->par))
8981 lni = ((Evas_Object_Textblock_Paragraph *)
8982 EINA_INLIST_GET(plni->par)->next)->lines;
8985 rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2,
8987 rects = eina_list_merge(rects, rects2);
8993 evas_textblock_cursor_format_item_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8995 Evas_Object_Textblock *o;
8996 Evas_Object_Textblock_Line *ln = NULL;
8997 Evas_Object_Textblock_Format_Item *fi;
8998 Evas_Object_Textblock_Item *it = NULL;
8999 Evas_Coord x, y, w, h;
9001 if (!cur || !evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
9002 o = (Evas_Object_Textblock *)(cur->obj->object_data);
9003 if (!o->formatted.valid) _relayout(cur->obj);
9004 if (!evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
9005 _find_layout_item_line_match(cur->obj, cur->node, cur->pos, &ln, &it);
9006 fi = _ITEM_FORMAT(it);
9007 if ((!ln) || (!fi)) return EINA_FALSE;
9008 x = ln->x + fi->parent.x;
9009 y = ln->par->y + ln->y + ln->baseline + fi->y;
9020 evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur)
9022 Eina_Bool ret = EINA_FALSE;
9023 Evas_Textblock_Cursor cur2;
9024 if (!cur) return EINA_FALSE;
9026 cur2.obj = cur->obj;
9027 evas_textblock_cursor_copy(cur, &cur2);
9028 evas_textblock_cursor_line_char_last(&cur2);
9029 if (cur2.pos == cur->pos)
9036 /* general controls */
9038 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)
9040 Evas_Object_Textblock_Line *ln;
9043 ln = _find_layout_line_num(obj, line);
9044 if (!ln) return EINA_FALSE;
9045 if (cx) *cx = ln->x;
9046 if (cy) *cy = ln->par->y + ln->y;
9047 if (cw) *cw = ln->w;
9048 if (ch) *ch = ln->h;
9053 evas_object_textblock_clear(Evas_Object *obj)
9056 Evas_Textblock_Cursor *cur;
9061 _paragraphs_free(obj, o->paragraphs);
9062 o->paragraphs = NULL;
9066 o->cursor->node = NULL;
9068 EINA_LIST_FOREACH(o->cursors, l, cur)
9074 _evas_textblock_changed(o, obj);
9078 evas_object_textblock_size_formatted_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
9081 if (!o->formatted.valid) _relayout(obj);
9082 if (w) *w = o->formatted.w;
9083 if (h) *h = o->formatted.h;
9087 _size_native_calc_line_finalize(const Evas_Object *obj, Eina_List *items,
9088 Evas_Coord *ascent, Evas_Coord *descent, Evas_Coord *w)
9090 Evas_Object_Textblock_Item *it;
9093 it = eina_list_data_get(items);
9098 /* If there are no text items yet, calc ascent/descent
9099 * according to the current format. */
9100 if (*ascent + *descent == 0)
9101 _layout_format_ascent_descent_adjust(obj, ascent, descent,
9106 *w = it->format->margin.l + it->format->margin.r;
9110 /* Adjust all the item sizes according to the final line size,
9111 * and update the x positions of all the items of the line. */
9112 EINA_LIST_FOREACH(items, i, it)
9114 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
9116 Evas_Coord fw, fh, fy;
9118 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
9119 if (!fi->formatme) goto loop_advance;
9120 _layout_calculate_format_item_size(obj, fi, ascent,
9121 descent, &fy, &fw, &fh);
9131 _size_native_calc_paragraph_size(const Evas_Object *obj,
9132 const Evas_Object_Textblock *o,
9133 const Evas_Object_Textblock_Paragraph *par,
9134 Evas_Coord *_w, Evas_Coord *_h)
9137 Evas_Object_Textblock_Item *it;
9138 Eina_List *line_items = NULL;
9139 Evas_Coord w = 0, y = 0, wmax = 0, h = 0, ascent = 0, descent = 0;
9141 EINA_LIST_FOREACH(par->logical_items, i, it)
9143 line_items = eina_list_append(line_items, it);
9144 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
9146 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
9147 if (fi->item && (_IS_LINE_SEPARATOR(fi->item) ||
9148 _IS_PARAGRAPH_SEPARATOR(o, fi->item)))
9150 _size_native_calc_line_finalize(obj, line_items, &ascent,
9153 if (ascent + descent > h)
9154 h = ascent + descent;
9160 ascent = descent = 0;
9161 line_items = eina_list_free(line_items);
9165 Evas_Coord fw, fh, fy;
9166 /* If there are no text items yet, calc ascent/descent
9167 * according to the current format. */
9168 if (it && (ascent + descent == 0))
9169 _layout_format_ascent_descent_adjust(obj, &ascent,
9170 &descent, it->format);
9172 _layout_calculate_format_item_size(obj, fi, &ascent,
9173 &descent, &fy, &fw, &fh);
9178 _layout_format_ascent_descent_adjust(obj, &ascent,
9179 &descent, it->format);
9183 _size_native_calc_line_finalize(obj, line_items, &ascent, &descent, &w);
9185 line_items = eina_list_free(line_items);
9187 /* Do the last addition */
9188 if (ascent + descent > h)
9189 h = ascent + descent;
9199 evas_object_textblock_size_native_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
9202 if (!o->native.valid)
9204 Evas_Coord wmax = 0, hmax = 0;
9205 Evas_Object_Textblock_Paragraph *par;
9206 /* We just want the layout objects to update, should probably
9208 if (!o->formatted.valid) _relayout(obj);
9209 EINA_INLIST_FOREACH(o->paragraphs, par)
9212 _size_native_calc_paragraph_size(obj, o, par, &tw, &th);
9221 o->native.valid = 1;
9222 o->content_changed = 0;
9223 o->format_changed = EINA_FALSE;
9225 if (w) *w = o->native.w;
9226 if (h) *h = o->native.h;
9230 evas_object_textblock_style_insets_get(const Evas_Object *obj, Evas_Coord *l, Evas_Coord *r, Evas_Coord *t, Evas_Coord *b)
9233 if (!o->formatted.valid) _relayout(obj);
9234 if (l) *l = o->style_pad.l;
9235 if (r) *r = o->style_pad.r;
9236 if (t) *t = o->style_pad.t;
9237 if (b) *b = o->style_pad.b;
9241 * FIXME: DELETE ME! DELETE ME!
9242 * This is an ugly workaround to get around the fact that
9243 * evas_object_textblock_coords_recalc isn't really called when it's supposed
9244 * to. When that bug is fixed please remove this. */
9246 _workaround_object_coords_recalc(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
9248 evas_object_textblock_coords_recalc(obj);
9251 /* all nice and private */
9253 evas_object_textblock_init(Evas_Object *obj)
9255 Evas_Object_Textblock *o;
9256 #ifdef HAVE_LINEBREAK
9257 static Eina_Bool linebreak_init = EINA_FALSE;
9258 if (!linebreak_init)
9260 linebreak_init = EINA_TRUE;
9266 /* alloc image ob, setup methods and default values */
9267 obj->object_data = evas_object_textblock_new();
9268 /* set up default settings for this kind of object */
9269 obj->cur.color.r = 255;
9270 obj->cur.color.g = 255;
9271 obj->cur.color.b = 255;
9272 obj->cur.color.a = 255;
9273 obj->cur.geometry.x = 0.0;
9274 obj->cur.geometry.y = 0.0;
9275 obj->cur.geometry.w = 0.0;
9276 obj->cur.geometry.h = 0.0;
9278 /* set up object-specific settings */
9279 obj->prev = obj->cur;
9280 /* set up methods (compulsory) */
9281 obj->func = &object_func;
9284 o = (Evas_Object_Textblock *)(obj->object_data);
9285 o->cursor->obj = obj;
9286 o->legacy_newline = EINA_TRUE;
9287 evas_object_event_callback_priority_add(obj, EVAS_CALLBACK_RESIZE, -1000,
9288 _workaround_object_coords_recalc, NULL);
9292 evas_object_textblock_new(void)
9294 Evas_Object_Textblock *o;
9296 /* alloc obj private data */
9297 EVAS_MEMPOOL_INIT(_mp_obj, "evas_object_textblock", Evas_Object_Textblock, 64, NULL);
9298 o = EVAS_MEMPOOL_ALLOC(_mp_obj, Evas_Object_Textblock);
9299 if (!o) return NULL;
9300 EVAS_MEMPOOL_PREP(_mp_obj, o, Evas_Object_Textblock);
9301 o->magic = MAGIC_OBJ_TEXTBLOCK;
9302 o->cursor = calloc(1, sizeof(Evas_Textblock_Cursor));
9303 _format_command_init();
9308 evas_object_textblock_free(Evas_Object *obj)
9310 Evas_Object_Textblock *o;
9312 evas_object_textblock_clear(obj);
9313 evas_object_textblock_style_set(obj, NULL);
9314 evas_object_textblock_style_user_set(obj, NULL);
9315 o = (Evas_Object_Textblock *)(obj->object_data);
9319 Evas_Textblock_Cursor *cur;
9321 cur = (Evas_Textblock_Cursor *)o->cursors->data;
9322 o->cursors = eina_list_remove_list(o->cursors, o->cursors);
9325 if (o->repch) eina_stringshare_del(o->repch);
9326 if (o->ellip_ti) _item_free(obj, NULL, _ITEM(o->ellip_ti));
9328 EVAS_MEMPOOL_FREE(_mp_obj, o);
9329 _format_command_shutdown();
9334 evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y)
9336 Evas_Object_Textblock_Paragraph *par, *start = NULL;
9337 Evas_Object_Textblock_Line *ln;
9338 Evas_Object_Textblock *o;
9340 int cx, cy, cw, ch, clip;
9341 const char vals[5][5] =
9350 /* render object to surface with context, and offxet by x,y */
9351 o = (Evas_Object_Textblock *)(obj->object_data);
9352 obj->layer->evas->engine.func->context_multiplier_unset(output,
9354 /* FIXME: This clipping is just until we fix inset handling correctly. */
9355 ENFN->context_clip_clip(output, context,
9356 obj->cur.geometry.x + x,
9357 obj->cur.geometry.y + y,
9358 obj->cur.geometry.w,
9359 obj->cur.geometry.h);
9360 clip = ENFN->context_clip_get(output, context, &cx, &cy, &cw, &ch);
9361 /* If there are no paragraphs and thus there are no lines,
9362 * there's nothing left to do. */
9363 if (!o->paragraphs) return;
9365 #define ITEM_WALK() \
9366 EINA_INLIST_FOREACH(start, par) \
9368 if (!par->visible) continue; \
9371 if ((obj->cur.geometry.y + y + par->y + par->h) < (cy - 20)) \
9373 if ((obj->cur.geometry.y + y + par->y) > (cy + ch + 20)) \
9376 _layout_paragraph_render(o, par); \
9377 EINA_INLIST_FOREACH(par->lines, ln) \
9379 Evas_Object_Textblock_Item *itr; \
9383 if ((obj->cur.geometry.y + y + par->y + ln->y + ln->h) < (cy - 20)) \
9385 if ((obj->cur.geometry.y + y + par->y + ln->y) > (cy + ch + 20)) \
9388 EINA_INLIST_FOREACH(ln->items, itr) \
9391 yoff = ln->baseline; \
9392 if (itr->format->valign != -1.0) \
9394 yoff += itr->format->valign * (ln->h - itr->h); \
9398 if ((obj->cur.geometry.x + x + ln->x + itr->x + itr->w) < (cx - 20)) \
9400 if ((obj->cur.geometry.x + x + ln->x + itr->x) > (cx + cw + 20)) \
9403 if ((ln->x + itr->x + itr->w) <= 0) continue; \
9404 if (ln->x + itr->x > obj->cur.geometry.w) break; \
9407 #define ITEM_WALK_END() \
9413 #define COLOR_SET(col) \
9414 ENFN->context_color_set(output, context, \
9415 (obj->cur.cache.clip.r * ti->parent.format->color.col.r) / 255, \
9416 (obj->cur.cache.clip.g * ti->parent.format->color.col.g) / 255, \
9417 (obj->cur.cache.clip.b * ti->parent.format->color.col.b) / 255, \
9418 (obj->cur.cache.clip.a * ti->parent.format->color.col.a) / 255);
9419 #define COLOR_SET_AMUL(col, amul) \
9420 ENFN->context_color_set(output, context, \
9421 (obj->cur.cache.clip.r * ti->parent.format->color.col.r * (amul)) / 65025, \
9422 (obj->cur.cache.clip.g * ti->parent.format->color.col.g * (amul)) / 65025, \
9423 (obj->cur.cache.clip.b * ti->parent.format->color.col.b * (amul)) / 65025, \
9424 (obj->cur.cache.clip.a * ti->parent.format->color.col.a * (amul)) / 65025);
9425 #define DRAW_TEXT(ox, oy) \
9426 if (ti->parent.format->font.font) ENFN->font_draw(output, context, surface, ti->parent.format->font.font, \
9427 obj->cur.geometry.x + ln->x + ti->parent.x + x + (ox), \
9428 obj->cur.geometry.y + ln->par->y + ln->y + yoff + y + (oy), \
9429 ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h, \
9433 #define DRAW_RECT(ox, oy, ow, oh, or, og, ob, oa) \
9436 ENFN->context_color_set(output, \
9438 (obj->cur.cache.clip.r * or) / 255, \
9439 (obj->cur.cache.clip.g * og) / 255, \
9440 (obj->cur.cache.clip.b * ob) / 255, \
9441 (obj->cur.cache.clip.a * oa) / 255); \
9442 ENFN->rectangle_draw(output, \
9445 obj->cur.geometry.x + ln->x + x + (ox), \
9446 obj->cur.geometry.y + ln->par->y + ln->y + y + (oy), \
9452 #define DRAW_FORMAT_DASHED(oname, oy, oh, dw, dp) \
9455 if (itr->format->oname) \
9457 unsigned char _or, _og, _ob, _oa; \
9458 int _ind, _dx = 0, _dn, _dr; \
9459 _or = itr->format->color.oname.r; \
9460 _og = itr->format->color.oname.g; \
9461 _ob = itr->format->color.oname.b; \
9462 _oa = itr->format->color.oname.a; \
9463 if (!EINA_INLIST_GET(itr)->next) \
9465 _dn = itr->w / (dw + dp); \
9466 _dr = itr->w % (dw + dp); \
9470 _dn = itr->adv / (dw + dp); \
9471 _dr = itr->adv % (dw + dp); \
9473 if (_dr > dw) _dr = dw; \
9474 for (_ind = 0 ; _ind < _dn ; _ind++) \
9476 DRAW_RECT(itr->x + _dx, oy, dw, oh, _or, _og, _ob, _oa); \
9479 DRAW_RECT(itr->x + _dx, oy, _dr, oh, _or, _og, _ob, _oa); \
9484 #define DRAW_FORMAT(oname, oy, oh) \
9487 if (itr->format->oname) \
9489 unsigned char _or, _og, _ob, _oa; \
9490 _or = itr->format->color.oname.r; \
9491 _og = itr->format->color.oname.g; \
9492 _ob = itr->format->color.oname.b; \
9493 _oa = itr->format->color.oname.a; \
9494 if (!EINA_INLIST_GET(itr)->next) \
9496 DRAW_RECT(itr->x, oy, itr->w, oh, _or, _og, _ob, _oa); \
9500 DRAW_RECT(itr->x, oy, itr->adv, oh, _or, _og, _ob, _oa); \
9507 Evas_Coord look_for_y = 0 - (obj->cur.geometry.y + y);
9510 Evas_Coord tmp_lfy = cy - (obj->cur.geometry.y + y);
9511 if (tmp_lfy > look_for_y)
9512 look_for_y = tmp_lfy;
9515 if (look_for_y >= 0)
9516 start = _layout_find_paragraph_by_y(o, look_for_y);
9519 start = o->paragraphs;
9524 DRAW_FORMAT(backing, 0, ln->h);
9528 /* There are size adjustments that depend on the styles drawn here back
9529 * in "_text_item_update_sizes" should not modify one without the other. */
9531 /* prepare everything for text draw */
9536 int shad_dst, shad_sz, dx, dy, haveshad;
9537 Evas_Object_Textblock_Text_Item *ti;
9538 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9541 shad_dst = shad_sz = dx = dy = haveshad = 0;
9542 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
9544 case EVAS_TEXT_STYLE_SHADOW:
9545 case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
9549 case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
9550 case EVAS_TEXT_STYLE_FAR_SHADOW:
9554 case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
9559 case EVAS_TEXT_STYLE_SOFT_SHADOW:
9571 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
9573 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
9577 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
9581 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
9585 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
9589 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
9593 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
9597 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
9601 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
9617 for (j = 0; j < 5; j++)
9619 for (i = 0; i < 5; i++)
9621 if (vals[i][j] != 0)
9623 COLOR_SET_AMUL(shadow, vals[i][j] * 50);
9624 DRAW_TEXT(i - 2 + dx, j - 2 + dy);
9639 Evas_Object_Textblock_Text_Item *ti;
9640 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9643 if (ti->parent.format->style == EVAS_TEXT_STYLE_GLOW)
9645 for (j = 0; j < 5; j++)
9647 for (i = 0; i < 5; i++)
9649 if (vals[i][j] != 0)
9651 COLOR_SET_AMUL(glow, vals[i][j] * 50);
9652 DRAW_TEXT(i - 2, j - 2);
9668 Evas_Object_Textblock_Text_Item *ti;
9669 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9672 if ((ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE) ||
9673 (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
9674 (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW))
9682 else if (ti->parent.format->style == EVAS_TEXT_STYLE_SOFT_OUTLINE)
9684 for (j = 0; j < 5; j++)
9686 for (i = 0; i < 5; i++)
9688 if (((i != 2) || (j != 2)) && (vals[i][j] != 0))
9690 COLOR_SET_AMUL(outline, vals[i][j] * 50);
9691 DRAW_TEXT(i - 2, j - 2);
9699 /* normal text and lines */
9702 Evas_Object_Textblock_Text_Item *ti;
9703 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9712 DRAW_FORMAT(strikethrough, (ln->h / 2), 1);
9715 DRAW_FORMAT(underline, ln->baseline + 1, 1);
9717 /* UNDERLINE DASHED */
9718 DRAW_FORMAT_DASHED(underline_dash, ln->baseline + 1, 1,
9719 ti->parent.format->underline_dash_width,
9720 ti->parent.format->underline_dash_gap);
9723 DRAW_FORMAT(underline2, ln->baseline + 3, 1);
9729 evas_object_textblock_render_pre(Evas_Object *obj)
9731 Evas_Object_Textblock *o;
9734 /* dont pre-render the obj twice! */
9735 if (obj->pre_render_done) return;
9736 obj->pre_render_done = 1;
9737 /* pre-render phase. this does anything an object needs to do just before */
9738 /* rendering. this could mean loading the image data, retrieving it from */
9739 /* elsewhere, decoding video etc. */
9740 /* then when this is done the object needs to figure if it changed and */
9741 /* if so what and where and add the appropriate redraw textblocks */
9742 o = (Evas_Object_Textblock *)(obj->object_data);
9743 if ((o->changed) || (o->content_changed) || (o->format_changed) ||
9744 ((obj->cur.geometry.w != o->last_w) ||
9745 (((o->valign != 0.0) || (o->have_ellipsis)) &&
9746 (obj->cur.geometry.h != o->last_h))))
9750 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9751 is_v = evas_object_is_visible(obj);
9752 was_v = evas_object_was_visible(obj);
9758 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9759 is_v = evas_object_is_visible(obj);
9760 was_v = evas_object_was_visible(obj);
9763 /* if someone is clipping this obj - go calculate the clipper */
9764 if (obj->cur.clipper)
9766 if (obj->cur.cache.clip.dirty)
9767 evas_object_clip_recalc(obj->cur.clipper);
9768 obj->cur.clipper->func->render_pre(obj->cur.clipper);
9770 /* now figure what changed and add draw rects */
9771 /* if it just became visible or invisible */
9772 is_v = evas_object_is_visible(obj);
9773 was_v = evas_object_was_visible(obj);
9776 evas_object_render_pre_visible_change(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9779 if ((obj->cur.map != obj->prev.map) ||
9780 (obj->cur.usemap != obj->prev.usemap))
9782 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9785 /* it's not visible - we accounted for it appearing or not so just abort */
9786 if (!is_v) goto done;
9787 /* clipper changed this is in addition to anything else for obj */
9788 evas_object_render_pre_clipper_change(&obj->layer->evas->clip_changes, obj);
9789 /* if we restacked (layer or just within a layer) and don't clip anyone */
9792 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9795 /* if it changed color */
9796 if ((obj->cur.color.r != obj->prev.color.r) ||
9797 (obj->cur.color.g != obj->prev.color.g) ||
9798 (obj->cur.color.b != obj->prev.color.b) ||
9799 (obj->cur.color.a != obj->prev.color.a))
9801 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9804 /* if it changed geometry - and obviously not visibility or color */
9805 /* calculate differences since we have a constant color fill */
9806 /* we really only need to update the differences */
9807 if ((obj->cur.geometry.x != obj->prev.geometry.x) ||
9808 (obj->cur.geometry.y != obj->prev.geometry.y) ||
9809 (obj->cur.geometry.w != obj->prev.geometry.w) ||
9810 (obj->cur.geometry.h != obj->prev.geometry.h))
9812 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9816 evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9820 evas_object_textblock_render_post(Evas_Object *obj)
9822 /* Evas_Object_Textblock *o; */
9824 /* this moves the current data to the previous state parts of the object */
9825 /* in whatever way is safest for the object. also if we don't need object */
9826 /* data anymore we can free it if the object deems this is a good idea */
9827 /* o = (Evas_Object_Textblock *)(obj->object_data); */
9828 /* remove those pesky changes */
9829 evas_object_clip_changes_clean(obj);
9830 /* move cur to prev safely for object data */
9831 obj->prev = obj->cur;
9832 /* o->prev = o->cur; */
9833 /* o->changed = 0; */
9836 static unsigned int evas_object_textblock_id_get(Evas_Object *obj)
9838 Evas_Object_Textblock *o;
9840 o = (Evas_Object_Textblock *)(obj->object_data);
9842 return MAGIC_OBJ_TEXTBLOCK;
9845 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj)
9847 Evas_Object_Textblock *o;
9849 o = (Evas_Object_Textblock *)(obj->object_data);
9851 return MAGIC_OBJ_CUSTOM;
9854 static void *evas_object_textblock_engine_data_get(Evas_Object *obj)
9856 Evas_Object_Textblock *o;
9858 o = (Evas_Object_Textblock *)(obj->object_data);
9859 if (!o) return NULL;
9860 return o->engine_data;
9864 evas_object_textblock_is_opaque(Evas_Object *obj __UNUSED__)
9866 /* this returns 1 if the internal object data implies that the object is */
9867 /* currently fulyl opque over the entire gradient it occupies */
9872 evas_object_textblock_was_opaque(Evas_Object *obj __UNUSED__)
9874 /* this returns 1 if the internal object data implies that the object was */
9875 /* currently fulyl opque over the entire gradient it occupies */
9880 evas_object_textblock_coords_recalc(Evas_Object *obj)
9882 Evas_Object_Textblock *o;
9884 o = (Evas_Object_Textblock *)(obj->object_data);
9885 if ((obj->cur.geometry.w != o->last_w) ||
9886 (((o->valign != 0.0) || (o->have_ellipsis)) &&
9887 (obj->cur.geometry.h != o->last_h)))
9889 o->formatted.valid = 0;
9895 evas_object_textblock_scale_update(Evas_Object *obj)
9897 Evas_Object_Textblock *o;
9899 o = (Evas_Object_Textblock *)(obj->object_data);
9900 _evas_textblock_invalidate_all(o);
9901 _evas_textblock_changed(o, obj);
9905 _evas_object_textblock_rehint(Evas_Object *obj)
9907 Evas_Object_Textblock *o;
9908 Evas_Object_Textblock_Paragraph *par;
9909 Evas_Object_Textblock_Line *ln;
9911 o = (Evas_Object_Textblock *)(obj->object_data);
9912 EINA_INLIST_FOREACH(o->paragraphs, par)
9914 EINA_INLIST_FOREACH(par->lines, ln)
9916 Evas_Object_Textblock_Item *it;
9918 EINA_INLIST_FOREACH(ln->items, it)
9920 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9922 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
9923 if (ti->parent.format->font.font)
9925 #ifdef EVAS_FRAME_QUEUING
9926 evas_common_pipe_op_text_flush((RGBA_Font *) ti->parent.format->font.font);
9928 evas_font_load_hinting_set(obj->layer->evas,
9929 ti->parent.format->font.font,
9930 obj->layer->evas->hinting);
9936 _evas_textblock_invalidate_all(o);
9937 _evas_textblock_changed(o, obj);
9945 /* return EINA_FALSE on error, used in unit_testing */
9947 _evas_textblock_check_item_node_link(Evas_Object *obj)
9949 Evas_Object_Textblock *o;
9950 Evas_Object_Textblock_Paragraph *par;
9951 Evas_Object_Textblock_Line *ln;
9952 Evas_Object_Textblock_Item *it;
9954 o = (Evas_Object_Textblock *)(obj->object_data);
9955 if (!o) return EINA_FALSE;
9957 if (!o->formatted.valid) _relayout(obj);
9959 EINA_INLIST_FOREACH(o->paragraphs, par)
9961 EINA_INLIST_FOREACH(par->lines, ln)
9963 EINA_INLIST_FOREACH(ln->items, it)
9965 if (it->text_node != par->text_node)
9974 _evas_textblock_format_offset_get(const Evas_Object_Textblock_Node_Format *n)
9981 /* Good for debugging */
9983 pfnode(Evas_Object_Textblock_Node_Format *n)
9985 printf("Format Node: %p\n", n);
9986 printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
9987 printf("text_node = %p, offset = %u, visible = %d\n", n->text_node, n->offset, n->visible);
9988 printf("'%s'\n", eina_strbuf_string_get(n->format));
9992 ptnode(Evas_Object_Textblock_Node_Text *n)
9994 printf("Text Node: %p\n", n);
9995 printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
9996 printf("format_node = %p\n", n->format_node);
9997 printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
10001 pitem(Evas_Object_Textblock_Item *it)
10003 Evas_Object_Textblock_Text_Item *ti;
10004 Evas_Object_Textblock_Format_Item *fi;
10005 printf("Item: %p\n", it);
10006 printf("Type: %s (%d)\n", (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
10007 "TEXT" : "FORMAT", it->type);
10008 printf("Text pos: %d Visual pos: %d\n", it->text_pos,
10009 #ifdef BIDI_SUPPORT
10015 printf("Coords: x = %d w = %d adv = %d\n", (int) it->x, (int) it->w,
10017 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
10019 ti = _ITEM_TEXT(it);
10020 printf("Text: '%*ls'\n", ti->text_props.text_len, GET_ITEM_TEXT(ti));
10024 fi = _ITEM_FORMAT(it);
10025 printf("Format: '%s'\n", fi->item);
10030 ppar(Evas_Object_Textblock_Paragraph *par)
10032 Evas_Object_Textblock_Item *it;
10034 EINA_LIST_FOREACH(par->logical_items, i, it)
10036 printf("***********************\n");